Ist es möglich, Dateien in Git zu verschieben / umzubenennen und ihren Verlauf beizubehalten?

666

Ich möchte einen Projekt-Teilbaum in Git umbenennen / verschieben, von dem aus er verschoben wird

/project/xyz

zu

/components/xyz

Wenn ich eine Ebene verwende git mv project components, geht der gesamte Festschreibungsverlauf für die xyz projectverloren. Gibt es eine Möglichkeit, dies so zu verschieben, dass die Historie erhalten bleibt?

Sgargan
quelle
2
Ich möchte nur darauf hinweisen, dass ich gerade das Verschieben von Dateien über das Dateisystem getestet habe. Nach dem Festschreiben (über Intellij) kann ich beim Anzeigen des Verlaufs (erneut in Intellij) den gesamten Verlauf (einschließlich des Verlaufs an einem anderen Ort) anzeigen. Ich gehe davon aus, dass Intellij nichts Besonderes tut, um das zu tun. Es ist also schön zu wissen, dass zumindest die Geschichte nachvollzogen werden kann.
BT
Die Regeln, die Git beim Erkennen einer Verzeichnisumbenennung befolgt, finden Sie in meiner Antwort unten
VonC
Ich habe hier eine Antwort geschrieben. Ich hoffe es klappt. stackoverflow.com/questions/10828267/…
Mahmut EFE

Antworten:

650

Git erkennt Umbenennungen, anstatt den Vorgang mit dem Commit fortzusetzen. Es spielt also keine Rolle , ob Sie ihn verwenden git mvoder mvnicht.

Der logBefehl verwendet ein --followArgument, das den Verlauf vor einer Umbenennungsoperation fortsetzt, dh er sucht mithilfe der Heuristik nach ähnlichen Inhalten:

http://git-scm.com/docs/git-log

Verwenden Sie den folgenden Befehl, um den vollständigen Verlauf abzurufen:

git log --follow ./path/to/file
Troels Thomsen
quelle
63
Ich vermute, dass dies eine Leistungsüberlegung ist. Wenn Sie nicht den vollständigen Verlauf benötigen, dauert das Scannen des Inhalts mit Sicherheit länger. Am einfachsten ist es, einen Alias ​​einzurichten git config alias.logf "log --follow"und einfach zu schreiben git logf ./path/to/file.
Troels Thomsen
13
@TroelsThomsen Diese E-Mail von Linus Torvalds, die mit dieser Antwort verknüpft ist , zeigt an, dass es sich um eine absichtliche Design-Wahl von Git handelt, da sie angeblich viel leistungsfähiger ist als das Verfolgen von Umbenennungen usw.
Emil Lundberg
127
Diese Antwort ist etwas irreführend. Git "erkennt Umbenennungen", aber sehr spät im Spiel; Die Frage ist, wie Sie sicherstellen, dass Git-Tracks umbenannt werden, und jemand, der dies liest, kann leicht darauf schließen, dass Git sie automatisch für Sie erkennt und notiert. Es tut nicht. Git hat keine wirkliche Handhabung von Umbenennungen, und stattdessen gibt es Merge / Log-Tools, die versuchen herauszufinden, was passiert ist - und es selten richtig machen. Linus hat ein falsches, aber vehementes Argument dafür, warum Git es niemals einfach richtig machen und Umbenennungen explizit verfolgen sollte. Also stecken wir hier fest.
Chris Moschini
29
Wichtig: Wenn Sie ein Verzeichnis umbenennen, z. B. beim Umbenennen eines Java-Pakets, müssen Sie zwei Commits ausführen, zum einen für den Befehl 'git mv {old} {new}' und zum anderen für die Aktualisierung aller Java-Dateien, die auf das verweisen Paketverzeichnis geändert. Andernfalls kann git die einzelnen Dateien auch mit dem Parameter --follow nicht verfolgen.
nn4l
44
Obwohl Linus wahrscheinlich nur sehr wenige Fehler macht, scheint dies einer zu sein. Durch einfaches Umbenennen eines Ordners wird ein massives Delta auf GitHub hochgeladen. Das macht mich vorsichtig beim Umbenennen meiner Ordner ... aber das ist eine ziemlich große Zwangsjacke für einen Programmierer. Gelegentlich MUSS ich die Bedeutung von etwas neu definieren oder die Kategorisierung von Dingen ändern. Linus: Mit anderen Worten, ich habe Recht. Ich habe immer Recht, aber manchmal habe ich mehr Recht als zu anderen Zeiten. Und verdammt, wenn ich sage, dass Dateien keine Rolle spielen, habe ich wirklich wirklich Recht ( tm). " ... Ich habe meine Zweifel daran.
Gabe Halsmer
93

