Umbenennen von Dateien in Git umbenennen

440

Ich habe gelesen, dass Sie beim Umbenennen von Dateien in Git alle Änderungen festschreiben, Ihre Umbenennung durchführen und dann Ihre umbenannte Datei bereitstellen sollten. Git erkennt die Datei anhand des Inhalts, anstatt sie als neue nicht verfolgte Datei zu betrachten, und behält den Änderungsverlauf bei.

Als ich heute Abend genau das tat, kehrte ich jedoch zurück git mv.

> $ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   modified:   index.html
#

Benennen Sie mein Stylesheet im Finder von iphone.cssnach ummobile.css

> $ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   modified:   index.html
#
# Changed but not updated:
#   (use "git add/rm <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#   deleted:    css/iphone.css
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#   css/mobile.css

Git glaubt nun, ich hätte eine CSS-Datei gelöscht und eine neue hinzugefügt. Nicht was ich will, lass uns die Umbenennung rückgängig machen und git die Arbeit machen lassen.

> $ git reset HEAD .
Unstaged changes after reset:
M   css/iphone.css
M   index.html

Zurück zu meinem Ausgangspunkt.

> $ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   modified:   index.html
#

Verwenden wir git mvstattdessen.

> $ git mv css/iphone.css css/mobile.css
> $ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   renamed:    css/iphone.css -> css/mobile.css
#
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#   modified:   index.html
#

Sieht so aus, als wären wir gut. Warum hat git die Umbenennung beim ersten Mal nicht erkannt, als ich Finder verwendet habe?

Greg K.
quelle
29
Git verfolgt Inhalte, keine Dateien. Es spielt also keine Rolle, wie Sie Ihren Index in den richtigen Zustand versetzen - add+rmoder mv- es wird das gleiche Ergebnis erzielt. Git verwendet dann die Umbenennungs- / Kopiererkennung, um Sie darüber zu informieren, dass es sich um eine Umbenennung handelt. Die von Ihnen angegebene Quelle ist ebenfalls ungenau. Es ist wirklich egal, ob Sie im selben Commit + umbenennen oder nicht. Wenn Sie einen Unterschied zwischen Ändern und Umbenennen vornehmen, wird dies bei der Umbenennungserkennung als Umbenennen + Ändern angezeigt. Wenn es sich bei der Änderung um eine vollständige Umschreibung handelt, wird sie als hinzugefügt und gelöscht angezeigt - unabhängig davon, wie Sie sie ausgeführt haben es.
Cascabel
6
Wenn dies zutrifft, warum wurde es bei meiner Umbenennung mit Finder nicht erkannt?
Greg K
26
git mv old newaktualisiert den Index automatisch. Wenn Sie außerhalb von Git umbenennen, müssen Sie das git add newund git rm olddie Änderungen am Index vornehmen. Sobald Sie dies getan haben, wird dies git statuswie erwartet funktionieren.
Chris Johnsen
4
Ich habe gerade ein paar Dateien in ein public_htmlVerzeichnis verschoben , die in Git verfolgt werden. Nach der Ausführung git add .und git commitzeigte es immer noch eine Reihe von 'gelöschten' Dateien in git status. Ich habe a ausgeführt git commit -aund die Löschungen wurden festgeschrieben, aber jetzt habe ich keinen Verlauf für die Dateien, in denen public_htmljetzt leben . Dieser Arbeitsablauf ist nicht so reibungslos, wie ich es gerne hätte.
Greg K

Antworten:

352

Für git mvdie Handbuchseite heißt es

Der Index wird nach erfolgreichem Abschluss aktualisiert, […]

Sie müssen den Index also zunächst selbst aktualisieren (mithilfe von git add mobile.css). Es werden jedoch
git status weiterhin zwei verschiedene Dateien angezeigt

$ git status
# On branch master
warning: LF will be replaced by CRLF in index.html
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       modified:   index.html
#       new file:   mobile.css
#
# Changed but not updated:
#   (use "git add/rm <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       deleted:    iphone.css
#

Sie können eine andere Ausgabe erhalten, indem Sie ausführen git commit --dry-run -a, was zu dem führt, was Sie erwarten:

