Wie kann git eine gelöschte und eine neue Datei als Datei verschieben markieren?

505

Ich habe eine Datei manuell verschoben und dann geändert. Laut Git handelt es sich um eine neue und eine entfernte Datei. Gibt es eine Möglichkeit, Git zu zwingen, es als Dateiverschiebung zu behandeln?

pupeno
quelle
3
Für eine gegebene Datei old_file.txt, dann git mv old_file.txt new_file.txtentspricht git rm --cached old_file.txt, mv old_file.txt new_file.txt, git add new_file.txt.
Jarl
3
Jarl: Nein, ist es nicht. Wenn es auch Änderungen in der Datei gibt, git mvwerden diese nicht zum Cache hinzugefügt, sondern git add. Ich ziehe es vor, die Datei zurück zu verschieben, damit ich sie verwenden kann git mv, und dann git add -pmeinen Änderungssatz zu überprüfen.
Dhardy
Bitte
überprüfen
Git hat sich in den letzten 8 Jahren verbessert. Wenn es sich nur um eine Datei handelt, hat die Top-Antwort stackoverflow.com/a/433142/459 nichts bewirkt. Sie können jedoch stackoverflow.com/a/1541072/459 folgen , um ein rm / add zu aktualisieren zu einem MV / Änderungsstatus.
Dlamblin

Antworten:

435

Git erkennt das Verschieben / Umbenennen automatisch, wenn Ihre Änderung nicht zu schwerwiegend ist. Nur git adddie neue Datei und git rmdie alte Datei. git statuszeigt dann an, ob die Umbenennung erkannt wurde.

Für das Verschieben in Verzeichnissen müssen Sie möglicherweise Folgendes tun:

  1. CD an den Anfang dieser Verzeichnisstruktur.
  2. Lauf git add -A .
  3. Führen Sie aus git status, um zu überprüfen, ob die "neue Datei" jetzt eine "umbenannte" Datei ist

Wenn der Git-Status weiterhin "Neue Datei" und nicht "Umbenannt" anzeigt, müssen Sie den Ratschlägen von Hank Gay folgen und den Umzug und die Änderung in zwei separaten Commits durchführen.

Bombe
quelle
75
Klarstellung: 'nicht zu streng' bedeutet, dass die neue Datei und die alte Datei zu> 50% 'ähnlich' sind, basierend auf einigen Ähnlichkeitsindizes, die git verwendet.
pjz
151
Etwas, das mich für ein paar Minuten aufgehängt hat: Wenn die umbenannten und gelöschten Dateien nicht zum Festschreiben bereitgestellt werden, werden sie als Lösch- und neue Datei angezeigt. Sobald Sie sie dem Staging-Index hinzufügen, wird er als Umbenennung erkannt.
Marczych
19
Es ist erwähnenswert, dass, wenn von " Git wird automatisch das Verschieben / Umbenennen erkennen ". Dies geschieht zu dem Zeitpunkt, zu dem Sie es verwenden git status, git logoder nicht zu dem Zeitpunkt git diff, zu dem Sie es verwenden , oder . Das Erkennen der Umbenennung ist nur für bereitgestellte Dateien sinnvoll. So eine durch eine Änderung in der Datei folgt aussehen in kann , als ob es sie als eine betrachtet , aber wenn Sie verwenden (wie bei ) auf die Datei, so wird deutlich , dass die Änderung zu groß ist , als Umbenennungs erkannt werden. git addgit mvgit rmgit mvgit statusrenamegit stagegit add
Jarl
5
Der eigentliche Schlüssel scheint darin zu bestehen, sowohl die neuen als auch die alten Standorte an derselben Stelle hinzuzufügen git add, was, wie @ jrhorn424 vorschlägt, wahrscheinlich nur das gesamte Repo auf einmal sein sollte.
Brian
4
Dies ist jedoch nicht unfehlbar, insbesondere wenn sich die Datei geändert hat und klein ist. Gibt es eine Methode, um Git speziell über den Umzug zu informieren?
Rebs
112

