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 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 i
denn so wie Sie es gezeichnet haben, befinden sich keine der Zusammenführungen, die Sie entfernen möchten, im Verlauf von beiden g
oder i
und 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 j
und 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.
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
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
.
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
oderh
die ich später herausfinden werde, und ich werde diese nicht ändern können, aber ich könnte sie ändernj'
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
undh
? Wie willst duf
verpflichtet sich, dabei zu seinj
oder Sie möchten, dass sie dupliziert werdenc
unde
, 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
undj
zusammen zu einem neuen Commitj'
. Die Konflikte wurden gelöst, als sie erstellt wurden, und jetzt befindet sich mein Repo in einem Zustand, den ich möchtej'
zu sein. Ich will und kann den Verlauf von nicht änderng
oderi
. 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
oderh
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önntenj
so wie du es ändern könntestj'
. “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