Es ist möglich , eine Datei umzubenennen und den Verlauf beizubehalten, obwohl dadurch die Datei im gesamten Verlauf des Repositorys umbenannt wird. Dies ist wahrscheinlich nur für die obsessiven Git-Log-Liebhaber und hat einige schwerwiegende Auswirkungen, einschließlich dieser:

  • Sie könnten einen gemeinsamen Verlauf neu schreiben, was bei der Verwendung von Git am wichtigsten ist. Wenn jemand anderes das Repository geklont hat, brechen Sie es dabei. Sie müssen erneut klonen, um Kopfschmerzen zu vermeiden. Dies mag in Ordnung sein, wenn die Umbenennung wichtig genug ist, aber Sie müssen dies sorgfältig prüfen - Sie könnten eine ganze OpenSource-Community verärgern!
  • Wenn Sie die Datei früher im Repository-Verlauf mit ihrem alten Namen referenziert haben, werden frühere Versionen effektiv beschädigt. Um dies zu beheben, müssen Sie etwas mehr Reifen springen. Es ist nicht unmöglich, nur langweilig und möglicherweise nicht wert.

Jetzt, da Sie noch bei mir sind, sind Sie wahrscheinlich ein Solo-Entwickler, der eine vollständig isolierte Datei umbenennt. Verschieben wir eine Datei mit filter-tree!

Angenommen, Sie verschieben eine Datei oldin einen Ordner dirund geben ihr den Namennew

Dies könnte getan werden git mv old dir/new && git add -u dir/new, aber das bricht die Geschichte.

Stattdessen:

git filter-branch --tree-filter 'if [ -f old ]; then mkdir dir && mv old dir/new; fi' HEAD

wird Redo jeden in dem Zweig commit, für jede Iteration den Befehl in die Zecken ausführt. Wenn Sie dies tun, können viele Dinge schief gehen. Normalerweise teste ich, ob die Datei vorhanden ist (andernfalls ist sie noch nicht zum Verschieben vorhanden), und führe dann die erforderlichen Schritte aus, um den Baum nach meinen Wünschen zu beschlagen. Hier können Sie Dateien durchsuchen, um Verweise auf die Datei usw. zu ändern. Sich selbst umhauen! :) :)

Nach Abschluss wird die Datei verschoben und das Protokoll ist intakt. Du fühlst dich wie ein Ninja-Pirat.

Ebenfalls; Das Verzeichnis mkdir ist natürlich nur erforderlich, wenn Sie die Datei in einen neuen Ordner verschieben. Das if verhindert , dass dieser Ordner früher im Verlauf erstellt wird, als Ihre Datei vorhanden ist.

Øystein Steimler
quelle
57
Als obsessiver Git-Log-Liebhaber würde ich das nicht machen. Die Dateien wurden zu diesen Zeitpunkten nicht so benannt, daher spiegelt die Geschichte eine nie existierende Situation wider. Wer weiß, welche Tests in der Vergangenheit brechen könnten! Das Risiko, frühere Versionen zu beschädigen, lohnt sich in jedem Fall nicht.
Vincent
7
@ Vincent Sie haben absolut Recht, und ich habe versucht, so klar wie möglich zu machen, dass es unwahrscheinlich ist, dass diese Lösung angemessen ist. Ich denke auch, dass wir in diesem Fall über zwei Bedeutungen des Wortes "Geschichte" sprechen. Ich schätze beide.
Øystein Steimler
6
Ich finde, es gibt Situationen, in denen man das brauchen könnte. Angenommen, ich habe in meinem eigenen Zweig etwas entwickelt, das ich jetzt vorgelagert zusammenführen möchte. Aber ich stelle fest, dass der Dateiname nicht angemessen ist, also ändere ich ihn für meinen gesamten persönlichen Zweig. Auf diese Weise kann ich eine saubere, korrekte Geschichte führen und von Anfang an den richtigen Namen haben.
user2291758
3
@ user2291758 das ist mein Anwendungsfall. Diese leistungsstärkeren Git-Befehle sind gefährlich, aber das bedeutet nicht, dass sie keine sehr überzeugenden Anwendungsfälle haben, wenn Sie wissen, was Sie tun!
Philix
1
Wenn möglich, ist die Verwendung von --index-filterfor-Umbenennungen viel schneller, da der Baum nicht bei jedem Commit ausgecheckt und wieder eingefügt werden muss. --index-filterwirkt direkt auf jeden Commit-Index.
Thomas Guyot-Sionnest
87