Führen Sie die Verschiebung und Änderung in separaten Commits durch.

Hank Gay
quelle
12
Ich verstehe, dass Git Bewegungen und Modifikationen gleichzeitig verarbeiten kann. Wenn Sie in Java programmieren und eine IDE verwenden, ist das Umbenennen einer Klasse sowohl eine Änderung als auch eine Verschiebung. Ich verstehe, dass Git sogar in der Lage sein sollte, es automatisch herauszufinden, wenn es einen Umzug gibt (aus einem Entfernen und einer Kreation).
Pupeno
1
Perl benötigt dies auch und ich habe Git noch nie den Umzug / Umbenennen erkennen lassen.
Jrockway
10
@ Rockway hatte ich. Passiert leicht mit kleinen Dateien, ich denke, sie werden "zu unterschiedlich", um einen Umzug zu bedeuten ".
ANeves
2
Gibt es eine Möglichkeit, dies automatisiert zu tun? Ich habe viele Dateien im Index, möchte zuerst den Umzug und dann die Änderung
festschreiben
5
Zu beachten ist, dass git diffdie Umbenennung bei einem Commit nicht bei zwei Commits erkannt wird. Sie müssten -Maka verwenden --find-renames, um das zu tun. Wenn die Motivation, die Sie zu dieser Frage geführt hat, darin besteht, die Umbenennung in einer Pull-Anfrage zu sehen (aus Erfahrung), führt die Aufteilung in zwei Commits nicht zu diesem Ziel.
Mattliu
44

Es ist alles eine Wahrnehmungssache. Git ist im Allgemeinen ziemlich gut darin, Bewegungen zu erkennen, da GIT ein Content-Tracker ist

Alles, was wirklich davon abhängt, ist, wie Ihr "stat" es anzeigt. Der einzige Unterschied ist hier die -M-Flagge.

git log --stat -M

commit 9c034a76d394352134ee2f4ede8a209ebec96288
Author: Kent Fredric
Date:   Fri Jan 9 22:13:51 2009 +1300


        Category Restructure

     lib/Gentoo/Repository.pm                |   10 +++++-----
     lib/Gentoo/{ => Repository}/Base.pm     |    2 +-
     lib/Gentoo/{ => Repository}/Category.pm |   12 ++++++------
     lib/Gentoo/{ => Repository}/Package.pm  |   10 +++++-----
     lib/Gentoo/{ => Repository}/Types.pm    |   10 +++++-----
     5 files changed, 22 insertions(+), 22 deletions(-)

git log --stat

commit 9c034a76d394352134ee2f4ede8a209ebec96288
Author: Kent Fredric
Date:   Fri Jan 9 22:13:51 2009 +1300

    Category Restructure

 lib/Gentoo/Base.pm                |   36 ------------------------
 lib/Gentoo/Category.pm            |   51 ----------------------------------
 lib/Gentoo/Package.pm             |   41 ---------------------------
 lib/Gentoo/Repository.pm          |   10 +++---
 lib/Gentoo/Repository/Base.pm     |   36 ++++++++++++++++++++++++
 lib/Gentoo/Repository/Category.pm |   51 ++++++++++++++++++++++++++++++++++
 lib/Gentoo/Repository/Package.pm  |   41 +++++++++++++++++++++++++++
 lib/Gentoo/Repository/Types.pm    |   55 +++++++++++++++++++++++++++++++++++++
 lib/Gentoo/Types.pm               |   55 -------------------------------------
 9 files changed, 188 insertions(+), 188 deletions(-)

Git Hilfe Protokoll

   -M
       Detect renames.

   -C
       Detect copies as well as renames. See also --find-copies-harder.
