Wie rebasiert man nach dem Hinzufügen von Git-Subtree?
Lesezeit: 11 Minuten
Epeli
Ich versuche, das Neue zu lernen git-Unterbaum Befehl, der in Git 1.7.11 hinzugefügt wurde. Ich scheine die Fähigkeit zum Rebase zu verlieren, nachdem ich einen Teilbaum hinzugefügt habe. Ich habe das primäre Repository mit README-Datei und ein Bibliotheks-Repository, das auch eine README-Datei enthält. Ich füge es dem lib-Verzeichnis mit hinzu subtree add:
$ git subtree add -P lib/mylib myliborigin master
Das funktioniert gut, aber jetzt sieht der Verlauf so aus:
Jetzt, wo ich mein Repo gegen rebasen möchte origin/master und es schlägt fehl, weil das Squash-Commit direkt auf sein übergeordnetes Commit angewendet wird, was nicht zutrifft, da es auf das Stammverzeichnis des Repos angewendet wird und nicht auf das Präfix, das ich ihm beim Hinzufügen des Unterbaums gegeben habe.
Der Grund dafür ist ziemlich klar, wenn ich mir den Squash-Commit anschaue. Es gibt keine Informationen über das Präfix. Es sind nur die ursprünglichen mylib-Commits, die zusammengedrückt wurden. Nur der nächste Merge-Commit weiß etwas davon, aber rebase berücksichtigt es hier nicht.
Gibt es Problemumgehungen (abgesehen davon, niemals über die Subtree-Commits zu rebasen)?
Hast du es versucht git rebase --preserve-merges ?
– weynhamz
12. Oktober 2012 um 15:13 Uhr
@TechliveZheng Das hat perfekt funktioniert, danke! Lesen Sie hier mehr
– helmesjo
11. Juli 2017 um 8:33 Uhr
Kevin Cooper
Das funktioniert in einfachen Fällen:
git rebase --preserve-merges master
Danke an @Techlive Zheng in den Kommentaren.
Sie können sehen
fatal: refusing to merge unrelated histories
Error redoing merge a95986e...
Was bedeutet, dass Git Ihren Teilbaum nicht automatisch anwenden konnte. Dies bringt Sie in die Situation, die @ericpeters in seiner Antwort beschrieben hat. Lösung:
Fügen Sie Ihren Teilbaum erneut hinzu (verwenden Sie denselben Befehl, den Sie ursprünglich verwendet haben):
git subtree add -P lib lib-origin master
Rebase fortsetzen:
git rebase --continue
Und du bist fertig!
Wenn Sie sich fragen, ob es erfolgreich funktioniert hat, können Sie nach dem Rebasing mit Ihrer ursprünglichen Version vergleichen, um sicherzustellen, dass Sie nichts geändert haben:
(das -- . am ende weist git nur noch an diff die Dateien in Ihrem aktuellen Verzeichnis).
Wenn alles andere fehlschlägt und Sie sich nicht darum kümmern, die Commits selbst zu erhalten, können Sie einfach git cherry-pick das ursprüngliche Teilbaum-Commit.
Die Commit-Nachricht sieht in etwa so aus Add 'lib/' from commit '9767e6...' – das ist es, was du willst.
Wenn Sie “erneut hinzufügen” sagen, bedeutet dies, dass Sie zuerst auch entfernen müssen? Ich bekomme ein prefix already exists Nachricht, wenn Sie die oben genannten Schritte ausführen git subtree add Befehl.
– Mike-E
27. Mai 2018 um 4:33 Uhr
@Mike-EEE in der beschriebenen Situation glaube ich, dass git es nicht automatisch hinzugefügt hat, also meine ich mit “erneut hinzufügen”, es selbst manuell hinzuzufügen. Es sollte zu diesem Zeitpunkt aufgrund der Rebase nicht vorhanden sein.
– Kevin Cooper
27. Mai 2018 um 18:13 Uhr
Danke für den Kontext und die Antwort, Kevin. Ich habe den Tag damit verbracht, mich von diesem Thema und meiner eigenen persönlichen Notlage, die von einer scheinbar harmlosen Sache herrührt, täuschen zu lassen git subtree pull auf einen Remote-Commit, das aus einer einzelnen Dateiänderung besteht das führt zu Dutzenden von Konflikten und Fehlern im übergeordneten Repo/Zweig, der sich nur von besagtem Commit unterscheidet. Hier konnte ich jedenfalls fündig werden: git-memo.readthedocs.io/en/latest/subtree.html Vielleicht hilft es jemand anderem.
– Mike-E
27. Mai 2018 um 20:45 Uhr
Für den relativ einfachen Fall “Ich habe vergessen, einige Änderungen vorzunehmen, bevor ich den Unterbaum hinzufüge, und möchte eine neue Basis erstellen”, funktioniert dies genau wie erwartet – danke!
– zwei
16. Juni 2018 um 11:39 Uhr
Etwas spät zur Party, aber diese Lösung hat perfekt funktioniert, danke Kevin! Ich habe auch eine prefix already exists Nachricht und es lag daran, dass sie eine generierte Datei (*.pyc) enthielt, die nicht versioniert ist und das Entfernen des Ordners verhinderte. Löschen Sie den Ordner vorher manuell git subtree add ... hat es geschafft. Noch ein Hinweis, falls Ihr Original git subtree add ... Befehl hatte a --squashdenken Sie daran, es auch hier einzufügen, damit die rebasierte Version genau gleich aussieht.
– Jimmy He
14. Dezember 2020 um 7:28 Uhr
Skiminok
Dies ist eine alte Frage, aber ich hatte gerade das gleiche Problem mit meinem Repo und habe endlich eine vollständige Lösung gefunden, die (hoffentlich) alle Subtree-Metadaten beibehält.
Angenommen, wir hätten diesen Commit-Baum:
B (master) Add README.md
|
A Initial commit
und wir gabelten a feature Zweig, in dem sich ein Teilbaum befindet lib/:
Es erstellt ein Merge-Commit D mit zwei Eltern: unserem aktuellen master (B) und einem nicht verwandten Commit F mit der gequetschten Geschichte des externen Repos. Dieser Commit enthält auch einige git subtree Metadaten in seiner Commit-Nachricht (nämlich git-subtree-dir und git-subtree-split).
D (feature) Merged commit 'F' as 'lib/'
/ \
/ F Squashed 'lib/' content from GGGGGG
B (master) Add README.md
|
A Initial commit
Später fügen wir beiden Zweigen unabhängig voneinander einige Commits hinzu.
E (feature) Remove .gitignore from lib/
C | (master) Add LICENSE.md
| D Merged commit 'F' as 'lib/'
| / \
|/ F Squashed 'lib/' content from GGGGGG
B Add README.md
|
A Initial commit
Jetzt wollen wir umrüsten feature auf zu master. Hier ist wie:
1. Wählen Sie die Commits aus feature nacheinander, um eine neue Kopie der zu erstellen feature Zweig oben auf master.
git branch -f feature C
git checkout feature
git cherry-pick D E
E' (feature) Remove .gitignore from lib/
|
D' Merged commit 'F' as 'lib/'
|
| E Remove .gitignore from lib/
C | (master) Add LICENSE.md
| D Merged commit 'F' as 'lib/'
| / \
|/ F Squashed 'lib/' content from GGGGGG
B Add README.md
|
A Initial commit
Jetzt haben wir das Äquivalent einer Rebase, aber wir haben alle Informationen über das externe Repo verloren, die für erforderlich sind git subtree. Um es wiederherzustellen:
2. Fügen Sie den fehlenden übergeordneten Link als Graft hinzu und schreiben Sie den Verlauf von neu feature um es dauerhaft zu machen.
git checkout feature
git replace --graft D' C F
git filter-branch --tag-name-filter cat -- master..
Und jetzt erhalten wir ein Bild, das genau dem entspricht, mit dem wir begonnen haben. Die alten Commits D und E sind immer noch da draußen, aber sie können später aussortiert werden.
E' (feature) Remove .gitignore from lib/
|
D' Merged commit 'F' as 'lib/'
|\
| \
C \ (master) Add LICENSE.md
| \
| \
| F Squashed 'lib/' content from GGGGGG
B Add README.md
|
A Initial commit
Warnung: Damit wird die Geschichte neu geschrieben feature, seien Sie also vorsichtig mit der Veröffentlichung, wenn jemand anderes mit Ihnen in diesem Zweig zusammenarbeitet. Da du aber in erster Linie eine Rebase machen wolltest, ist dir das wahrscheinlich bewusst 🙂
Dies ist keine Lösung, aber es ist die aktuelle Arbeit, die ich verwende …
Rebasieren Sie interaktiv auf den 2. Commit, bevor Sie den Teilbaum hinzufügen:
$ git rebase -i 020e372
Löschen Sie die beiden Unterbaumeinträge und markieren Sie die Bearbeitung für den vorherigen Commit:
e b99d55b Add readme
Datei speichern/schließen, dann, wenn es zum Commit „Readme hinzufügen“ kommt, den Änderungsbefehl ausführen:
$ git commit --amend
Fügen Sie dann Ihren neuen Teilbaum erneut hinzu:
$ git subtree add -P lib/mylib myliborigin master
Rebase fortsetzen:
$ git rebase --continue
Ihr Zweig sollte dann vom Master umbasiert werden, und der Teilbaum wird “normal” sein, wobei Squash + the Merge intakt sind:
* 22c1fe6 (HEAD, master) Merge commit 'b6e698d9f4985825efa06dfdd7bba8d2930cd40e' as 'lib/mylib' -
|\
| * b6e698d Squashed 'lib/mylib/' content from commit d7dbd3d
Was ist, wenn ich das Szenario habe, in dem ich spätere Commits, die an das Unterprojekt vorgenommen wurden und in der Historie des Superprojekts angezeigt werden, quetschen möchte? Zum Beispiel: commit 597df58 (HEAD, master) Ein Commit für das Superprojekt Commit 9cd82bd Ein Commit für das Unterprojekt Commit 059ffbe Ein Commit für das Unterprojekt Commit af5209e Merge Commit „da151a…“ als „Subproject“ Commit da151a3 Gestauchter „Subproject/“-Inhalt aus Commit 4d455fb commit b10f7c9 initial commit to superproject Ich möchte 9cd82bd 059ffbe mit af5209e quetschen, da der Verlauf des Unterprojekts bereits mit git subtree push in sein eigenes Repo verschoben wurde …
– Kuhbert
25. September 2014 um 5:22 Uhr
Ich bin mir nicht sicher, ich denke, ich müsste einen besseren Blick darauf werfen, wie das Projektlayout aussah, stellen Sie sicher, dass die Terminologie konsistent bleibt, es gibt Unterbäume und Untermodule, aber kein Unterprojekt 🙂
– Ericpeter
25. September 2014 um 22:09 Uhr
Nun, das Teilprojekt ist nur das Ergebnis von subtree add –prefix=subproject repoB branch
– Kuhbert
26. September 2014 um 3:28 Uhr
Hoffentlich haben Sie das andere Repository nicht zerstört, weil Sie dachten, später wäre alles in Ordnung 🙁
– Anthony Mastréan
25. September 2015 um 15:52 Uhr
Das funktioniert sicherlich, aber es ist ein bisschen hacky. Siehe meine Antwort unten für eine einfachere Methode, um das gleiche Ergebnis zu erzielen.
– Kevin Cooper
17. Januar 2018 um 4:45 Uhr
nyanpasu64
Git 2.24.0 (veröffentlicht am 04.11.2019) hat Unterstützung für hinzugefügt git rebase --rebase-merges --strategy [strategy]. Also wenn du jetzt rennst git rebase --rebase-merges --strategy subtree [branch] Wenn Ihr aktueller Zweig eine Teilbaumzusammenführung enthält, funktioniert es jetzt einfach.
Für mein Projekt habe ich beschlossen, dass ich es möglicherweise nicht verwende git subtree addsondern werfen Sie stattdessen den zweiten Elternteil des Merge-Commits mit weg git replace --edit. habe ich auch benutzt das veraltete „subtree“-Tutorial des Git-Buchs v1was dasselbe tut, aber langweilig ist.
Nicht, dass dies für irgendjemanden eine große Hilfe wäre. Ich würde auch gerne einen Workaround dafür finden.
Ich bin wirklich der Meinung, dass dieses Anti-Feature deutlicher beworben werden muss. Das Verhalten ist völlig verwirrend und jeder sollte wissen, dass es in Unterbäume eindringt, aber die Dokumentation hebt es nicht hervor. Ich habe eine Weile gebraucht, um diesen Stackoverflow-Artikel zu finden.
Ich hatte ein ähnliches Problem: Ich wollte nach dem Hinzufügen eines Unterbaums eine neue Basis erstellen, und die Verwendung von –preserve-merges hinterließ immer noch einen Zusammenführungskonflikt (aufgrund von Konflikten .gitignore Dateien und andere).
In meinem Fall hatte ich nicht unbedingt vor, eine Teilbaumfunktionalität zu verwenden: Ich habe einfach ein Repo eingefügt, das ursprünglich Teil des Superprojekts hätte sein sollen. Falls es jemand anderem hilft, habe ich Folgendes getan, basierend auf anderen verwandten Antworten, die ich gefunden habe.
Angenommen, ich habe zwei Projekte, main_project und sub_project, im selben Verzeichnis. Ich möchte sub_project in ein Verzeichnis namens sub_project innerhalb von main_project ziehen, vorausgesetzt, dass keines der Repos jemals ein Verzeichnis namens sub_project hatte:
Ich werde aktualisieren, wenn ich Probleme mit diesem Ansatz finde.
Ich bin wirklich der Meinung, dass dieses Anti-Feature deutlicher beworben werden muss. Das Verhalten ist völlig verwirrend und jeder sollte wissen, dass es in Unterbäume eindringt, aber die Dokumentation hebt es nicht hervor. Ich habe eine Weile gebraucht, um diesen Stackoverflow-Artikel zu finden.
Hast du es versucht
git rebase --preserve-merges
?– weynhamz
12. Oktober 2012 um 15:13 Uhr
@TechliveZheng Das hat perfekt funktioniert, danke! Lesen Sie hier mehr
– helmesjo
11. Juli 2017 um 8:33 Uhr