Nein.

Die kurze Antwort lautet NEIN . Es ist nicht möglich, eine Datei in Git umzubenennen und sich den Verlauf zu merken. Und es ist ein Schmerz.

Gerüchten zufolge wird es git log --follow--find-copies-harderfunktionieren, aber es funktioniert bei mir nicht, selbst wenn der Dateiinhalt nicht geändert wurde und die Verschiebungen mit vorgenommen wurden git mv.

(Anfangs habe ich Eclipse verwendet, um Pakete in einem Vorgang umzubenennen und zu aktualisieren, was Git möglicherweise verwirrt hat. Aber das ist eine sehr häufige Sache. --followScheint zu funktionieren, wenn nur a ausgeführt mvwird und dann a commitund das mvnicht zu weit ist.)

Laut Linus sollten Sie den gesamten Inhalt eines Softwareprojekts ganzheitlich verstehen und nicht einzelne Dateien nachverfolgen müssen. Nun, leider kann mein kleines Gehirn das nicht.

Es ist wirklich ärgerlich, dass so viele Leute die Aussage, dass Git Bewegungen automatisch verfolgt, sinnlos wiederholt haben. Sie haben meine Zeit verschwendet. Git macht so etwas nicht. Mit Absicht (!) Verfolgt Git überhaupt keine Bewegungen.

Meine Lösung besteht darin, die Dateien wieder an ihren ursprünglichen Speicherort umzubenennen. Ändern Sie die Software entsprechend der Quellcodeverwaltung. Mit Git müssen Sie es einfach gleich beim ersten Mal richtig "git".

Leider bricht das Eclipse, das zu verwenden scheint --follow. git log --followManchmal wird nicht der vollständige Verlauf von Dateien mit komplizierten Umbenennungsverläufen angezeigt, obwohl dies der git logFall ist. (Keine Ahnung warum.)

(Es gibt einige zu clevere Hacks, die zurückgehen und alte Arbeiten wieder aufnehmen, aber sie sind ziemlich beängstigend. Siehe GitHub-Gist: emiller / git-mv-with-history .)

Tuntable
quelle
2
Ich glaube du hast recht. Ich habe nur versucht, mit php-cs-fixer die Quelle für mein Laravel 5-Projekt neu zu formatieren, aber es besteht darauf, die Großschreibung der Namespace-Klauseln so zu ändern, dass sie dem Kleinbuchstaben des App-Ordners entspricht. Namespaces (oder Autoloads von Komponisten) funktionieren jedoch nur mit CamelCase. Ich muss die Groß- und Kleinschreibung des Ordners in App ändern, aber dadurch gehen meine Änderungen verloren. Dies ist das trivialste Beispiel, zeigt jedoch, dass die Git-Heuristik nicht einmal den einfachsten Namensänderungen folgen kann (--follow und --find-copy-härter sollten die Regel sein, nicht die Ausnahme).
Zack Morris
6
Git -1, Subversion +1
Cosmin
Ist das noch wahr? Das ist ein weiterer Grund für mich, vorerst bei tfs zu bleiben. In einem großen Projekt ist es ein Muss, den Verlauf einer verschobenen / umbenannten Datei beizubehalten.
Cesar
@Cesar Wenn er mit "Erinnere dich an den Verlauf" "beim Anzeigen des Protokolls den Umbenennungen folgen" meint (was das einzig Nützliche ist, um das wir uns kümmern sollten), dann war dies niemals wahr! Git "zeichnet" keine Umbenennungen auf, aber Tools können sie leicht erkennen und uns Umbenennungen und Verschiebungen präsentieren. Wenn "es nicht funktioniert" für jemanden, sollte er / sie die Werkzeuge ändern, die er / sie verwendet. Es gibt viele wundervolle Git-GUIs , die diese Fähigkeit haben.
Mohammad Dehghan
Die kurze Antwort lautet Ja. Die aktuelle Git-Version unterstützt auch "git log --follow". und ich stimme @MohammadDehghan zu
insung
42
git log --follow [file]

