Neuordnung von Commits

103

Ich arbeite derzeit an einer Filiale und möchte, dass einige Commits in andere Filialen integriert werden:

    a-b-c-d-e-f-g (branchA)
   /
--o-x-x-x-x-x-x-x-x-x-x (master)
   \
    x-x-x-x-x (branchB)

(Buchstaben bezeichnen Commits, und das "x" sind irrelevante Commits.)

Ich bemerkte jedoch, dass es eine gute Idee wäre, einige Commits zusammenzufassen. Ich möchte a, d, e und g in einem Patch "verketten" und an den Master übergeben. Die Commits b und f sollten als ein Commit für branchB ausgeführt werden. Gibt es einen guten Weg, dies zu erreichen?

Peter
quelle
4
Die beiden bereits gegebenen Antworten erwähnen den richtigen Befehl, aber ich denke, bei einer so grundlegenden Aufgabe wie dieser lohnt es sich, hier auf StackOverflow echte Anweisungen zu haben, also habe ich einige gepostet. (Ich bin überrascht, dass ich keine gute vorherige Frage zum Verlinken finden kann.)
Cascabel

Antworten:

125

Der Befehl, den Sie suchen, ist git rebasespeziell die -i/--interactiveOption.

Ich gehe davon aus, dass Sie Commit C in Zweig A belassen möchten und dass Sie wirklich bedeuten, dass Sie die anderen Commits in die anderen Zweige verschieben möchten, anstatt sie zusammenzuführen, da Zusammenführungen unkompliziert sind. Beginnen wir mit der Manipulation von Zweig A.

git rebase -i <SHA1 of commit a>^ branchA

Das ^bedeutet das vorherige Festschreiben, daher sagt dieser Befehl, Zweig A mit dem Festschreiben vor "a" als Basis neu zu gründen. Git präsentiert Ihnen eine Liste der Commits in diesem Bereich. Ordne sie neu und sag git, er soll die passenden zerquetschen:

pick c ...
pick a ...
squash d ...
squash e ...
squash g ...
pick b
squash f

Jetzt sollte die Geschichte so aussehen:

    c - [a+d+e+g] - [b+f] (branchA)
   /
--o-x-x-x-x-x-x-x-x-x-x (master)

Nehmen wir nun das neu gequetschte Commit b + f für branchB.

git checkout branchB
git cherry-pick branchA  # cherry-pick one commit, the tip of branchA

Und das gleiche für a + d + e + g für Master:

git checkout master
git cherry-pick branchA^

Aktualisieren Sie abschließend branchA so, dass es auf c zeigt:

git branch -f branchA branchA^^

Wir sollten jetzt haben:

    c (branch A) - [a+d+e+g] - [b+f] (dangling commits)
   /
--o-x-x-x-x-x-x-x-x-x-x-[a+d+e+g] (master)
   \
    x-x-x-x-x-[b+f] (branchB)

Beachten Sie, dass Sie bei mehreren Commits, die Sie zwischen Zweigen verschieben möchten, Rebase erneut verwenden können (nicht interaktiv):

# create a temporary branch
git branch fromAtoB branchA
# move branchA back two commits
git branch -f branchA branchA~2
# rebase those two commits onto branchB
git rebase --onto branchB branchA fromAtoB
# merge (fast-forward) these into branchB
git checkout branchB
git merge fromAtoB
# clean up
git branch -d fromAtoB

Zum Schluss noch ein Haftungsausschluss: Es ist durchaus möglich, Commits so neu zu ordnen, dass einige nicht mehr sauber gelten. Dies kann daran liegen, dass Sie eine schlechte Reihenfolge gewählt haben (indem Sie vor dem Festschreiben einen Patch einfügen, um die gepatchte Funktion einzuführen). In diesem Fall möchten Sie die rebase ( git rebase --abort) abbrechen . Andernfalls müssen Sie die Konflikte intelligent beheben (genau wie bei Zusammenführungskonflikten), die Korrekturen hinzufügen und dann ausführen git rebase --continue, um fortzufahren. Diese Anweisungen werden auch durch die Fehlermeldung bereitgestellt, die gedruckt wird, wenn der Konflikt auftritt.