Kent Fredric
quelle
76
Es tut mir leid, wenn dies etwas pedantisch erscheint, aber "Git ist im Allgemeinen ziemlich gut darin, Bewegungen zu erkennen, weil GIT ein Content-Tracker ist" scheint mir keine Folge zu sein. Es ist ein Content-Tracker, ja, und vielleicht ist es gut darin, Bewegungen zu erkennen, aber die eine Aussage folgt nicht wirklich der anderen. Nur weil es sich um einen Content-Tracker handelt, ist die Bewegungserkennung nicht unbedingt gut. Tatsächlich könnte ein Content-Tracker überhaupt keine Bewegungserkennung haben.
Laurence Gonsalves
1
@WarrenSeine Wenn Sie ein Verzeichnis umbenennen, ändern sich die SHA1 der Dateien in diesem Verzeichnis nicht. Sie haben lediglich ein neues TREE-Objekt mit denselben SHA1s. Es gibt keinen Grund, warum eine Umbenennung des Verzeichnisses zu erheblichen Datenänderungen führen würde.
Kent Fredric
1
@KentFredric Du hast gezeigt, dass wir mit -M mit "git log" überprüfen können, ob die Datei umbenannt wurde oder nicht, und ich habe es versucht und es funktioniert einwandfrei, aber wenn ich meinen Code zur Überprüfung poste und diese Datei in gerrit ( gerritcodereview.com ) sehe Dort wird angezeigt, dass die Datei neu hinzugefügt und die vorherige gelöscht wurde. Gibt es also eine Option in "git commit", mit der ich festschreibe und gerrit es richtig anzeigt?
Patrick
1
Nein, das habe ich überhaupt nicht gezeigt. Ich habe gezeigt, dass Git in der Lage ist, so zu tun, als wäre Add + Delete eine Umbenennung, da der Inhalt derselbe ist. Es gibt keine Möglichkeit zu wissen, was passiert ist, und es ist ihm egal. Alle Git-Erinnerungen werden "hinzugefügt" und "gelöscht". "Umbenannt" wird nie aufgenommen.
Kent Fredric
1
Zum Beispiel habe ich in letzter Zeit viel "Copy + Edit" für ein Repo gemacht. Die meisten Arten der Anzeige sehen nur "neue Datei", aber wenn Sie bestehen git log -M1 -C1 -B1 -D --find-copies-harder, kann git "entdecken", dass die neue Datei möglicherweise zuerst kopiert wurde. Manchmal macht es das richtig, manchmal findet es völlig unabhängige Dateien, die zufällig identischen Inhalt haben.
Kent Fredric
36

git diff -Moder git log -Msollten solche Änderungen automatisch als Umbenennung mit geringfügigen Änderungen erkennen , solange sie tatsächlich vorhanden sind. Wenn Ihre geringfügigen Änderungen nicht geringfügig sind, können Sie die Ähnlichkeit verringern, z

$ git log -M20 -p --stat

um es von der Standardeinstellung von 50% auf 20% zu reduzieren.

Simon East
quelle
13
Ist es möglich, einen Schwellenwert in der Konfiguration zu definieren?
Wiedererkennung
34

Hier ist eine schnelle und schmutzige Lösung für eine oder mehrere umbenannte und geänderte Dateien, die nicht festgeschrieben sind.

Angenommen, die Datei wurde benannt foound heißt jetzt bar:

  1. Umbenennen barin einen temporären Namen:

    mv bar side
    
  2. Kasse foo:

    git checkout HEAD foo
    
  3. Benennen Sie fooauf barmit Git:

    git mv foo bar
    
  4. Benennen Sie nun Ihre temporäre Datei wieder in um bar.

    mv side bar
    

Dieser letzte Schritt bringt Ihren geänderten Inhalt zurück in die Datei.

Dies kann zwar funktionieren, aber wenn sich der Inhalt der verschobenen Datei zu stark vom ursprünglichen Git unterscheidet, ist es effizienter, zu entscheiden, dass es sich um ein neues Objekt handelt. Lassen Sie mich demonstrieren:

$ git status
On branch workit
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    new file:   .gitignore
    renamed:    README -> README.md

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   README.md
    modified:   work.js

