Wie man nachfolgende Merge-Commits „squasht“.

Lesezeit: 7 Minuten

In meinem Projekt habe ich eine Geschichte wie diese:

    b --- c --- g
  /        \     \ 
a           f --- h --- j
  \        /           /
    d --- e --- i ----

Ich möchte Zwischen-Merge-Commits entfernen und nur einen Merge-Commit mit demselben Inhalt haben wie:

    b --- c --- g
  /              \ 
a                 j'
  \              /
    d --- e --- i 

Ein Ansatz wäre, alles von Grund auf neu zu machen, aber f hatte ca. 600 widersprüchliche Dateien, g sind tatsächlich wieder 200 neue Commits, und ich möchte keine Woche mehr damit verbringen, Konflikte zu lösen. Wie erreiche ich das?

  • Ich vermute etwas ein XY-Problem. Könnte nützlich sein, es auszuschließen, also haben Sie Geduld mit mir. Wenn der Endzustand beider Situationen gleich ist, können wir wohl sagen, dass es sich nicht um ein funktionales Problem handelt, das Sie haben. Warum wollen/müssen Sie eine Geschichte haben, die das tut? nicht widerspiegeln, was passiert ist? Es ist im Allgemeinen nützlicher, verlässliche Informationen zu haben als eine hübsche, aber falsche Geschichte. Ihr spezifischer Kontext könnte dies alles erklären, ich frage nur, um Ihre Frage besser zu verstehen.

    – Romain Waleri

    11. November 2019 um 14:11 Uhr


  • @RomainValeri Ihr Verdacht ist richtig, das Problem ist hauptsächlich ästhetisch. Eine funktionale Sache, mit der ich mein Ziel begründen kann, ist, dass ich (möglicherweise) Fehler gemacht habe f oder h die ich später herausfinden werde, und ich werde diese nicht ändern können, aber ich könnte sie ändern j' bevor ich dem Team meinen großen Merge vorstelle.

    – AB

    11. November 2019 um 14:21 Uhr


  • In was wollen Sie die Commits quetschen f und h? Wie willst du f verpflichtet sich, dabei zu sein joder Sie möchten, dass sie dupliziert werden c und e, oder nicht verwendet? Ich möchte einen Vorschlag machen, Optionen für die Zusammenführungsstrategie zu verwenden, da es so klingt, als hätten Sie die gewünschten Dateien und möchten nur Konflikte vermeiden.

    – leorleor

    11. November 2019 um 14:43 Uhr


  • @leorleor Ich möchte die drei Merge-Commits zerquetschen: f, h und j zusammen zu einem neuen Commit j'. Die Konflikte wurden gelöst, als sie erstellt wurden, und jetzt befindet sich mein Repo in einem Zustand, den ich möchte j' zu sein. Ich will und kann den Verlauf von nicht ändern g oder i. Alles, was ich mir wünsche, ist, diese komplizierte Geschichte in meinem Zweig loszuwerden.

    – AB

    11. November 2019 um 14:52 Uhr

  • @AB Ihre Sorge um Fehler in f oder h macht dies nicht zu einem funktionalen Problem, da Sie entweder (1) die Änderungen zu einem neuen Commit hinzufügen (was Sie tun sollten) oder (2) ändern könnten j so wie du es ändern könntest j'. “Die Tatsache zu verbergen, dass es jemals Fehler gab” ist nicht wirklich ein zwingender Grund, den Verlauf zu bearbeiten

    – Markus Adelsberger

    11. November 2019 um 14:58 Uhr

Benutzer-Avatar
Markus Adelsberger

Ich könnte den Vorgang vielleicht etwas klarer erklären, wenn ich die Verzweigungsstruktur kennen würde, aber im Allgemeinen würden Sie Folgendes tun:

Sie müssen wirklich nur eine neue Zusammenführung zwischen erstellen g und idenn so wie Sie es gezeichnet haben, befinden sich keine der Zusammenführungen, die Sie entfernen möchten, im Verlauf von beiden g oder iund alle relevanten Änderungen sind in der Geschichte von beiden g oder i (d. h. der Abschnitt, der entfernt wird, ist nur Commits zusammenführen[1]).

Sie möchten nicht die gesamte Konfliktlösung wiederholen, aber das ist in Ordnung, da Sie das gewünschte Ergebnis der Zusammenführung bereits kennen. Sie müssen also eine Kopie davon erstellen j wessen Eltern sind g und i. Dafür gibt es mindestens zwei Möglichkeiten.

Der einfachste Weg verwendet Installationsbefehle, sodass er möglicherweise weniger vertraut aussieht.

git checkout $( git commit-tree j^{tree} -p g -p i -m "merge message here" )
git branch -f my_branch

wo my_branch ist die Filiale, die Sie derzeit haben jund j g und i sind Ausdrücke, die zu den jeweiligen Commits aus Ihrem Diagramm aufgelöst werden. (Das könnten Commit-ID-Werte (SHA) sein, Referenzen, die derzeit auf diese Commits verweisen usw.)

