Wiederherstellen eines Git-Merge-Commits

183

Nehmen Sie den folgenden Fall:

Ich habe einige Arbeiten in einem Themenbereich und bin jetzt bereit, wieder zum Master zurückzukehren:

* eb3b733 3     [master] [origin/master]
| * b62cae6 2   [topic]
|/  
* 38abeae 1

Ich führe die Zusammenführung vom Master durch, löse die Konflikte und jetzt habe ich:

*   8101fe3 Merge branch 'topic'  [master]
|\  
| * b62cae6 2                     [topic]
* | eb3b733 3                     [origin/master]
|/  
* 38abeae 1

Jetzt hat die Zusammenführung einige Zeit in Anspruch genommen, sodass ich einen weiteren Abruf durchführe und feststelle, dass der Remote-Master-Zweig neue Änderungen aufweist:

*   8101fe3 Merge branch 'topic'  [master]
|\  
| * b62cae6 2                     [topic]
| | * e7affba 4                   [origin/master]
| |/  
|/|   
* | eb3b733 3
|/  
* 38abeae 1

Wenn ich 'git rebase origin / master' von master versuche, bin ich gezwungen, alle Konflikte erneut zu lösen, und ich verliere auch das Merge-Commit:

* d4de423 2       [master]
* e7affba 4       [origin/master]
* eb3b733 3
| * b62cae6 2     [topic]
|/  
* 38abeae 1

Gibt es eine saubere Möglichkeit, das Zusammenführungs-Commit neu zu definieren, damit ich einen Verlauf wie den unten gezeigten erhalte?

*   51984c7 Merge branch 'topic'  [master]
|\  
| * b62cae6 2                     [topic]
* | e7affba 4                     [origin/master]
* | eb3b733 3
|/  
* 38abeae 1
Jipumarino
quelle
74
TL; DR:git rebase --preserve-merges origin/master
Ilia K.
6
Wenn Sie Konflikte erneut lösen müssen, sollten Sie sich git rerere ansehen .
Parker Coates
git config --global pull.rebase preserve um die Merge Commits während einer Rebase immer zu bewahren
Galath
4
Warnung: Ab Git 2.18 (Q2 2018, 5 Jahre später) git --rebase-mergeswird letztendlich das alte ersetzt git --preserve-merges. Siehe Was genau machtrebase --preserve-merges
Gits
1
--preserve-mergesist veraltet. Verwenden Siegit rebase --rebase-merges origin/master
Arjun Sreedharan

Antworten:

126

Hier gibt es zwei Möglichkeiten.

Eine besteht darin, eine interaktive Rebase durchzuführen und das Zusammenführungs-Commit zu bearbeiten, die Zusammenführung manuell zu wiederholen und die Rebase fortzusetzen.

Eine andere --rebase-mergesMöglichkeit besteht darin, die Option on zu verwenden git rebase, die im Handbuch wie folgt beschrieben wird: "Standardmäßig löscht eine Rebase einfach Zusammenführungs-Commits aus der Aufgabenliste und fügt die neu basierten Commits in einen einzelnen linearen Zweig ein. Mit --rebase- Beim Zusammenführen versucht die Neuausrichtung stattdessen, die Verzweigungsstruktur innerhalb der neu zu basierenden Commits beizubehalten, indem die Zusammenführungs-Commits neu erstellt werden. Alle gelösten Zusammenführungskonflikte oder manuellen Änderungen in diesen Zusammenführungs-Commits müssen manuell gelöst / erneut angewendet werden. "