Cascabel
quelle
Ist das Diagramm nicht git branch -f branchA branchA^^falsch? Sollte branchA an dieser Stelle nicht auf c zeigen, damit die erste Zeile des Diagramms angezeigt wird c (branchA)und nicht c - [a+d+e+g] - [b+f] (branchA)?
Ntc2
@enoksrd: Ja, es gibt einen Fehler, aber Ihre Bearbeitung hatte Fehler. Ich werde es reparieren, wenn ich hier eine Chance bekomme.
Cascabel
Anstatt zu tun, git branch -f branchA branchA^^warum kannst du nicht einfach einen git reset --hard <sha1 of c>in Zweig A machen?
Mikhail Vasilyev
17
git rebase -i HEAD~3

Wo 3ist die Anzahl der Commits, die neu angeordnet werden müssen ( Quelle ).

Dies öffnet vi und listet Commits vom ältesten (oben) zum neuesten (unten) auf.
Nun Neuordnungs die Linien, speichern , und beenden Sie den Editor.

ddpverschiebt die aktuelle Zeile nach unten
ddkP verschiebt die aktuelle Zeile nach oben ( Quelle )

Nelu
quelle
8

git rebase --interactive ist der Befehl, den Sie wollen.

Beispiel:

Der aktuelle Status lautet:

bernt@le3180:~/src/stackoverflow/reordering_of_commits
$ git status
On branch master
nothing to commit, working tree clean
bernt@le3180:~/src/stackoverflow/reordering_of_commits
$ git log --oneline
a6e3c6a (HEAD -> master) Fourth commit
9a24b81 Third commit
7bdfb68 Second commit
186d1e0 First commit

Ich möchte Commits 9a24b81(Third Commit) und 7bdfb68(Second Commit) neu anordnen . Dazu finde ich zuerst das Commit vor dem ersten Commit, das wir ändern möchten . Dies ist Commit 186d1e0(First Commit).

Der auszuführende Befehl lautet git rebase --interactive COMMIT-BEFORE-FIRST-COMMIT-WE-WANT-TO-CHANGEin diesem Fall:

bernt@le3180:~/src/stackoverflow/reordering_of_commits
$ git rebase --interactive 186d1e0

Dadurch wird im Standardeditor eine Datei mit folgendem Inhalt geöffnet:

pick 7bdfb68 Second commit
pick 9a24b81 Third commit
pick a6e3c6a Fourth commit

# Rebase 186d1e0..a6e3c6a onto 186d1e0 (3 commands)
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
# d, drop = remove commit
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#

Beachten Sie, dass die Reihenfolge der Commits in der Datei der für das Git-Protokoll verwendeten entgegengesetzt ist. Im Git-Protokoll befindet sich das letzte Commit oben. In dieser Datei befindet sich das letzte Commit unten.

Wie der Kommentar am Ende der Datei erklärt, kann ich verschiedene Dinge tun, z. B. Commits quetschen, löschen und neu anordnen. Um Commits neu zu ordnen, bearbeite ich die Datei so, dass sie so aussieht (die unteren Kommentare werden nicht angezeigt):

pick 9a24b81 Third commit
pick 7bdfb68 Second commit
pick a6e3c6a Fourth commit

Der pickBefehl am Anfang jeder Zeile bedeutet "dieses Commit verwenden (dh einschließen)". Wenn ich die temporäre Datei mit den Rebase-Befehlen speichere und den Editor verlasse, führt git die Befehle aus und aktualisiert das Repository und das Arbeitsverzeichnis:

$ git rebase --interactive 186d1e0
Successfully rebased and updated refs/heads/master.
bernt@le3180:~/src/stackoverflow/reordering_of_commits
$ git log --oneline
ba48fc9 (HEAD -> master) Fourth commit
119639c Second commit
9716581 Third commit
186d1e0 First commit

Beachten Sie den neu geschriebenen Commit-Verlauf.

Links:

Codeape
quelle
1
Bitte fügen Sie relevante Details aus Ihrem Link hinzu, um eine vollständige und in sich geschlossene Antwort zu erhalten. SO runzelt die Stirn bei "Nur-Link-Antworten". Wie würde man konkret git rebase --interactivedas Ziel von OP erreichen?
SherylHohman
Der zweite Link existiert jetzt nicht.
Devsaw
Inhalt hinzugefügt, um die Antwort vollständig und in sich geschlossen zu machen. Zweiter Link entfernt.
Codeape
-1

Git Rebase ist was Sie wollen. Überprüfen Sie die Option --interactive.


quelle
4
Bitte fügen Sie relevante Details aus Ihrem Link hinzu, um eine vollständige und in sich geschlossene Antwort zu erhalten. SO runzelt die Stirn bei "Nur-Link-Antworten".
SherylHohman