zeigt Ihnen den Verlauf durch Umbenennen.

Erik Hesselink
quelle
29
Es scheint, dass Sie dazu nur die Umbenennung festschreiben müssen, bevor Sie mit dem Ändern der Datei beginnen. Wenn Sie die Datei (in der Shell) verschieben und dann ändern, sind alle Wetten deaktiviert.
Jojo
22
@yoyo: Das liegt daran, dass Git Umbenennungen nicht verfolgt, sondern erkennt. A macht im git mvGrunde a git rm && git add. Es gibt Optionen wie -M90/ --find-renames=90, um eine Datei als umzubenennen zu betrachten, wenn sie zu 90% identisch ist.
vdboor
22

Ich mache:

git mv {old} {new}
git add -u {new}
James M. Greene
quelle
3
Das -u scheint nichts für mich zu tun, soll es den Verlauf aktualisieren?
Jeremy
1
Vielleicht möchten Sie -Astattdessen das Verhalten von ? Siehe auch hier: git-scm.com/docs/git-add
James M. Greene
1
Die Dateien werden zwar hinzugefügt, der Verlauf wird jedoch nicht aktualisiert, sodass der Name der Git-Protokolldatei den vollständigen Verlauf anzeigt. Der vollständige Verlauf wird nur angezeigt, wenn Sie die Option --follow weiterhin verwenden.
Jeremy
3
Ich habe einen komplizierten Refactor durchgeführt, der ein Include-Verzeichnis verschoben hat (mit mv, nicht mit git mv) und dann viele # include-Pfade in den umbenannten Dateien geändert hat. git konnte nicht genug Ähnlichkeit finden, um die Geschichte zu verfolgen. Aber git add -u war genau das, was ich brauchte. Der Git-Status zeigt jetzt "umbenannt" an, wo zuvor "gelöscht" und "neue Datei" angezeigt wurden.
AndyJost
1
Es gibt viele Fragen zu SO, die den Zweck von ansprechen git add -u. Die Git-Dokumente sind in der Regel nicht hilfreich und der letzte Ort, an dem ich nachsehen möchte. Hier ist ein Beitrag git add -uin Aktion: stackoverflow.com/a/2117202 .
Nobar
17

Ich möchte einen Projekt-Teilbaum in Git umbenennen / verschieben, von dem aus er verschoben wird

/project/xyz

zu

/ components / xyz

Wenn ich eine Ebene verwende git mv project components, geht der gesamte Festschreibungsverlauf für das xyzProjekt verloren.

Nein (8 Jahre später, Git 2.19, Q3 2018), da Git die Umbenennung des Verzeichnisses erkennt und dies nun besser dokumentiert ist.

Siehe Commit b00bf1c , Commit 1634688 , Commit 0661e49 , Commit 4d34dff , Commit 983f464 , Commit c840e1a , Commit 9929430 (27. Juni 2018) und Commit d4e8062 , Commit 5dacd4a (25. Juni 2018) von Elijah Newren ( newren) .
(Zusammengeführt von Junio ​​C Hamano - gitster- in Commit 0ce5a69 , 24. Juli 2018)

Das wird jetzt erklärt in Documentation/technical/directory-rename-detection.txt:

Beispiel:

Wenn alle x/a, x/bund x/chaben zu bewegen z/a, z/bund z/cwird es wahrscheinlich , dass x/din der Zwischenzeit hinzugefügt möchte auch bewegen , z/dindem Sie den Hinweis , dass das gesamte Verzeichnis unter 'x ‘ bewegt ‚ z‘.

Aber es gibt viele andere Fälle, wie:

Eine Seite der Geschichte wird umbenannt x -> z eine Datei umbenannt, auf der anderen Seite wird eine Datei umbenannt x/e, sodass für die Zusammenführung eine transitive Umbenennung erforderlich ist.

Um die Erkennung von Verzeichnisumbenennungen zu vereinfachen, werden diese Regeln von Git erzwungen:

