Wie tritt man dem letzten N-Merge bei?

9

In meinem Repository habe ich diesen Verlauf:

%Vor%

Dabei wurden <merged-commit-id-1> und <merged-commit-id-2> aus einem anderen Zweig zusammengeführt, aus dem ich zuvor den aktuellen branch erstellt habe. Jetzt möchte ich mich irgendwie mit diesen Merge Commits verbinden:

%Vor%

Ich habe es versucht

%Vor%

mit p und s , aber bekomme diesen Fehler:

%Vor%

(Ich habe es auch in einem neuen, einfachsten möglichen Repository reproduziert) Gibt es einen Weg um es herum?

Was ich wirklich will, ist, eine Menge von Commits mit ihren IDs aus dem ursprünglichen Zweig zusammenführen zu können (der geändert wurde), während Konflikte schrittweise aufgelöst werden und Dutzende von Merge-Commits nicht eingeführt werden ( merge --squash ist auch keine Option, da ich die Geschichte bewahren muss.

Beispiel:

%Vor%

Lösung von torek:

%Vor%     
user5365198 22.09.2015, 21:26
quelle

1 Antwort

4

Zuerst etwas Hintergrund ... Zusammenführen, in git, hat zwei Funktionen:

  1. Er vergleicht die "merge base" zweier Entwicklungslinien mit den beiden tip commits und kombiniert dann die Änderungen mit einem semi-intelligenten 1 Algorithmus. Das heißt, git merge other berechnet zuerst $base , 2 dann git diff $base HEAD und git diff $base other . Diese beiden Diffs teilen git mit, was Sie tun möchten, $base , z. B. fügen Sie eine Zeile zu foo.txt hinzu, entfernen eine Zeile aus bar.tex und ändern eine Zeile in xyz.py . Git versucht dann, all diese Änderungen beizubehalten, und wenn die zu foo.txt hinzugefügte Zeile dem selben Weg in jedem Zweig hinzugefügt wurde, oder die selbe Zeile wurde entfernt von bar.tex , behalte einfach eine Kopie der Änderung.

  2. Es zeichnet für die zukünftige Verwendung die Tatsache auf, dass zwei Entwicklungslinien zusammengeführt wurden und dass alle gewünschten Änderungen von beiden Linien nun im "merge commit" vorhanden sind. Das resultierende Commit eignet sich daher beispielsweise als neue Merge-Base. Wir werden so etwas (nicht genau das) gleich sehen.

Diese beiden Funktionen werden auf sehr unterschiedliche Weise implementiert. Der erste Schritt erfolgt, indem Sie die Diffs in Ihrem Arbeitsbaum zusammenführen, sodass Sie keine allzu schwierigen Fälle manuell behandeln können. Der zweite Vorgang wird zu dem Zeitpunkt ausgeführt, zu dem Sie den Merge-Commit durchführen, indem Sie zwei 3 "Parent-Commit-IDs" im neuen (Zusammenführungs-) Commit auflisten.

Was Sie entscheiden müssen, ist, wie viel davon auf welche Weise zu bewahren ist. Ich denke, es hilft in diesen Fällen immens, das Commit-Diagramm zu zeichnen.

Hier beginnen Sie in Ihrem Beispiel mit einer gemeinsamen Basis, die ich B (für Basis) nennen werde, dann haben Sie zwei weitere Commits für master und eins für branch :

%Vor%

Nun auf branch fusionieren Sie zuerst commit C :

%Vor%

Ob ein manueller Eingriff erforderlich ist, hängt von der Änderung von B nach C und der Änderung von B nach E ab, aber in jedem Fall wird dadurch der neue Zusammenführungs-Commit F .

Dann fragst du git im commit D zusammenzuführen. Dies verwendet jedoch nicht mehr commit B als Zusammenführungs-Basis, da git nach der neuen Zusammenführung F rückwärts feststellt, dass die Zusammenführungs-Basis - die letzte Festschreibung zwischen branch und master - jetzt commit %Code%. Die Vergleiche sind daher C bis C und D bis C . Git kombiniert diese (vielleicht muss man aufhören und Hilfe bekommen); Wenn jemand (du oder Git) das Ergebnis festlegt, erhalten wir:

%Vor%

Nun fragst du (denke ich), F und G zu einem einzigen Merge-Commit zu "quetschen". In Anbetracht der Funktionsweise von git können Sie dies nur erreichen, indem Sie ein neues Merge-Commit erstellen und F -Point darauf setzen (den Verweis auf branch löschen und damit den einzigen Verweis auf G so gut).

Beim Erstellen dieses neuen Zusammenführungs-Commit müssen zwei Dinge beachtet werden:

  • Welcher Baum (Satz von Dateien / Verzeichnissen, die an den Commit angehängt sind, wird aus dem Index / Staging-Bereich erstellt) wollen Sie? Du kannst nur eins haben!

  • Welche Eltern-Commits möchten Sie auflisten? Damit es nach dem commit F gleich kommt, soll sein "erster Elternteil" E sein. Um es zu einem Zusammenführungs-Commit zu machen, muss es mindestens einen anderen Elternteil haben.

Der zu wählende Baum ist offensichtlich: commit E hat das gewünschte Zusammenführungsergebnis, also benutze den Baum von G .

Der zweite (und vielleicht dritte) Elternteil ist weniger offensichtlich. Es ist möglich, sowohl G als auch C aufzulisten, was zu einem Oktopus merge D :

führt %Vor%

Aber das macht nichts besonders Sinnvolles, weil O ein Vorfahre von C ist. Wenn Sie ein normaleres Zusammenführungs-Commit D erstellt haben, das nur auf M (als erstes Elternteil) und E verweist (als zweites Elternteil), erhalten Sie Folgendes:

%Vor%

Das wäre "genauso gut" wie der Octopus verschmelzen würde: Er hat den gleichen Baum (wir wählen den Baum jedes Mal aus commit D ) und er hat das gleiche erste Elternteil ( G ) . Es hat nur ein anderes übergeordnetes Element (d. H.% Co_de%), aber das bewirkt, dass E in seinem Verlauf liegt, sodass keine explizite Verbindung direkt zu D benötigt wird.

(Es kann eine explizite Verbindung haben, wenn Sie eine wollen, aber es gibt keinen besonders guten Grund, es zu geben.)

Es bleibt ein letztes Problem übrig: die Mechanismen, die tatsächlich diese Zusammenführung erzeugen ( C oder C , je nachdem, was Sie bevorzugen) und M darauf zu verweisen.

Es gibt einen "plumbing" -Befehl, der ihn direkt erstellt, vorausgesetzt, Sie befinden sich gerade in diesem Zustand:

%Vor%

An dieser Stelle können Sie laufen (beachten Sie, dass diese nicht getestet sind):

%Vor%

wobei O eine Datei ist, die die gewünschte Commit-Nachricht enthält. Dies erzeugt commit branch (mit nur den zwei Elternteilen, wobei der erste Commit msg ist, d. H.% Co_de% und der zweite Commit M ist, d. H.% Co_de%). Dann:

%Vor%

Es ist möglich, E mit normalen git "Porzellan" -Befehlen zu erstellen, aber es erfordert ein bisschen mehr Trickiness.Zuerst möchten wir den gewünschten Baum speichern, d. H.% Co_de%:

%Vor%

Dann, während auf dem Zweig branch~2 (weil D den aktuellen Zweig verwendet), sag git, um zwei Commits zu sichern. Es spielt keine Rolle, ob wir hier einen weichen, harten oder gemischten Reset verwenden, wir drehen gerade das Label master für den Moment zurück:

%Vor%

Dann sagen Sie git, dass wir M zusammenführen, aber machen Sie nichts, egal was. (Dies ist nur, um git einzurichten branch^{tree} und branch - Sie könnten diese Dateien direkt schreiben, anstatt diesen Teil zu tun.)

%Vor%

Löschen Sie dann alles, was in der Arbeitsbaumstruktur und im Index vorhanden ist, und ersetzen Sie das Ganze durch die gespeicherte Baumstruktur von commit git reset stattdessen:

%Vor%

(Sie müssen dafür in der obersten Ebene des Arbeitsbaums sein). Dies bereitet den Index für das neue Commit vor, und jetzt gibt es nur noch dieses letzte Commit:

%Vor%

Dies schreibt die Zusammenführung unter Verwendung des Baumes, dessen ID wir ergriffen haben, bevor wir branch ausführen. Der master hat das Tip-Commit von .git/MERGE_MSG be commit .git/MERGE_HEAD gemacht, also ist unser neues Commit merge-commit G , genauso, als hätten wir die Low-Level-Plumbing-Befehle verwendet.

(Hinweis: git reset kann dies nicht ganz, es ist nicht schlau genug, um zu wissen, wann dies in Ordnung ist - was nur dann der Fall ist, wenn wir es so eingerichtet haben, dass es überhaupt in Ordnung ist.)

1 Es ist nicht besonders schlau, aber es behandelt die einfachen Fälle ziemlich gut.

2 Sie können dies selbst mit git reset , mit branch berechnen. Für einige Fälle (gelegentlich, aber nicht so selten, wie man hoffen könnte) kann es mehr als eine geeignete Zusammenführungsbasis geben. Der Standard-Merge-Algorithmus ("rekursiv") führt zwei Merge-Basen zusammen, um eine "virtuelle Basis" zu erstellen und diese zu verwenden. Es gibt also einige subtile Unterschiede, wenn Sie versuchen, dies alles manuell zu tun.

3 Es kann mehr als zwei Eltern geben: Die Definition eines "Zusammenführungs-Commits" ist ein Commit mit mehr als einem Elternteil. Mehrwegezusammenführungen ("Octopus" -Mischungen) werden jedoch unterschiedlich durchgeführt (mit der Strategie "Octopus Merge"). Also ignoriere ich diese hier zum größten Teil.

    
torek 23.09.2015, 01:14
quelle

Tags und Links