$ git add README.md work.js # why are the changes unstaged, let's add them.
$ git status
On branch workit
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    new file:   .gitignore
    deleted:    README
    new file:   README.md
    modified:   work.js

$ git stash # what? let's go back a bit
Saved working directory and index state WIP on dir: f7a8685 update
HEAD is now at f7a8685 update
$ git status
On branch workit
Untracked files:
  (use "git add <file>..." to include in what will be committed)

    .idea/

nothing added to commit but untracked files present (use "git add" to track)
$ git stash pop
Removing README
On branch workit
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    new file:   .gitignore
    new file:   README.md

Changes not staged for commit:
  (use "git add/rm <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    deleted:    README
    modified:   work.js

Dropped refs/stash@{0} (1ebca3b02e454a400b9fb834ed473c912a00cd2f)
$ git add work.js
$ git status
On branch workit
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    new file:   .gitignore
    new file:   README.md
    modified:   work.js

Changes not staged for commit:
  (use "git add/rm <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    deleted:    README

$ git add README # hang on, I want it removed
$ git status
On branch workit
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    new file:   .gitignore
    deleted:    README
    new file:   README.md
    modified:   work.js

$ mv README.md Rmd # Still? Try the answer I found.
$ git checkout README
error: pathspec 'README' did not match any file(s) known to git.
$ git checkout HEAD README # Ok the answer needed fixing.
$ git status
On branch workit
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    new file:   .gitignore
    new file:   README.md
    modified:   work.js

Changes not staged for commit:
  (use "git add/rm <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    deleted:    README.md
    modified:   work.js

Untracked files:
  (use "git add <file>..." to include in what will be committed)

    Rmd

$ git mv README README.md
$ git status
On branch workit
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    new file:   .gitignore
    renamed:    README -> README.md
    modified:   work.js

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   work.js

Untracked files:
  (use "git add <file>..." to include in what will be committed)

    Rmd

$ mv Rmd README.md
$ git status
On branch workit
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    new file:   .gitignore
    renamed:    README -> README.md
    modified:   work.js

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   README.md
    modified:   work.js

$ # actually that's half of what I wanted; \
  # and the js being modified twice? Git prefers it in this case.
Dlamblin
quelle
27
Dieser Prozess hat keinen Zweck. git fügt keine Metadaten zum Umbenennen hinzu, git mvist einfach eine Annehmlichkeit für ein git rm/ git addPaar. Wenn Sie bereits 'mv bar foo' gemacht haben, müssen Sie nur sicherstellen, dass Sie git add foound git rm barbevor Sie das Commit durchführen. Dies kann wie ein einzelner git add -ABefehl oder möglicherweise eine git add foo; git commit -aSequenz erfolgen.
CB Bailey
17
Ich weiß nur, dass Git es vorher nicht als Bewegung erkannt hat, bevor ich es getan habe. Nachdem ich das getan hatte, erkannte Git es als einen Zug.
8
Es erkennt es als eine Bewegung. Aber es hat jetzt auch die Änderung als nicht inszenierte Änderung. Sobald Sie adddie Datei erneut haben, bricht git das Verschieben / Ändern in ein gelöschtes / wieder hinzugefügtes.
Michael Piefel
4
Dieser Vorgang würde funktionieren, wenn Sie git commitnach Schritt 3, sonst ist er falsch. Außerdem ist @CharlesBailey korrekt. Sie können mv blah fooin Schritt 3 genauso einfach eine Normalisierung durchführen, gefolgt von einem Commit, und die gleichen Ergebnisse erzielen .
DaFlame
5
"Dieser Prozess hat keinen Zweck." - Es dient einem Zweck, wenn Git verwirrt ist und denkt, dass eine Datei gelöscht und eine weitere Datei hinzugefügt wurde. Dies beseitigt die Verwirrung von Git und markiert die Datei als explizit verschoben, ohne dass zwischenzeitliche Commits durchgeführt werden müssen.
Danack
20

Wenn Sie davon sprechen git status, die Umbenennungen nicht anzuzeigen, versuchen Sie es git commit --dry-run -astattdessen

BoD
quelle
11

Wenn Sie TortoiseGit verwenden, ist es wichtig zu beachten, dass die automatische Umbenennungserkennung von Git während des Festschreibens erfolgt, die Tatsache, dass dies geschehen wird, jedoch nicht immer vorher von der Software angezeigt wird. Ich hatte zwei Dateien in ein anderes Verzeichnis verschoben und einige geringfügige Änderungen vorgenommen. Ich verwende TortoiseGit als Commit-Tool und in der Liste der vorgenommenen Änderungen wurden die Dateien angezeigt, die gelöscht und hinzugefügt und nicht verschoben wurden. Das Ausführen des Git-Status über die Befehlszeile zeigte eine ähnliche Situation. Nach dem Festschreiben der Dateien wurden sie jedoch im Protokoll umbenannt. Die Antwort auf Ihre Frage lautet also: Solange Sie nichts zu drastisches getan haben, sollte Git die Umbenennung automatisch übernehmen.

Bearbeiten: Wenn Sie die neuen Dateien hinzufügen und dann über die Befehlszeile einen Git-Status ausführen, sollte die Umbenennung vor dem Festschreiben angezeigt werden.

Bearbeiten 2: Fügen Sie in TortoiseGit außerdem die neuen Dateien im Festschreibungsdialog hinzu, aber schreiben Sie sie nicht fest. Wenn Sie dann in den Befehl Show Log gehen und sich das Arbeitsverzeichnis ansehen, werden Sie sehen, ob Git die Umbenennung vor dem Festschreiben erkannt hat.

Dieselbe Frage wurde hier gestellt: https://tortoisegit.org/issue/1389 und wurde als Fehler behoben, der hier behoben werden muss: https://tortoisegit.org/issue/1440 Es stellt sich heraus, dass es sich um ein Anzeigeproblem mit TortoiseGits Commit handelt Dialog und existiert auch irgendwie im Git-Status, wenn Sie die neuen Dateien nicht hinzugefügt haben.

Giles Roberts
quelle
2
Sie haben Recht, auch wenn TortoiseGit Löschen + Hinzufügen anzeigt, auch wenn der Git-Status Löschen + Hinzufügen anzeigt, auch wenn Git Commit - Trockenlauf Löschen + Hinzufügen anzeigt. Nach Git Commit sehe ich Umbenennen und nicht Löschen + Hinzufügen.
Tomas Kubes
1
Ich bin mir ziemlich sicher, dass die automatische Umbenennungserkennung beim Abrufen des Verlaufs erfolgt . Beim Commit wird immer add + delete verwendet. Das erklärt auch das von Ihnen beschriebene schizophrene Verhalten. Daher die Optionen hier: stackoverflow.com/a/434078/155892
Mark Sowul
10

Oder versuchen Sie die Antwort auf diese Frage hier von Amber ! Um es noch einmal zu zitieren:

Brechen Sie zunächst Ihr inszeniertes Hinzufügen für die manuell verschobene Datei ab:

$ git reset path/to/newfile
$ mv path/to/newfile path/to/oldfile

Verwenden Sie dann Git, um die Datei zu verschieben:

$ git mv path/to/oldfile path/to/newfile

Wenn Sie die manuelle Verschiebung bereits festgeschrieben haben, möchten Sie möglicherweise stattdessen vor der Verschiebung auf die Revision zurücksetzen und dann einfach mv von dort aus git.

Zingam
quelle
7

Verwenden Sie den git mvBefehl zum Verschieben der Dateien anstelle der Befehle zum Verschieben des Betriebssystems: https://git-scm.com/docs/git-mv

Bitte beachten Sie, dass der git mvBefehl nur in Git-Versionen 1.8.5 und höher vorhanden ist. Möglicherweise müssen Sie Ihr Git aktualisieren, um diesen Befehl verwenden zu können.

mostafa.elhoushi
quelle
3

Ich hatte dieses Problem kürzlich beim Verschieben (aber nicht Ändern) einiger Dateien.

Das Problem ist, dass Git einige Zeilenenden geändert hat, als ich die Dateien verschoben habe, und dann nicht feststellen konnte, dass die Dateien identisch waren.

Mit git mvProblem wurde das Problem behoben, aber es funktioniert nur mit einzelnen Dateien / Verzeichnissen, und ich hatte viele Dateien im Stammverzeichnis des Repositorys zu erledigen.

Eine Möglichkeit, dies zu beheben, wäre etwas Bash / Batch-Magie.

Ein anderer Weg ist der folgende

  • Verschieben Sie die Dateien und git commit. Dadurch werden die Zeilenenden aktualisiert.
  • Verschieben Sie die Dateien an ihren ursprünglichen Speicherort zurück, nachdem sie die neuen Zeilenenden haben, und git commit --amend
  • Verschieben Sie die Dateien erneut und git commit --amend. Diesmal gibt es keine Änderung an den Zeilenenden, sodass Git glücklich ist
cedd
quelle
1

Für mich hat es funktioniert, alle Änderungen vor dem Festschreiben zu speichern und sie wieder herauszuholen. Dadurch analysierte git die hinzugefügten / gelöschten Dateien erneut und markierte sie korrekt als verschoben.

Alex Ilie
quelle
0

Es gibt wahrscheinlich eine bessere "Kommandozeilen" -Methode, und ich weiß, dass dies ein Hack ist, aber ich habe nie eine gute Lösung gefunden.

Verwenden von TortoiseGIT: Wenn Sie ein GIT-Commit haben, bei dem einige Vorgänge zum Verschieben von Dateien als Laden von Hinzufügungen / Löschungen angezeigt werden, anstatt sie umzubenennen, obwohl die Dateien nur geringfügige Änderungen aufweisen, gehen Sie folgendermaßen vor:

  1. Überprüfen Sie, was Sie vor Ort getan haben
  2. Checken Sie eine kleine einzeilige Änderung in einem zweiten Commit ein
  3. Gehen Sie zu GIT Log in Tortoise Git
  4. Wählen Sie die beiden Commits aus, klicken Sie mit der rechten Maustaste und wählen Sie "In einem Commit zusammenführen".

Das neue Commit zeigt nun die Umbenennungen der Dateien ordnungsgemäß an. Dies hilft dabei, den ordnungsgemäßen Dateiversionsverlauf aufrechtzuerhalten.

Jon Rea
quelle
0

Wenn ich eine Datei gleichzeitig bearbeite, umbenenne und verschiebe, funktioniert keine dieser Lösungen. Die Lösung besteht darin, dies in zwei Commits durchzuführen (bearbeiten und umbenennen / separat verschieben) und dann fixupdas zweite Commit über git rebase -i, um es in einem Commit zu haben.

Hativ
quelle
0

Ich verstehe diese Frage wie folgt: "Wie kann Git das Löschen einer alten Datei und das Erstellen einer neuen Datei als Dateiverschiebung erkennen?"

Ja, wenn Sie im Arbeitsverzeichnis eine alte Datei löschen und eine alte Datei einfügen, git statuswerden " deleted: old_file" und " Untracked files: ... new_file" angezeigt.

Im Staging-Index / der Staging-Ebene wird das Hinzufügen und Entfernen von Dateien mit git jedoch als Dateiverschiebung erkannt. Geben Sie dazu die folgenden Befehle ein, vorausgesetzt, Sie haben das Löschen und Erstellen mit Ihrem Betriebssystem durchgeführt:

git add new_file
git rm old_file

Wenn der Inhalt der Datei zu 50% oder mehr ähnlich ist, sollte der git statusBefehl running Folgendes ergeben:

renamed: old_file -> new_file
hardainfo
quelle
1
git statuswird sagen " deleted: old_file" und " Untracked files: ... new_file": Nicht seit Git 2.18: stackoverflow.com/a/50573107/6309
VonC