Bei der Erkennung der Umbenennung von Verzeichnissen gelten einige grundlegende Regeln:

  1. Wenn ein bestimmtes Verzeichnis auf beiden Seiten einer Zusammenführung noch vorhanden ist, wird es nicht als umbenannt angesehen.
  2. Wenn eine Teilmenge der umzubenennenden Dateien eine Datei oder ein Verzeichnis im Weg hat (oder sich gegenseitig im Weg stehen würde), "deaktivieren" Sie die Umbenennung des Verzeichnisses für diese bestimmten Unterpfade und melden Sie den Konflikt dem Benutzer .
  3. Wenn die andere Seite des Verlaufs ein Verzeichnis in einen Pfad umbenannt hat, den Ihre Seite des Verlaufs umbenannt hat, ignorieren Sie diese bestimmte Umbenennung von der anderen Seite des Verlaufs für implizite Umbenennungen des Verzeichnisses (warnen Sie den Benutzer jedoch).

Sie können viele Tests in sehen t/t6043-merge-rename-directories.sh, die auch darauf hinweisen, dass:

  • a) Wenn Umbenennungen ein Verzeichnis in zwei oder mehr andere aufteilen, gewinnt das Verzeichnis mit den meisten Umbenennungen.
  • b) Vermeiden Sie die Erkennung von Verzeichnisumbenennungen für einen Pfad, wenn dieser Pfad die Quelle einer Umbenennung auf beiden Seiten einer Zusammenführung ist.
  • c) Wenden Sie implizite Verzeichnisumbenennungen nur auf Verzeichnisse an, wenn die andere Seite des Verlaufs die Umbenennung durchführt.
VonC
quelle
15

Zielsetzung

  • Verwendung (inspiriert von Smar , entlehnt von Exherbo )git am
  • Fügen Sie den Commit-Verlauf kopierter / verschobener Dateien hinzu
  • Von einem Verzeichnis zum anderen
  • Oder von einem Repository zum anderen

Verjährung

  • Tags und Zweige werden nicht beibehalten
  • Der Verlauf wird beim Umbenennen der Pfaddatei abgeschnitten (Verzeichnis umbenennen).

Zusammenfassung

  1. Extrahieren Sie den Verlauf im E-Mail-Format mit
    git log --pretty=email -p --reverse --full-index --binary
  2. Ordnen Sie den Dateibaum neu an und aktualisieren Sie die Dateinamen
  3. Fügen Sie einen neuen Verlauf mit hinzu
    cat extracted-history | git am --committer-date-is-author-date

1. Extrahieren Sie den Verlauf im E-Mail-Format

Beispiel: Extrahieren Sie den Verlauf von file3, file4undfile5

my_repo
├── dirA
│   ├── file1
│   └── file2
├── dirB            ^
│   ├── subdir      | To be moved
│   │   ├── file3   | with history
│   │   └── file4   | 
│   └── file5       v
└── dirC
    ├── file6
    └── file7

Ziel festlegen / reinigen

export historydir=/tmp/mail/dir       # Absolute path
rm -rf "$historydir"    # Caution when cleaning the folder

Extrahieren Sie den Verlauf jeder Datei im E-Mail-Format

cd my_repo/dirB
find -name .git -prune -o -type d -o -exec bash -c 'mkdir -p "$historydir/${0%/*}" && git log --pretty=email -p --stat --reverse --full-index --binary -- "$0" > "$historydir/$0"' {} ';'

Leider Option --followoder --find-copies-harderkann nicht mit kombiniert werden --reverse. Aus diesem Grund wird der Verlauf beim Umbenennen der Datei (oder beim Umbenennen eines übergeordneten Verzeichnisses) abgeschnitten.

Temporärer Verlauf im E-Mail-Format:

/tmp/mail/dir
    ├── subdir
    │   ├── file3
    │   └── file4
    └── file5

Dan Bonachea schlägt vor, die Schleifen des Befehls zur Generierung des Git-Protokolls in diesem ersten Schritt umzukehren : Anstatt das Git-Protokoll einmal pro Datei auszuführen, führen Sie es genau einmal mit einer Liste von Dateien in der Befehlszeile aus und generieren Sie ein einzelnes einheitliches Protokoll. Auf diese Weise bleiben Commits, die mehrere Dateien ändern, ein einziges Commit im Ergebnis, und alle neuen Commits behalten ihre ursprüngliche relative Reihenfolge bei. Beachten Sie, dass dies auch Änderungen im zweiten Schritt unten erfordert, wenn Dateinamen im (jetzt einheitlichen) Protokoll neu geschrieben werden.


2. Ordnen Sie den Dateibaum neu an und aktualisieren Sie die Dateinamen

Angenommen, Sie möchten diese drei Dateien in diesem anderen Repo verschieben (kann dasselbe Repo sein).

