Was ist der Zweck von git-mv?

287

Soweit ich weiß, muss Git die Vorgänge zum Umbenennen / Verschieben / Kopieren von Dateien nicht wirklich verfolgen. Was ist also der eigentliche Zweck von git mv ? Die Manpage ist nicht besonders beschreibend ...

Ist es veraltet? Ist es ein interner Befehl, der nicht von normalen Benutzern verwendet werden soll?

Mauricio Scheffer
quelle

Antworten:

390
git mv oldname newname

ist nur eine Abkürzung für:

mv oldname newname
git add newname
git rm oldname

Das heißt, der Index für alte und neue Pfade wird automatisch aktualisiert.

CB Bailey
quelle
38
Es hat auch ein paar Sicherheitsvorkehrungen eingebaut.
Jakub Narębski
6
Danke @CharlesBailey - Betrachtet git dann die Dateien newNameFile und oldNameFile als unterschiedlich? Wenn ja, was passiert, wenn wir sie zusammenführen möchten? Angenommen, wir verzweigen ein Ameisenprojekt in Zweig A und erstellen Zweig B, dann mavenisieren wir Projekte in B. Die Dateinamen sind dieselben, werden jedoch bei Änderung der Projektstruktur auf unterschiedliche Pfade gesetzt. Angenommen, beide Zweige sind einige Zeit parallel gewachsen. Wenn wir irgendwann die Projekte zusammenführen möchten, woher weiß Git dann, dass es sich um dieselbe Datei handelt, die gerade in Pfad umbenannt wurde? (wenn "git mv" == "git add + git rm")
Rose
2
@SergeyOrshanskiy Wenn die automatische Erkennung schief geht mv oldname newname; git add newname; git rm oldname, geht sie auch schief git mv oldname newname(siehe diese Antwort ).
Ajedi32
5
Beachten Sie, dass git mvsich dies geringfügig von dem unterscheidet , da diese Änderungen erst dann vorgenommen werden mv oldname newname; git add newname; git rm oldname, wenn Sie Änderungen an der Datei vorgenommen haben, bevor git mvSie git adddie neue Datei erstellt haben.
Ajedi32
2
git mv macht etwas anderes, da es Änderungen in der Dateinamen-Groß- / Kleinschreibung (foo.txt bis Foo.txt) behandelt, während diese Befehle nicht einzeln ausgeführt werden (unter OSX)
greg.kindel
66

Aus dem offiziellen GitFaq :

Git hat einen Umbenennungsbefehl git mv, aber das ist nur eine Annehmlichkeit. Der Effekt ist nicht zu unterscheiden vom Entfernen der Datei und Hinzufügen einer weiteren Datei mit einem anderen Namen und demselben Inhalt

Adam Nofsinger
quelle
8
Verlieren Sie also den Dateiversionsverlauf? Ich nahm an, dass die Umbenennung die alte Geschichte für dieses Verzeichnis beibehalten würde ...
Will Hancock
17
Ja und nein. Lesen Sie den offiziellen GitFaq-Link oben über Umbenennungen und dann Linus Torvalds lange E-Mail darüber, warum ihm die Vorstellung von SCM-Tool-Tracking-Dateien nicht gefällt: permalink.gmane.org/gmane.comp.version-control.git/ 217
Adam Nofsinger
3
@WillHancock Ich habe git jetzt etwas häufiger verwendet und kann Ihnen definitiver antworten: Abhängig von Ihrem git-Client und seinen Optionen können Sie die Datei nach dem Umbenennen verfolgen, wenn sich die Datei intern so wenig geändert hat, dass sie als a betrachtet wird umbenennen. Wenn Sie die Datei zu stark ändern UND sie jedoch umbenennen, erkennt git sie nicht - in gewissem Sinne heißt es: "Nein, Sie können genauso gut davon ausgehen, dass es sich um eine völlig andere Datei handelt!"
Adam Nofsinger
7
@AdamNofsinger dieser Link ist tot. Hier ist ein Spiegel: web.archive.org/web/20150209075907/http://…
Carl Walsh
2
Gibt es eine offizielle Referenz (dh schubwürdiger als eine FAQ), die die Gleichwertigkeit zwischen git mvund den manuellen Ansatz angibt ? Es ist nicht offensichtlich von git help mv.
Bis
40