Tanascius@H181 /d/temp/blo (master)
$ git commit --dry-run -a
# On branch master
warning: LF will be replaced by CRLF in index.html
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       modified:   index.html
#       renamed:    iphone.css -> mobile.css
#

Ich kann Ihnen nicht genau sagen, warum wir diese Unterschiede zwischen git statusund sehen
git commit --dry-run -a, aber hier ist ein Hinweis von Linus :

git , dass es nicht auch egal über die ganze „Umbenennungs detection“ intern und alle Commits Sie mit Umbenennungen getan haben , sind völlig unabhängig von den Heuristiken wir dann verwenden , zeigen die Umbenennungen.

A dry-runverwendet die realen Umbenennungsmechanismen, während a dies git statuswahrscheinlich nicht tut.

Tanascius
quelle
1
Sie haben den Schritt, in dem Sie ihn ausgeführt haben, nicht erwähnt git add mobile.css. Ohne sie git status -ahätte nur das Entfernen der zuvor verfolgten iphone.cssDatei "gesehen", aber die neue, nicht verfolgte Datei nicht berührt mobile.css. Auch git status -aist ungültig mit Git 1.7.0 und höher. "" Git status "ist nicht mehr" git commit --dry-run "." in kernel.org/pub/software/scm/git/docs/RelNotes-1.7.0.txt . Verwenden git commit --dry-run -aSie diese Option, wenn Sie diese Funktionalität wünschen. Wie andere gesagt haben, aktualisieren Sie einfach den Index und git statuses funktioniert genau so, wie es das OP erwartet.
Chris Johnsen
3
Wenn Sie eine normale git commitDatei ausführen, wird die umbenannte Datei nicht festgeschrieben, und der Arbeitsbaum ist immer noch derselbe. git commit -abesiegt so ziemlich jeden Aspekt des Workflow- / Denkmodells von git - jede Änderung wird begangen. Was ist, wenn Sie die Datei nur umbenennen möchten, aber Änderungen index.htmlin einem anderen Commit festschreiben möchten ?
Knittl
@ Chris: Ja, natürlich habe ich hinzugefügt, mobile.csswas ich hätte erwähnen sollen. Aber das ist der Punkt meiner Antwort: Die Manpage sagt das, the index is updatedwenn Sie verwenden git-mv. Vielen Dank für die status -aKlarstellung, ich habe Git 1.6.4
Tanascius
4
Gute Antwort! Ich schlug meinen Kopf gegen die Wand und versuchte herauszufinden, warum ich git statusdie Umbenennung nicht entdeckte. Das Ausführen git commit -a --dry-runnach dem Hinzufügen meiner "neuen" Dateien zeigte die Umbenennungen und gab mir schließlich das Vertrauen, mich zu verpflichten!
stephen.hanson
1
In Git 1.9.1 git statusverhält es sich jetzt wie git commit.
Jacques René Mesrine
77

Sie müssen die beiden geänderten Dateien zum Index hinzufügen, bevor git sie als Verschiebung erkennt.

Der einzige Unterschied zwischen mv old newund git mv old newbesteht darin, dass der Git MV auch die Dateien zum Index hinzufügt.

mv old newdann git add -Ahätte es auch geklappt.

Beachten Sie, dass Sie nicht nur verwenden können, git add .da dadurch keine Entfernungen zum Index hinzugefügt werden.

Siehe Unterschied zwischen "git add -A" und "git add".

nicht rechteckig
quelle
3
Vielen Dank für den git add -ALink, sehr nützlich, da ich nach einer solchen Verknüpfung gesucht habe!
PhiLho
5
Beachten Sie, dass mit git 2, git add . nicht umzug zum Index hinzufügen.
Nick McCurdy
19

Am besten probieren Sie es selbst aus.

mkdir test
cd test
git init
touch aaa.txt
git add .
git commit -a -m "New file"
mv aaa.txt bbb.txt
git add .
git status
git commit --dry-run -a

Jetzt zeigen git status und git commit --dry-run -a zwei verschiedene Ergebnisse an, wobei git status bbb.txt als neue Datei anzeigt / aaa.txt gelöscht wird und die Befehle --dry-run die tatsächliche Umbenennung anzeigen.