my_other_repo
├── dirF
│   ├── file55
│   └── file56
├── dirB              # New tree
│   ├── dirB1         # from subdir
│   │   ├── file33    # from file3
│   │   └── file44    # from file4
│   └── dirB2         # new dir
│        └── file5    # from file5
└── dirH
    └── file77

Organisieren Sie daher Ihre Dateien neu:

cd /tmp/mail/dir
mkdir -p dirB/dirB1
mv subdir/file3 dirB/dirB1/file33
mv subdir/file4 dirB/dirB1/file44
mkdir -p dirB/dirB2
mv file5 dirB/dirB2

Ihre vorübergehende Geschichte ist jetzt:

/tmp/mail/dir
    └── dirB
        ├── dirB1
        │   ├── file33
        │   └── file44
        └── dirB2
             └── file5

Ändern Sie auch Dateinamen innerhalb des Verlaufs:

cd "$historydir"
find * -type f -exec bash -c 'sed "/^diff --git a\|^--- a\|^+++ b/s:\( [ab]\)/[^ ]*:\1/$0:g" -i "$0"' {} ';'

3. Wenden Sie einen neuen Verlauf an

Dein anderes Repo ist:

my_other_repo
├── dirF
│   ├── file55
│   └── file56
└── dirH
    └── file77

Übernehmen Sie Commits aus temporären Verlaufsdateien:

cd my_other_repo
find "$historydir" -type f -exec cat {} + | git am --committer-date-is-author-date

--committer-date-is-author-datebehält die ursprünglichen Festschreibungszeitstempel bei (Kommentar von Dan Bonachea ).

Dein anderes Repo ist jetzt:

my_other_repo
├── dirF
│   ├── file55
│   └── file56
├── dirB
│   ├── dirB1
│   │   ├── file33
│   │   └── file44
│   └── dirB2
│        └── file5
└── dirH
    └── file77

Verwenden Sie git statusdiese Option, um die Anzahl der Commits anzuzeigen, die zum Pushing bereit sind :-)


Zusätzlicher Trick: Überprüfen Sie umbenannte / verschobene Dateien in Ihrem Repo

So listen Sie die umbenannten Dateien auf:

find -name .git -prune -o -exec git log --pretty=tformat:'' --numstat --follow {} ';' | grep '=>'

Weitere Anpassungen: Sie können den Befehl git logmit den Optionen --find-copies-harderoder ausführen --reverse. Sie können die ersten beiden Spalten auch mit cut -f3-dem vollständigen Muster '{. * =>. *}' Entfernen und erfassen.

find -name .git -prune -o -exec git log --pretty=tformat:'' --numstat --follow --find-copies-harder --reverse {} ';' | cut -f3- | grep '{.* => .*}'
olibre
quelle
4
ACHTUNG: Bei dieser Technik werden Commits, die zwei oder mehr Dateien ändern, in separate fragmentierte Commits aufgeteilt und ihre Reihenfolge durch Sortieren nach Dateinamen verschlüsselt (sodass die Fragmente eines ursprünglichen Commits im linearen Verlauf nicht nebeneinander angezeigt werden). Der resultierende Verlauf ist daher nur Datei für Datei "korrekt". Wenn Sie mehr als eine Datei verschieben, stellt KEINE der neuen Festschreibungen im resultierenden Verlauf eine konsistente Momentaufnahme der verschobenen Dateien dar, die jemals im Verlauf des ursprünglichen Repos vorhanden waren.
Dan Bonachea
2
Hallo @DanBonachea. Vielen Dank für Ihr interessantes Feedback. Ich habe einige Repos mit mehreren Dateien mit dieser Technik erfolgreich migriert (auch bei umbenannten Dateien und Dateien, die über Verzeichnisse verschoben wurden). Was schlagen Sie vor, um diese Antwort zu ändern? Denken Sie, wir sollten oben in dieser Antwort ein WARNING-Banner einfügen, das die Einschränkungen dieser Technik erklärt? Prost
olibre
2
Ich habe diese Technik angepasst, um das Problem zu vermeiden, indem ich die Schleifen des Befehls zur Erzeugung des Git-Protokolls in Schritt 1 invertiert habe. Anstatt das Git-Protokoll einmal pro Datei auszuführen, führen Sie es genau einmal mit einer Liste von Dateien in der Befehlszeile aus und generieren Sie ein einzelnes einheitliches Protokoll. Auf diese Weise bleiben Commits, die zwei oder mehr Dateien ändern, ein einziges Commit im Ergebnis, und alle neuen Commits behalten ihre ursprüngliche relative Reihenfolge bei. Beachten Sie, dass dies auch Änderungen in Schritt 2 erfordert, wenn Dateinamen im (jetzt einheitlichen) Protokoll neu geschrieben werden. Ich habe auch git am --committer-date-is-author-date verwendet, um die ursprünglichen Commit-Zeitstempel beizubehalten.
Dan Bonachea
1
Vielen Dank für Ihr Experimentieren und Teilen. Ich habe die Antwort für andere Leser etwas aktualisiert. Ich habe mir jedoch Zeit genommen, um Ihre Verarbeitung zu testen. Sie können diese Antwort jederzeit bearbeiten, wenn Sie Beispiele für Befehlszeilen bereitstellen möchten. Prost;)
Olibre
4