Siride
quelle
16
Ich habe die Option -p ausprobiert, und der Commit-Verlauf bleibt zwar so, wie ich es wollte, aber ich muss die Konflikte erneut lösen, selbst in Dateien, die nicht in origin / master bearbeitet wurden. Welches wäre nach Ihrem ersten Vorschlag die genaue Reihenfolge der Befehle?
Jipumarino
2
@jipumarino: git rebase -i (sag ihm, dass er das Merge-Commit bearbeiten soll), wenn es zum Merge-Commit kommt, git reset --hard HEAD ^, git merge, Konflikte beheben, git commit, git rebase --continue. Vielleicht möchten Sie sich auch git rerere ansehen, das bei solchen Dingen helfen soll (aber ich habe es nie benutzt, daher kann ich keinen Rat oder keine Hilfe anbieten).
Siride
2
Vielen Dank. Ich habe rerere aktiviert und es mit rebase -p versucht und es funktioniert wie es sollte.
Jipumarino
3
Hier ist ein ausgezeichneter Blog-Beitrag, der genau diese Situation beschreibt: Rebasing Merge Commits in Git
kynan
1
Dies ist nicht die Lösung, da Sie Zusammenführungen beim ersten Mal immer noch manuell auflösen müssen.
Flimm
29

Ok, das ist eine alte Frage, die bereits beantwortet wurde @siride, aber diese Antwort hat in meinem Fall nicht ausgereicht, da --preserve-mergesSie gezwungen sind, alle Konflikte zum zweiten Mal zu lösen. Meine Lösung basiert auf der Idee, @Tobi Baber mit genauen Schritt-für-Schritt-Befehlen

Wir beginnen also mit einem solchen Zustand anhand eines Beispiels in der Frage:

*   8101fe3 Merge branch 'topic'  [HEAD -> master]
|\  
| * b62cae6 2                     [topic]
| |
| | * f5a7ca8 5                   [origin/master]
| | * e7affba 4
| |/  
|/|   
* | eb3b733 3
|/  
* 38abeae 1

Beachten Sie, dass wir 2 Commits vor uns haben, sodass Cherry-Pick nicht funktionieren würde.

  1. Lassen Sie uns zunächst den richtigen Verlauf erstellen, den wir möchten:

    git checkout -b correct-history # create new branch to save master for future
    git rebase --strategy=ours --preserve-merges origin/master
    

    Wir verwenden --preserve-merges, um unser Merge-Commit in der Geschichte zu speichern. Wir --strategy=oursignorieren alle Zusammenführungskonflikte, da es uns egal ist, welche Inhalte in diesem Zusammenführungs-Commit enthalten sind. Wir brauchen jetzt nur einen schönen Verlauf.

    Die Geschichte wird so aussehen (Meister ignorieren):

    *   51984c7 Merge branch 'topic'  [HEAD -> correct-history]
    |\  
    | * b62cae6 2                     [topic]
    * | f5a7ca8 5                     [origin/master]
    * | e7affba 4
    * | eb3b733 3
    |/  
    * 38abeae 1
    
  2. Lassen Sie uns jetzt den richtigen Index erhalten.

    git checkout master # return to our master branch
    git merge origin/master # merge origin/master on top of our master
    

    Es kann sein, dass hier einige zusätzliche Zusammenführungskonflikte auftreten. Dies sind jedoch nur Konflikte aus Dateien, die zwischen 8101fe3und geändert wurden f5a7ca8, aber nicht bereits gelöste Konflikte austopic

    Der Verlauf sieht folgendermaßen aus (ohne Berücksichtigung des korrekten Verlaufs):

    *   94f1484 Merge branch 'origin/master'  [HEAD -> master]
    |\  
    * | f5a7ca8 5                   [origin/master]
    * | e7affba 4
    | *   8101fe3 Merge branch 'topic'
    | |\  
    | | * b62cae6 2                     [topic]
    |/ /
    * / eb3b733 3
    |/  
    * 38abeae 1
    
  3. Die letzte Phase besteht darin, unseren Zweig mit der richtigen Historie und den Zweig mit dem richtigen Index zu kombinieren

    git reset --soft correct-history
    git commit --amend
    

    Wir verwenden reset --soft, um unseren Zweig (und den Verlauf) auf den korrekten Verlauf zurückzusetzen, lassen jedoch den Index und den Arbeitsbaum unverändert. Dann commit --amendschreiben wir unser Merge-Commit, das früher einen falschen Index hatte, mit unserem guten Index vom Master neu.

    Am Ende werden wir einen solchen Status haben (beachten Sie eine andere ID von Top Commit):

    *   13e6d03 Merge branch 'topic'  [HEAD -> master]
    |\  
    | * b62cae6 2                     [topic]
    * | f5a7ca8 5                     [origin/master]
    * | e7affba 4
    * | eb3b733 3
    |/  
    * 38abeae 1
    