Denken Sie daran, dass die Eltern des Commits geordnet sind – die zweite -p Option identifiziert den Commit, der scheinbar mit dem ersten ‘zusammengeführt’ wurde -p Commit der Option.

Ein anderer Ansatz wäre das Auschecken g (vorausgesetzt g sollte der erste Commit sein; Tauschen g und i in diesem Verfahren sonst) und

git merge --no-commit i
git rm -rf :/:
git checkout j -- :/:
git commit -m "merge message here"

Bei diesem Ansatz initiieren Sie eine neue Zusammenführung, löschen den Arbeitsbaum und den Index, laden den Commit-Baum und den Index aus dem vorherigen Zusammenführungsergebnis und schließen dann die Zusammenführung mit diesem Inhalt ab.

Ich finde das ein wenig komplizierter, und es enthält eine rm Befehl, der möglicherweise Warnsignale auslöst, wenn Sie sich nicht sicher sind, was vor sich geht, aber er hat den Vorteil, dass er sich an Befehle hält, die für Endbenutzer bestimmt sind.


[1] Ich gehe davon aus, dass keine der Zusammenführungen neue Änderungen einführt (außer der Konfliktlösung). Wenn dies der Fall ist, handelt es sich um das, was manchmal als „böse Fusionen“ bezeichnet wird; Das obige Verfahren würde tatsächlich immer noch funktionieren, aber es würde dazu führen, dass Ihre neue Zusammenführung ebenfalls eine “böse Zusammenführung” wäre.

  • Während ich den Ansatz gewählt habe, nur die Frage zu beantworten, werde ich auch darauf hinweisen, dass ich im Allgemeinen bin nicht zugunsten einer unnötigen Änderung der Geschichte – und in 99% der Fälle wäre so etwas unnötig.

    – Markus Adelsberger

    11. November 2019 um 14:50 Uhr

Hier ist eine Möglichkeit, dies zu tun:

 git reset --hard g //reset branch to g commit (use 'i' and merge 'g' if the  branch originally came from i) 
 git merge i --no-commit  
 git reset j :/: //resolve all conflicts by replacing the staging area with all file versions from original commit j
 git commit
 optional:  git reset --hard //if you want the work tree to reflect the new merge commit

  • Ich habe nicht sorgfältig genug gelesen, um herauszufinden, ob dies das verrückte Szenario in der ursprünglichen Frage gelöst hat, aber es hat für meinen Anwendungsfall funktioniert (im Grunde nur “f und h in h” “gequetscht”).

    – Elfenprinz13

    29. April um 19:04 Uhr

Die einzige Möglichkeit, die ich mir vorstellen kann, um dies in einem einzigen Schuss zu tun, ohne den Prozess des Zusammenführens durchlaufen zu müssen, ist die Verwendung git commit-tree. Rufen Sie zuerst die ID des Baums der J-Revision mit git cat-file ab:

git cat-file -p j

Kopieren Sie die ID des Baumobjekts.

Führen Sie dann git commit tree aus, Sie können die beiden übergeordneten Revisionen mit -p angeben, den Kommentar mit -m, und die Baum-ID, die Sie vom vorherigen git-Befehl erhalten haben, wird als letztes Argument für den Aufruf verwendet.

git commit-tree -m 'Here's the comment" -p g -p i tree-object-of-j

Wenn Sie diesen Befehl ausführen, gibt Git die ID des neu erstellten Revisionsobjekts aus. Sie können einen Zweig darauf zeigen oder den j-Zeiger bewegen, wenn Sie bereits überprüft haben, ob die Revision genau das ist, was Sie wollten:

git branch -f j new-revision-of #move j branch to the new revision id

Sie werden der Autor der Revision sein…. wenn Sie die Daten oder die Autoren ändern möchten, können Sie dies tun, indem Sie Umgebungsvariablen setzen. Überprüfen git help commit-tree.

  • Dies ist im Grunde der Installationsansatz, den ich unten erwähnt habe; Beachten Sie jedoch, dass die Schritte, in denen Sie Befehle ausführen, um SHA-Werte zu lernen und sie dann an andere Befehle weiterzugeben, nicht erforderlich sind und Fehler verursachen können

    – Markus Adelsberger

    11. November 2019 um 15:00 Uhr

  • Netter Trick mit j^{tree}. Noch eins für meine Trickkiste.

    – eftshift0

    11. November 2019 um 15:05 Uhr

  • Hm…. aber j^{tree} lässt Sie nicht denken, dass es der Baum von Js Eltern ist? Sollte es nicht sein j{tree}? Ich bin mir nicht sicher, müsste etwas über diese anscheinend doppelte Verwendung von ^ (^ vs ^{}) erfahren.

    – eftshift0

    11. November 2019 um 15:07 Uhr

  • @eftshift0 danke für deine Antwort. Ich wünschte, ich könnte sowohl Ihre als auch Marks Antwort akzeptieren, denn obwohl er der Erste war, war Ihre Erklärung der beteiligten Befehle für mich klarer.

    – AB

    11. November 2019 um 15:24 Uhr

1011150cookie-checkWie man nachfolgende Merge-Commits „squasht“.

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

Privacy policy