Ich folgte diesem mehrstufigen Prozess, um Code in das übergeordnete Verzeichnis zu verschieben und den Verlauf beizubehalten.

Schritt 0: Erstellt einen Zweig 'Verlauf' von 'Master' zur sicheren Aufbewahrung

Schritt 1: Verwendet das Git-Filter-Repo- Tool, um den Verlauf neu zu schreiben. Dieser Befehl unten hat den Ordner 'FolderwithContentOfInterest' auf eine Ebene nach oben verschoben und den entsprechenden Commit-Verlauf geändert

git filter-repo --path-rename ParentFolder/FolderwithContentOfInterest/:FolderwithContentOfInterest/ --force

Schritt 2: Zu diesem Zeitpunkt hat das GitHub-Repository seinen Remote-Repository-Pfad verloren. Remote-Referenz hinzugefügt

git remote add origin [email protected]:MyCompany/MyRepo.git

Schritt 3: Informationen zum Repository abrufen

git pull

Schritt 4: Verbinden Sie den lokalen verlorenen Zweig mit dem Ursprungszweig

git branch --set-upstream-to=origin/history history

Schritt 5: Adresszusammenführungskonflikt für die Ordnerstruktur, wenn Sie dazu aufgefordert werden

Schritt 6: Drücken Sie !!

git push

Hinweis: Der geänderte Verlauf und der verschobene Ordner scheinen bereits festgeschrieben zu sein. enter code here

Erledigt. Der Code wird in das übergeordnete / gewünschte Verzeichnis verschoben, wobei der Verlauf erhalten bleibt!

Parag Bangad
quelle
2

Während der Kern von Git, die Git-Installation, die Umbenennungen nicht verfolgt, kann der Verlauf, den Sie mit dem Git-Protokoll "Porzellan" anzeigen, diese erkennen, wenn Sie möchten.

git logVerwenden Sie für eine bestimmte Option die Option -M:

Git Log -p -M

Mit einer aktuellen Version von Git.

Dies funktioniert für andere Befehle wie git diff .

Es gibt Optionen, um die Vergleiche mehr oder weniger streng durchzuführen. Wenn Sie eine Datei umbenennen, ohne gleichzeitig wesentliche Änderungen an der Datei vorzunehmen, können Git-Protokoll und Freunde die Umbenennung leichter erkennen. Aus diesem Grund benennen einige Benutzer Dateien in einem Commit um und ändern sie in einem anderen.

Die CPU-Nutzung ist mit Kosten verbunden, wenn Sie Git fragen, wo Dateien umbenannt wurden. Ob Sie sie verwenden oder nicht und wann, liegt bei Ihnen.

Wenn Sie möchten, dass Ihr Verlauf immer mit Umbenennungserkennung in einem bestimmten Repository gemeldet wird, können Sie Folgendes verwenden:

git config diff.renames 1

Dateien, die von einem Verzeichnis in ein anderes verschoben werden, werden erkannt. Hier ist ein Beispiel:

commit c3ee8dfb01e357eba1ab18003be1490a46325992
Author: John S. Gruber <[email protected]>
Date:   Wed Feb 22 22:20:19 2017 -0500

    test rename again

diff --git a/yyy/power.py b/zzz/power.py
similarity index 100%
rename from yyy/power.py
rename to zzz/power.py

commit ae181377154eca800832087500c258a20c95d1c3
Author: John S. Gruber <[email protected]>
Date:   Wed Feb 22 22:19:17 2017 -0500

    rename test

diff --git a/power.py b/yyy/power.py
similarity index 100%
rename from power.py
rename to yyy/power.py

Bitte beachten Sie, dass dies immer dann funktioniert, wenn Sie diff verwenden, nicht nur mit git log. Zum Beispiel:

$ git diff HEAD c3ee8df
diff --git a/power.py b/zzz/power.py
similarity index 100%
rename from power.py
rename to zzz/power.py

Als Test habe ich eine kleine Änderung an einer Datei in einem Feature-Zweig vorgenommen und diese festgeschrieben. Im Master-Zweig habe ich die Datei umbenannt, festgeschrieben und dann eine kleine Änderung an einem anderen Teil der Datei vorgenommen und diese festgeschrieben. Als ich zum Feature-Zweig ging und vom Master zusammengeführt wurde, benannte die Zusammenführung die Datei um und führte die Änderungen zusammen. Hier ist die Ausgabe der Zusammenführung:

 $ git merge -v master
 Auto-merging single
 Merge made by the 'recursive' strategy.
  one => single | 4 ++++
  1 file changed, 4 insertions(+)
  rename one => single (67%)

Das Ergebnis war ein Arbeitsverzeichnis, in dem die Datei umbenannt und beide Textänderungen vorgenommen wurden. So ist es Git möglich, das Richtige zu tun, obwohl Umbenennungen nicht explizit verfolgt werden.

Dies ist eine späte Antwort auf eine alte Frage, sodass die anderen Antworten zu diesem Zeitpunkt möglicherweise für die Git-Version korrekt waren.

John S. Gruber
quelle
1

Erstellen Sie zunächst ein eigenständiges Commit mit nur einer Umbenennung.

Eventuelle Änderungen am Dateiinhalt werden dann in das separate Commit übernommen.

Jakub Pawlowski
quelle
1

So benennen Sie ein Verzeichnis oder eine Datei um (ich weiß nicht viel über komplexe Fälle, daher kann es einige Einschränkungen geben):

git filter-repo --path-rename OLD_NAME:NEW_NAME

So benennen Sie ein Verzeichnis in Dateien um, in denen es erwähnt wird (es ist möglich, Rückrufe zu verwenden, aber ich weiß nicht wie):

git filter-repo --replace-text expressions.txt

expressions.txtist eine Datei, die mit Zeilen wie gefüllt ist literal:OLD_NAME==>NEW_NAME(es ist möglich, Pythons RE mit regex:oder glob mit zu verwenden glob:).

So benennen Sie ein Verzeichnis in Commit-Nachrichten um:

git-filter-repo --message-callback 'return message.replace(b"OLD_NAME", b"NEW_NAME")'

Die regulären Ausdrücke von Python werden ebenfalls unterstützt, müssen jedoch manuell in Python geschrieben werden.

Wenn das Repository original ist, ohne Remote, müssen Sie hinzufügen --force , um ein Umschreiben zu erzwingen. (Möglicherweise möchten Sie zuvor eine Sicherungskopie Ihres Repositorys erstellen.)

Wenn Sie keine Refs beibehalten möchten (diese werden im Zweigverlauf der Git-GUI angezeigt), müssen Sie hinzufügen --replace-refs delete-no-add.

Pugsley
quelle
0

Verschieben Sie einfach die Datei und inszenieren Sie mit:

git add .

Vor dem Festschreiben können Sie den Status überprüfen:

git status

Das wird zeigen:

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        renamed:    old-folder/file.txt -> new-folder/file.txt

Ich habe mit Git Version 2.26.1 getestet.

Aus der GitHub- Hilfeseite extrahiert .

Junior
quelle
-3

Ich mache das Verschieben der Dateien und mache es dann

git add -A

die in den Sättigungsbereich alle gelöschten / neuen Dateien legen. Hier erkennt git, dass die Datei verschoben wird.

git commit -m "my message"
git push

Ich weiß nicht warum, aber das funktioniert bei mir.

Xelian
quelle
Der Trick dabei ist, dass Sie keinen einzelnen Buchstaben ändern müssen, auch wenn Sie etwas ändern und Strg + Z drücken, wird der Verlauf unterbrochen. Wenn Sie also etwas schreiben, setzen Sie die Datei zurück, verschieben Sie sie erneut und führen Sie ein einzelnes Add-> Commit dafür durch.
Xelian