Ivan Naydonov
quelle
Das ist großartig und hat sehr geholfen! Aber ich habe den Trick mit Commit nicht verstanden - amend: Könnten Sie mehr Informationen dazu hinzufügen? Was genau passiert, nachdem Sie es ausgeführt haben - ich habe festgestellt, dass SHA of Commit geändert wurde - aber warum? Oder was passiert, wenn Sie dies nicht ausführen?
ZenJ
1
@ZenJ git commit --amendfügt die Änderungen zum letzten Commit hinzu (HEAD, in diesem Fall das Merge-Commit). Da sich der Commit-Inhalt ändert, wird der Hash aktualisiert.
Staelens
1
Für Personen, für die vor dem Beheben von Konflikten kein erneutes Aktivieren aktiviert wurde, ist diese Lösung hervorragend geeignet, da Sie keine Konflikte mehr beheben müssen. Vielen Dank!
Shackleford
6

Angesichts der Tatsache, dass ich gerade einen Tag verloren habe, um dies herauszufinden, und tatsächlich mit Hilfe eines Kollegen eine Lösung gefunden habe, dachte ich, ich sollte mich einschalten.

Wir haben eine große Codebasis und müssen uns mit 2 Zweigen befassen, die gleichzeitig stark modifiziert werden. Es gibt einen Hauptzweig und einen Nebenzweig, wenn Sie welche.

Während ich den sekundären Zweig mit dem Hauptzweig zusammenführe, wird die Arbeit im Hauptzweig fortgesetzt, und bis ich fertig bin, kann ich meine Änderungen nicht mehr übertragen, da sie nicht kompatibel sind.

Ich muss daher meine "Zusammenführung" "neu gründen".

So haben wir es endlich gemacht:

1) notieren Sie sich die SHA. Beispiel: c4a924d458ea0629c0d694f1b9e9576a3ecf506b

git log -1

2) Erstellen Sie den richtigen Verlauf, aber dadurch wird die Zusammenführung unterbrochen.

git rebase -s ours --preserve-merges origin/master

3) notieren Sie sich die SHA. Beispiel: 29dd8101d78

git log -1

4) Setzen Sie nun zurück, wo Sie vorher waren

git reset c4a924d458ea0629c0d694f1b9e9576a3ecf506b --hard

5) Führen Sie nun den aktuellen Master in Ihrem Arbeitszweig zusammen

git merge origin/master
git mergetool
git commit -m"correct files

6) Nachdem Sie nun die richtigen Dateien, aber den falschen Verlauf haben, erhalten Sie den richtigen Verlauf zusätzlich zu Ihrer Änderung mit:

git reset 29dd8101d78 --soft

7) Und dann - ändern Sie die Ergebnisse in Ihrem ursprünglichen Zusammenführungs-Commit

git commit --amend

Voila!

Claude Peloquin
quelle
1

Es sieht so aus, als ob Sie Ihre erste Zusammenführung entfernen möchten. Sie können wie folgt vorgehen:

git checkout master      # Let's make sure we are on master branch
git reset --hard master~ # Let's get back to master before the merge
git pull                 # or git merge remote/master
git merge topic

Das würde dir geben, was du willst.

Antoine Pelisse
quelle
4
Wenn Rerere aktiviert ist, scheint dies das gleiche Ergebnis zu liefern wie die oben von Siride angegebene Rebase-P-Lösung.
Jipumarino
0
  • Aus Ihrem Merge-Commit
  • Wählen Sie die neue Änderung aus, die einfach sein sollte
  • kopiere deine Sachen
  • Wiederholen Sie die Zusammenführung und lösen Sie die Konflikte, indem Sie einfach die Dateien von Ihrer lokalen Kopie kopieren;)
Tobi B.
quelle
1
Diese Antwort sieht gut aus, wäre aber nützlicher, wenn Sie die tatsächlichen Git-Befehle geben würden, falls ein Benutzer neu bei Git ist
Louise Davies