Git versucht nur, für Sie zu erraten, was Sie versuchen zu tun. Es unternimmt jeden Versuch, die ungebrochene Geschichte zu bewahren. Natürlich ist es nicht perfekt. So git mvkönnen Sie Ihre Absicht explizit festlegen und einige Fehler vermeiden.

Betrachten Sie dieses Beispiel. Beginnend mit einem leeren Repo,

git init
echo "First" >a
echo "Second" >b
git add *
git commit -m "initial commit"
mv a c
mv b a
git status

Ergebnis:

# On branch master
# 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)
#
#   modified:   a
#   deleted:    b
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#   c
no changes added to commit (use "git add" and/or "git commit -a")

Autodetection fehlgeschlagen :( Oder?

$ git add *
$ git commit -m "change"
$ git log c

commit 0c5425be1121c20cc45df04734398dfbac689c39
Author: Sergey Orshanskiy <*****@gmail.com>
Date:   Sat Oct 12 00:24:56 2013 -0400

    change

und dann

$ git log --follow c

Author: Sergey Orshanskiy <*****@gmail.com>
Date:   Sat Oct 12 00:24:56 2013 -0400

    change

commit 50c2a4604a27be2a1f4b95399d5e0f96c3dbf70a
Author: Sergey Orshanskiy <*****@gmail.com>
Date:   Sat Oct 12 00:24:45 2013 -0400

    initial commit

Versuchen Sie es stattdessen (denken Sie daran, den .gitOrdner beim Experimentieren zu löschen ):

git init
echo "First" >a
echo "Second" >b
git add *
git commit -m "initial commit"
git mv a c
git status

So weit, ist es gut:

# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   renamed:    a -> c


git mv b a
git status

Jetzt ist niemand mehr perfekt:

# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   modified:   a
#   deleted:    b
#   new file:   c
#

"Ja wirklich?" Aber natürlich...

git add *
git commit -m "change"
git log c
git log --follow c

... und das Ergebnis ist das gleiche wie oben: --followzeigt nur den vollständigen Verlauf.


Seien Sie jetzt vorsichtig beim Umbenennen, da beide Optionen immer noch seltsame Effekte erzeugen können . Beispiel:

git init
echo "First" >a
git add a
git commit -m "initial a"
echo "Second" >b
git add b
git commit -m "initial b"

git mv a c
git commit -m "first move"
git mv b a
git commit -m "second move"

git log --follow a

commit 81b80f5690deec1864ebff294f875980216a059d
Author: Sergey Orshanskiy <*****@gmail.com>
Date:   Sat Oct 12 00:35:58 2013 -0400

    second move

commit f284fba9dc8455295b1abdaae9cc6ee941b66e7f
Author: Sergey Orshanskiy <*****@gmail.com>
Date:   Sat Oct 12 00:34:54 2013 -0400

    initial b

Vergleichen Sie es mit:

git init
echo "First" >a
git add a
git commit -m "initial a"
echo "Second" >b
git add b
git commit -m "initial b"

git mv a c
git mv b a
git commit -m "both moves at the same time"

git log --follow a

Ergebnis:

commit 84bf29b01f32ea6b746857e0d8401654c4413ecd
Author: Sergey Orshanskiy <*****@gmail.com>
Date:   Sat Oct 12 00:37:13 2013 -0400

    both moves at the same time

commit ec0de3c5358758ffda462913f6e6294731400455
Author: Sergey Orshanskiy <*****@gmail.com>
Date:   Sat Oct 12 00:36:52 2013 -0400

    initial a

Ups ... Jetzt geht der Verlauf zurück zu Initiale a anstelle von Initiale b , was falsch ist. Als wir also zwei Züge gleichzeitig machten, wurde Git verwirrt und verfolgte die Änderungen nicht richtig. Übrigens geschah in meinen Experimenten dasselbe, als ich Dateien löschte / erstellte, anstatt sie zu verwenden git mv. Gehen Sie vorsichtig vor; du wurdest gewarnt...

osa
quelle
5
+1 für die ausführliche Erklärung. Ich habe nach Problemen gesucht, die im Protokollverlauf auftreten können, wenn Dateien in Git verschoben werden. Ihre Antwort war wirklich interessant. Danke dir! Übrigens, kennen Sie andere Fallstricke, die wir beim Verschieben von Dateien in Git vermeiden sollten? (oder irgendein Hinweis, auf den Sie verweisen könnten ... nicht sehr glücklich, dafür zu googeln)
pabrantes
1
Nun, meine Beispiele sind pessimistisch. Wenn die Dateien leer sind, ist es viel schwieriger, die Änderungen richtig zu interpretieren. Ich stelle mir vor, dass es Ihnen gut gehen sollte, wenn Sie sich nach jeder Umbenennung festlegen.
osa
27

Wie @Charles sagt, git mvist eine Abkürzung.

Die eigentliche Frage lautet hier: "Andere Versionskontrollsysteme (z. B. Subversion und Perforce) behandeln das Umbenennen von Dateien besonders. Warum nicht Git?"

Linus erklärt unter http://permalink.gmane.org/gmane.comp.version-control.git/217 mit charakteristischem Takt:

Bitte hör auf mit diesem "Track Files" Mist. Git verfolgt genau das, was zählt, nämlich "Sammlungen von Dateien". Nichts anderes ist relevant, und selbst wenn Sie denken, dass es relevant ist, schränkt dies Ihre Weltanschauung nur ein. Beachten Sie, dass der Begriff "Annotieren" von CVS immer zwangsläufig die Verwendung durch Personen einschränkt. Ich denke, es ist ein völlig nutzloser Mist, und ich habe etwas beschrieben, das ich für millionenfach nützlicher halte, und alles ist genau deshalb herausgefallen , weil ich mein Denken nicht auf das falsche Modell der Welt beschränke.

Oberst Panik
quelle
9

Es gibt eine andere Verwendung, die ich git mvoben nicht erwähnt habe.

Seit der Entdeckung git add -p(der Patch-Modus von git add; siehe http://git-scm.com/docs/git-add ) verwende ich ihn gerne, um Änderungen zu überprüfen, wenn ich sie dem Index hinzufüge. So wird mein Workflow (1) Code bearbeiten, (2) überprüfen und zum Index hinzufügen, (3) festschreiben.

Wie git mvpasst das zusammen? Wenn Sie eine Datei direkt mit git rmund verschieben git add, werden alle Änderungen zum Index hinzugefügt, und die Verwendung von git diff zum Anzeigen von Änderungen ist weniger einfach (vor dem Festschreiben). Mit git mvwird jedoch der neue Pfad zum Index hinzugefügt, jedoch keine Änderungen an der Datei vorgenommen, sodass die Arbeit wie gewohnt möglich ist git diffund git add -pfunktioniert.

Dhardy
quelle
5

Es gibt einen Nischenfall, in dem es git mvsehr nützlich bleibt: Wenn Sie die Schreibweise eines Dateinamens in einem Dateisystem ohne Berücksichtigung der Groß- und Kleinschreibung ändern möchten. Sowohl APFS (Mac) als auch NTFS (Windows) unterscheiden standardmäßig nicht zwischen Groß- und Kleinschreibung (jedoch ohne Berücksichtigung der Groß- und Kleinschreibung).

greg.kindel erwähnt dies in einem Kommentar zu CB Baileys Antwort.

Angenommen, Sie arbeiten an einem Mac und lassen eine Datei Mytest.txtvon git verwalten. Sie möchten den Dateinamen in ändern MyTest.txt.

Du könntest es versuchen:

$ mv Mytest.txt MyTest.txt
overwrite MyTest.txt? (y/n [n]) y
$ git status
On branch master
Your branch is up to date with 'origin/master'.

nothing to commit, working tree clean

Ach je. Git erkennt nicht an, dass Änderungen an der Datei vorgenommen wurden.

Sie können dies umgehen, indem Sie die Datei vollständig umbenennen und dann wieder umbenennen:

$ mv Mytest.txt temp.txt
$ git rm Mytest.txt
rm 'Mytest.txt'
$ mv temp.txt MyTest.txt
$ git add MyTest.txt 
$ git status
On branch master
Your branch is up to date with 'origin/master'.

Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    renamed:    Mytest.txt -> MyTest.txt

Hurra!

Oder Sie könnten sich all diese Mühe sparen, indem Sie Folgendes verwenden git mv:

$ git mv Mytest.txt MyTest.txt
$ git status
On branch master
Your branch is up to date with 'origin/master'.

Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    renamed:    Mytest.txt -> MyTest.txt
Duncan
quelle