~/test$ git status

# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   new file:   bbb.txt
#
# 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:    aaa.txt
#


/test$ git commit --dry-run -a

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

Jetzt checken Sie ein.

git commit -a -m "Rename"

Jetzt können Sie sehen, dass die Datei tatsächlich umbenannt wurde und was im Git-Status angezeigt wird, falsch ist.

Moral der Geschichte: Wenn Sie nicht sicher sind, ob Ihre Datei umbenannt wurde, geben Sie ein "git commit --dry-run -a" aus. Wenn angezeigt wird, dass die Datei umbenannt wurde, können Sie loslegen.

dimuthu
quelle
3
Für Git sind beide richtig . Letzteres ist näher daran, wie Sie es als Commiter wahrscheinlich sehen. Der wirkliche Unterschied zwischen Umbenennen und Löschen + Erstellen liegt nur auf der Ebene des Betriebssystems / Dateisystems (z. B. gleiche Inode # vs. neue Inode #), was Git nicht wirklich wichtig ist.
Alois Mahdal
15

Für Git 1.7.x haben die folgenden Befehle für mich funktioniert:

git mv css/iphone.css css/mobile.css
git commit -m 'Rename folder.' 

Git add musste nicht hinzugefügt werden, da sich die Originaldatei (dh css / mobile.css) bereits zuvor in den festgeschriebenen Dateien befand.

GrigorisG
quelle
5
Diese. Alle anderen Antworten sind lächerlich und unnötig komplex. Dadurch wird der Dateiversionsverlauf zwischen den Commits beibehalten, sodass Zusammenführungen vor / nach dem Umbenennen der Datei nicht unterbrochen werden.
Phlucious
10

du musst git add css/mobile.cssdie neue datei und git rm css/iphone.cssdamit weiß git davon. dann wird die gleiche Ausgabe in angezeigtgit status

Sie können es deutlich in der Statusausgabe sehen (der neue Name der Datei):

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

und (der alte Name):

# Changed but not updated:
#   (use "git add/rm <file>..." to update what will be committed)

Ich denke, hinter den Kulissen git mvgibt es nichts weiter als ein Wrapper-Skript, das genau das tut: Löschen Sie die Datei aus dem Index und fügen Sie sie unter einem anderen Namen hinzu

stricken
quelle
Ich dachte nicht, dass ich git rm css/iphone.cssmüsste, weil ich dachte, dies würde die bestehende Geschichte entfernen. Vielleicht verstehe ich den Workflow in Git falsch.
Greg K
4
@ Greg K: Entfernt git rmden Verlauf nicht. Es wird nur ein Eintrag aus dem Index entfernt, sodass das nächste Commit den Eintrag nicht enthält. Es wird jedoch weiterhin in den Ahnen-Commits vorhanden sein. Was Sie möglicherweise verwirren, ist, dass (zum Beispiel) git log -- newan dem Punkt aufhört, an dem Sie sich verpflichtet haben git mv old new. Wenn Sie Umbenennungen folgen möchten, verwenden Sie git log --follow -- new.
Chris Johnsen
9

Lassen Sie uns über Ihre Dateien aus der Git-Perspektive nachdenken.

Denken Sie daran, dass git keine Metadaten zu Ihren Dateien verfolgt

Ihr Repository hat (unter anderem)

$ cd repo
$ ls
...
iphone.css
...

und es ist unter git Kontrolle:

$ git ls-files --error-unmatch iphone.css &>/dev/null && echo file is tracked
file is tracked

Testen Sie dies mit:

$ touch newfile
$ git ls-files --error-unmatch newfile &>/dev/null && echo file is tracked
(no output, it is not tracked)
$ rm newfile

Wenn Sie das tun

$ mv iphone.css mobile.css

Aus Git-Perspektive,

  • Es gibt keine iphone.css (sie wird gelöscht - Git warnt davor).
  • Es gibt eine neue Datei mobile.css .
  • Diese Dateien haben nichts miteinander zu tun.

Git berät also über bereits bekannte Dateien ( iphone.css ) und neue erkannte Dateien ( mobile.css ), jedoch nur, wenn sich Dateien im Index befinden oder HEAD git beginnt, deren Inhalt zu überprüfen.

Derzeit sind weder "iphone.css deletion" noch mobile.css im Index.

Fügen Sie dem Index das Löschen von iphone.css hinzu

$ git rm iphone.css

git sagt dir genau, was passiert ist: ( iphone.css wird gelöscht. Es ist nichts mehr passiert)

Fügen Sie dann die neue Datei mobile.css hinzu

$ git add mobile.css

Diesmal befinden sich sowohl das Löschen als auch die neue Datei im Index. Jetzt erkennt git, dass der Kontext identisch ist, und macht ihn als Umbenennung verfügbar. Wenn Dateien zu 50% ähnlich sind, wird dies als Umbenennung erkannt , sodass Sie mobile.css ein wenig ändern können, während der Vorgang als Umbenennung beibehalten wird .

Siehe dies ist reproduzierbar auf git diff. Jetzt, da Ihre Dateien im Index sind, müssen Sie verwenden --cached. Bearbeiten Sie mobile.css ein wenig, fügen Sie das zum Index hinzu und sehen Sie den Unterschied zwischen:

$ git diff --cached 

und

$ git diff --cached -M

-Mist die Option "Umbenennungen erkennen" für git diff. -Msteht für -M50%(50% oder mehr Ähnlichkeit lässt git es als Umbenennung ausdrücken), aber Sie können dies auf -M20%(20%) reduzieren, wenn Sie mobile.css häufig bearbeiten.

Albfan
quelle
8

Schritt 1: Benennen Sie die Datei von oldfile in newfile um

git mv #oldfile #newfile

Schritt 2: Git Commit und Kommentare hinzufügen

git commit -m "rename oldfile to newfile"

Schritt 3: Drücken Sie diese Änderung auf Remote-Server

git push origin #localbranch:#remotebranch
Haimei
quelle
1
Bitte fügen Sie einen Kommentar hinzu, damit es für OP
Devrath
Schritt 2 ist nicht erforderlich. Danach git mvbefindet sich die neue Datei bereits im Index.
Alois Mahdal
7

Git erkennt die Datei am Inhalt und sieht sie nicht als neue, nicht verfolgte Datei

Dort bist du falsch gelaufen.

Erst nachdem Sie die Datei hinzugefügt haben, erkennt Git sie anhand des Inhalts.

hasen
quelle
Genau. Bei der Inszenierung zeigt git die Umbenennung korrekt an.
Max MacLeod
3

Sie haben die Ergebnisse Ihres Finder-Zuges nicht inszeniert. Ich glaube, wenn Sie den Umzug über Finder und dann getan hätten git add css/mobile.css ; git rm css/iphone.css, würde git den Hash der neuen Datei berechnen und erst dann erkennen, dass die Hashes der Dateien übereinstimmen (und somit eine Umbenennung).

Mike Seplowitz
quelle
2

In Fällen, in denen Sie die Dateien wirklich manuell umbenennen müssen, z. Verwenden eines Skripts zum Batch-Umbenennen einer Reihe von Dateien, dann mit git add -A .für mich funktioniert.

pckben
quelle
2

Für Xcode-Benutzer: Wenn Sie Ihre Datei in Xcode umbenennen, wird das Ausweissymbol in Anhängen geändert. Wenn Sie ein Commit mit XCode durchführen, erstellen Sie tatsächlich eine neue Datei und verlieren den Verlauf.

Eine Problemumgehung ist einfach, muss jedoch vor dem Festschreiben mit Xcode durchgeführt werden:

  1. Machen Sie einen Git-Status in Ihrem Ordner. Sie sollten sehen, dass die inszenierten Änderungen korrekt sind:

umbenannt in: Project / OldName.h -> Project / NewName.h umbenannt in: Project / OldName.m -> Project / NewName.m

  1. Commit -m 'Namensänderung'

Kehren Sie dann zu XCode zurück, und Sie werden sehen, dass das Abzeichen von A nach M geändert wurde. Es ist sicher, weitere Änderungen bei der Verwendung von xcode vorzunehmen.

doozMen
quelle