Wie rebasiert man nach dem Hinzufügen von Git-Subtree?

Lesezeit: 11 Minuten

Benutzer-Avatar
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:

*   22c1fe6 (HEAD, master) Merge commit 'b6e698d9f4985825efa06dfdd7bba8d2930cd40e' as 'lib/mylib' - 
|\                                                                                                                
| * b6e698d Squashed 'lib/mylib/' content from commit d7dbd3d
* b99d55b Add readme
* 020e372 Initial

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

Benutzer-Avatar
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:

git diff <ref-before-rebase> <ref-after-rebase> -- .

(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

Benutzer-Avatar
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/:

git remote add -f githublib https://github.com/lib/lib.git
git subtree add --prefix lib/ githublib master --squash

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 …

Anhand Ihres Ausgangsbeispiels:

*   22c1fe6 (HEAD, master) Merge commit 'b6e698d9f4985825efa06dfdd7bba8d2930cd40e' as 'lib/mylib' - 
|\                                                                                                                
| * b6e698d Squashed 'lib/mylib/' content from commit d7dbd3d
* b99d55b Add readme
* 020e372 Initial

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

Benutzer-Avatar
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.

Anscheinend ist dies erwartetes Verhalten (für eine perverse Definition von “erwartetes Verhalten”). Siehe: http://git.661346.n2.nabble.com/subtree-merges-lose-prefix-after-rebase-td7332850.html.

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.

    – Tim Harper

    31. März um 22:18 Uhr


  • Der obige Link ist jetzt defekt. Ich glaube marc.info/?l=git&m=133062667420408 ist identisch mit dem was verlinkt wurde.

    – nhooyr

    25. Mai um 19:18 Uhr


Benutzer-Avatar
Gemeinschaft

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:

cd main_project
git fetch ../sub_project
git checkout -b sub_project FETCH_HEAD
git filter-branch --prune-empty --tree-filter '
    if [[ ! -e sub_project ]]; then
        mkdir -p sub_project
        git ls-tree --name-only $GIT_COMMIT | xargs -I files mv files sub_project
    fi'
git checkout branch-to-merge-within
git merge sub_project
git branch -d sub_project

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.

    – Tim Harper

    31. März um 22:18 Uhr


  • Der obige Link ist jetzt defekt. Ich glaube marc.info/?l=git&m=133062667420408 ist identisch mit dem was verlinkt wurde.

    – nhooyr

    25. Mai um 19:18 Uhr


Sie müssen verwenden

git rebase --preserve-merges --preserve-committer --onto new_place start end

  • Das hat nicht geholfen. Ich verliere immer noch das Präfix.

    – Vinnie Falco

    13. Mai 2014 um 19:14 Uhr

  • Was sind typische Werte für new_place, start und end wenn ich einen Feature-Branch rebasen möchte master

    – Bernhard Döbler

    18. Mai 2017 um 20:54 Uhr

1312130cookie-checkWie rebasiert man nach dem Hinzufügen von Git-Subtree?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy