Was sind die drei Dateien in einem 3-Wege-Merge für interaktives Rebasing mit Git und Meld?

Lesezeit: 7 Minuten

Was sind die drei Dateien in einem 3 Wege Merge fur interaktives
Jake

Nehmen wir an, ich mache eine interaktive Rebase mit git rebase -i. Wenn ein Konflikt auftritt, wird mir möglicherweise ein Zusammenführungskonflikt angezeigt und ich werde aufgefordert, eine 3-Wege-Zusammenführung durchzuführen. Verwenden meldwerden mir drei Fenster angezeigt: LOCAL (links), ??? (Mitte) und REMOTE (rechts). Hier vorbei ??? Ich meine einfach das meld stellt keinen speziellen Namen bereit, der an die Datei angehängt werden kann.

Während einer normalen Zusammenführung ist dies sinnvoll, da die Mitte der gemeinsame Vorfahre ist und Sie die lokalen und entfernten Änderungen an diesem Vorfahren zusammenführen. Dies scheint jedoch während einer interaktiven Rebase nicht der Fall zu sein – es ist unklar, was jede Datei darstellt.

Was stellen diese Dateien in der 3-Wege-Zusammenführung jeweils während einer interaktiven Rebase dar? Und was ist mein Ziel, wenn ich diese Dateien bearbeite?

Aktualisieren: Basierend auf den Kommentaren und Experimenten, die ich sehe:

  • Links (LOCAL): Ihre lokale Version der Datei an diesem Punkt in der Commit-Wiedergabesequenz.
  • Rechts (REMOTE): Der Zustand der Datei unmittelbar nachdem der aktuelle Commit ursprünglich angewendet wurde.
  • Mitte: Das übergeordnete Element des Rechts in der ursprünglichen Commit-Sequenz.

Meine Aufgabe ist es also, das Delta von Mitte nach rechts zu bestimmen und dieses Delta dann nach links anzuwenden. Die Mitte sollte den Status der Datei widerspiegeln, nachdem das aktuelle Commit-Delta in der neuen Commit-Sequenz angewendet wurde.

Beachten Sie, dass diese Konfiguration zumindest bis zu einem gewissen Grad spezifisch für die Verschmelzung zu sein scheint. Das 3-Wege-Merge-Verhalten von Git kann für andere Editoren abweichen.

Was sind die drei Dateien in einem 3 Wege Merge fur interaktives
Torek

Die mittlere Version ist die Zusammenführungsbasis, genau wie bei a git merge.

(Der Name „other“ könnte angemessener sein als „remote“, da es nicht erforderlich ist, dass die andere Seite einer Zusammenführung eine Remote ist, und da Mercurial konsequent den Namen „other“ dafür verwendet, muss Git nicht mit Mercurial übereinstimmen , aber etwas Konsistenz könnte nett sein. Beachten Sie, dass Git hier auch die Namen “ours” und “theirs” verwendet, also werden wir niemals 100% Konsistenz von Git bekommen. 🙂 )

Aber warten Sie, wie gibt es eine Merge-Basis?

Es gibt immer eine Merge-Basis.

Normalerweise müssen wir es nicht einmal finden, da jeder Patch sauber angewendet wird, wenn er als Patch behandelt wird (ohne eine Drei-Wege-Zusammenführung zu versuchen). Aber manchmal wird der Patch nicht sauber angewendet, und wir tun auf die Drei-Wege-Mischung zurückgreifen müssen.

(Übrigens können Sie diesen Fallback deaktivieren. Siehe --3way, --no-3wayund am.threeWay in die Git-Am-Dokumentationobwohl die hier verlinkte Seite bereits veraltet ist, da sich diese Steuerelemente kürzlich geändert haben.)

$ git rebase -i
pick aaaaaaa first commit
pick bbbbbbb second commit
pick ccccccc third commit

Lassen Sie uns auch das Commit-Diagramm zeichnen, damit wir sehen können, wovon und wohin wir rebasen:

              A - B - C   <-- branch
            /
... - o - *
            \
              G - H       <-- origin/branch

Wir werden Commits herauspicken A, Bund C (A = aaaaaaaetc), sodass wir am Ende dieses Ergebnis erhalten:

              A - B - C   [abandoned]
            /
... - o - *           A' - B' - C'   <-- branch
            \       /
              G - H       <-- origin/branch

Schauen wir uns den ersten Rosinenpick genau an A.

Dies vergleicht (diffs) A gegen seinen Elternteil, der begangen wird *und versucht, den resultierenden Diff zum Festschreiben anzuwenden H.

Begehen Hist jedoch etwas von commit abgedriftet *. Tatsächlich können wir eine Zusammenführungsbasis dazwischen finden A und Hund es ist … verpflichten *. Dies ist eigentlich eine ziemlich anständige Merge-Basis, obwohl es am besten ist, wenn Git den Patch einfach so anwenden kann, wie er ist, ohne auf den Drei-Wege-Merge-Code zurückgreifen zu müssen.

Also verpflichte dich * ist die Zusammenführungsbasis beim Rosinenpicken A auf zu H. Wenn die Zusammenführung abgeschlossen ist, erhalten wir einen neuen Commit A'. (Die neue SHA-1-ID könnte lauten aaaaaa1 zum Beispiel. Wahrscheinlich nicht; nennen wir es einfach A'.)

Jetzt werden wir Rosinen pflücken B. Dies unterscheidet sich B gegenüber seinem Elternteil, das ist Aund versucht, das diff auf anzuwenden A'.

Begehen A'ist jedoch etwas von commit abgedriftet B. Tatsächlich können wir eine Zusammenführungsbasis dazwischen finden B und A'und das ist … verpflichten * aufs Neue. Leider ist dies eine erbärmliche Merge-Basis. Glücklicherweise greift Git nur dann darauf zurück, wenn der Patch nicht unverändert angewendet werden kann, und normalerweise ist dies möglich. Aber wenn es nicht kann, Git unterscheidet sich * vs B und * vs A' und versuchen Sie, diese beiden Unterschiede zusammenzuführen. Beachten Sie, dass * vs B enthält alle Änderungen, die wir in vorgenommen haben Aaber * vs A' enthält auch alle diese gleichen A Wenn wir also Glück haben, bemerkt Git die bereits eingearbeiteten Änderungen und dupliziert sie nicht. bearbeiten Git-Cheats. (Dieser Code wurde kürzlich in Version 2.6 geändert, obwohl die Gesamtstrategie dieselbe bleibt.)

Betrachten Sie die tatsächliche Ausgabe von git diff wenn es verwendet wird, um nur die Änderung vom Commit anzuzeigen A begehen B. Dazu gehört ein index Linie:

diff --git a/foo b/foo
index f0b98f8..0ea3286 100644

Der Wert auf der linken Seite ist der (abgekürzte) Hash für die Version der Datei foo im Einsatz A. Der Wert auf der rechten Seite ist der Hash für die Version der Datei im Commit B.

Git täuscht einfach eine Zusammenführungsbasis aus dem Hash der linken Seite vor. Mit anderen Worten, die Dateiversion im Commit A wird zur gefälschten Merge-Basis. (Git besteht --build-fake-ancestor zu git apply. Dies erfordert, dass sich die bestimmten Datei-Blob-Objekte im Repository befinden, aber sie sind es, da sie festgeschrieben sind A. Für Patches per E-Mail verwendet Git denselben Code, aber der Blob kann vorhanden sein oder nicht.)

Beachten Sie, dass Git dies tatsächlich beim Rosinenpicken von Commit tut A auch, aber dieses Mal ist die Merge-Basisdatei die Version von Commit *was wirklich ist die Merge-Basis.

Schließlich pflücken wir Rosinen C. Dies unterscheidet sich B vs Cso wie wir uns unterschieden A vs B letztes Mal. Wenn wir den Patch so anwenden können, wie er ist, gut; wenn nicht, fallen wir zurück Commit verwenden * wieder als Zusammenführungsbasis. Es ist wieder einmal eine ziemlich erbärmliche Merge-Basis. auf die gleiche Weise wie zuvor, indem Sie vorgeben, dass die Version in B war die gemeinsame Basis.

Dies erklärt übrigens auch, warum Sie bei diesen Rebases dazu neigen, immer wieder dieselben Merge-Konflikte zu sehen: Wir verwenden jedes Mal dieselbe Merge-Basis. (Aktivieren git rerere kann helfen.)

  • Ich mache gerade eine Rebase, bei der die Mitte kein gemeinsamer Vorfahre von links und rechts ist – sie ist nur ein Vorfahre von rechts (entfernt) und scheint ein Nachfolger von links (lokal) zu sein. Wie kann das sein, wenn es sich um eine Merge-Basis handelt? Beachten Sie, dass ich in meinem Rebase einen Commit aus meinem Verlauf lösche und die Mitte den Status der Datei darstellt, als der gelöschte Commit angewendet wurde.

    – Jake

    3. Mai 2016 um 1:03 Uhr


  • Hm, wie eigentlich? Ich bemerke hier, dass Sie sich eine Datei ansehen, die kein Commit ist (also können wir die ID der Datei nicht verwenden, um die ID des Commits abzuleiten, es sei denn, diese bestimmte Version dieser Datei ist eindeutig für einen bestimmten Commit). Es ist auch möglich, dass einige Merge-Tools etwas anderes tun, um hilfreich zu sein. Es gibt Code in Git, um beispielsweise die gemeinsamen Teile von zwei Tip-of-Commit-Dateien zu extrahieren, die einige Tools unterwegs verwenden könnten. Insbesondere verwendet p4merge create_virtual_base von git-sh-setup. Ich weiß nichts von meld obwohl.

    – Torek

    3. Mai 2016 um 1:16 Uhr

  • Aha, ich war sehr neugierig darauf, also habe ich mich durchgegraben git am Quellcode und stellte fest, dass ich mich in Bezug auf die Merge-Basen hier irre. Dies wäre die Antwort, außer dass Git cheats!

    – Torek

    3. Mai 2016 um 1:32 Uhr

  • @CodeWizard, Jake: aktualisierte Antwort. Es ist tatsächlich die Version aus dem übergeordneten Commit (in diesem speziellen Fall, aber nicht für per E-Mail gesendete Patches, bei denen Sie nur die Blob-ID und nicht die tatsächliche übergeordnete Commit-ID haben).

    – Torek

    3. Mai 2016 um 1:50 Uhr

  • Interessant! Vielen Dank, dass Sie eine so gründliche Antwort geschrieben und darüber hinaus die zusätzliche Recherche durchgeführt haben. Das hat mir wirklich geholfen, den Merge-/Rebase-Prozess zu verstehen 🙂

    – Jake

    3. Mai 2016 um 5:27 Uhr

Ein Merge und ein Rebase sind in dieser Hinsicht identisch. Der einzige Unterschied zwischen einem Merge und einem Rebase besteht darin, dass der Verlauf mit einem Rebase schöner (linearer) aussieht. Aber was die Konflikte betrifft, die entstehen und die Sie lösen müssen, sind sie gleich.

  • Könnten Sie das klären? An einer Zusammenführung sind zwei Zweige beteiligt. Bei einem interaktiven Rebase werden die Commits innerhalb eines Zweigs neu angeordnet oder geändert. Sie sind also nicht gleich. Folglich ist nicht klar, was lokaler, entfernter und gemeinsamer Vorfahre ist.

    – Jake

    3. Mai 2016 um 0:33 Uhr


998540cookie-checkWas sind die drei Dateien in einem 3-Wege-Merge für interaktives Rebasing mit Git und Meld?

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

Privacy policy