Wie quetsche ich zwei nicht aufeinanderfolgende Commits?

182

Ich bin ein bisschen neu in der gesamten Rebasing-Funktion von Git. Nehmen wir an, ich habe folgende Zusagen gemacht:

A -> B -> C -> D

Danach stelle ich fest, dass es Deinen Fix enthält, der von neu hinzugefügtem Code abhängt A, und dass diese Commits zusammengehören. Wie quetsche ich A& Dzusammen und lasse B& in CRuhe?

Nik Reiman
quelle

Antworten:

256

Sie können git rebase --interactiveD vor B ausführen und neu anordnen und D in A quetschen.

Git öffnet einen Editor und Sie sehen eine Datei wie diese, z. git rebase --interactive HEAD~4

pick aaaaaaa Commit A
pick bbbbbbb Commit B
pick ccccccc Commit C
pick ddddddd Commit D

# Rebase aaaaaaa..ddddddd onto 1234567 (4 command(s))
#
# 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
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out

Jetzt ändern Sie die Datei so, dass sie so aussieht:

pick aaaaaaa Commit A
squash ddddddd Commit D
pick bbbbbbb Commit B
pick ccccccc Commit C

Und git wird nun die Änderungen von A und D zu einem Commit zusammenführen und anschließend B und C setzen. Wenn Sie die Festschreibungsnachricht von D nicht beibehalten möchten squash, verwenden Sie stattdessen das fixupSchlüsselwort. Weitere Informationenfixup finden Sie in den git rebaseDokumenten oder in dieser Frage, die einige gute Antworten enthält.

Rudi
quelle
5
Anfangs las ich es als "Rebase D auf A, Squash D in A, dann Rebase B auf DA". Aus der Antwort geht nicht hervor, dass dies durch Neuanordnen von Zeilen in einem Texteditor erfolgen kann.
Victor Sergienko
3
Wenn Ihre Niederlassung lokal ist, wird There is no tracking information for the current branchbeim erneuten Basieren eine Fehlermeldung angezeigt. In diesem Fall müssen Sie die Anzahl der Commits angeben, mit denen Sie arbeiten möchten git rebase -i HEAD~4. Siehe diese Antwort .
Johndodo
1
Ich benutze den interaktiven Modus (git rebase -i) seit Jahren. Ich habe gerade festgestellt, dass er nachbestellt werden kann . Danke 🤟🏻
CalvinChe
43

Hinweis: Sie sollten Commits, die auf ein anderes Repo verschoben wurden, in keiner Weise ändern, es sei denn, Sie kennen die Konsequenzen .

git log --oneline -4

D commit_message_for_D
C commit_message_for_C
B commit_message_for_B
A commit_message_for_A

git rebase --interactive

pick D commit_message_for_D
pick C commit_message_for_C
pick B commit_message_for_B
pick A commit_message_for_A

Typ i(VIM in den Einfügemodus versetzen)

Ändern Sie die Liste so, dass sie so aussieht (Sie müssen die Festschreibungsnachricht nicht entfernen oder einschließen). Nicht falsch schreiben squash! ::

pick C commit_message_for_C
pick B commit_message_for_B
pick A commit_message_for_A
squash D

Geben Sie Escdann ZZ(Speichern und Beenden von VIM) ein.

# This is a combination of 2 commits.
# The first commit's message is:

commit_message_for_D

# This is the 2nd commit message:

commit_message_for_A

Art i

Ändern Sie den Text so, wie die neue Festschreibungsnachricht aussehen soll. Ich empfehle eine Beschreibung der Änderungen im Commit Aund D:

new_commit_message_for_A_and_D

Geben Sie Escdann einZZ

git log --oneline -4

E new_commit_message_for_A_and_D
C commit_message_for_C
B commit_message_for_B

git show E

(You should see a diff showing a combination of changes from A and D)

Sie haben jetzt ein neues Commit erstellt E. Commits Aund Dsind nicht mehr in Ihrer Geschichte, aber nicht weg. Sie können sie zu diesem Zeitpunkt noch für eine Weile wiederherstellen git rebase --hard D( git rebase --hardzerstört alle lokalen Änderungen! ).

Nate
quelle
3

Für diejenigen, die SourceTree verwenden :

Stellen Sie sicher, dass Sie die Commits noch nicht verschoben haben.

  1. Repository> Interaktiver Rebase ...
  2. Ziehen Sie D (das neuere Commit) direkt über A (das ältere Commit).
  3. Stellen Sie sicher, dass Commit D markiert ist
  4. Klicken Squash with previous
Adam Johns
quelle
1

Die interaktive Rebase funktioniert gut, bis Sie einen großen Feature-Zweig mit 20 bis 30 Commits und / oder einigen Zusammenführungen von Master- oder / und Konfliktbehebungen haben, während Sie in Ihrem Zweig Commits durchgeführt haben. Auch mit meinen Commits durch die Geschichte Suchen und Ersetzen pickmit squashnicht hier gearbeitet. Also suchte ich nach einem anderen Weg und fand diesen Artikel . Ich habe meine Änderungen vorgenommen, um dies in einem separaten Zweig zu tun:

git checkout master
git fetch
git pull
git merge branch-name
git reset origin/master
git branch -D branch-name
git checkout -b branch-name
git add --all
#Do some commit
git push -f --set-upstream origin branch-name

Vorher habe ich meine Pull-Anfrage mit ca. 30 Commits mit 2-3 Zusammenführungen von Master + Fixing-Konflikten erhalten. Und danach bekam ich klare PR mit einem Commit.

PS hier ist Bash- Skript , um diese Schritte im Automode auszuführen.

Oleksiy Guzenko
quelle
Die erste Lösung in diesem Artikel ist wirklich nett, danke für den Link
Hoody
-1

$ git checkout master

$ git log --oneline

D
C
B
A

$ git rebase --onto HEAD ^^^ HEAD ^

$ git log --oneline

D
A
Baumwolle
quelle
Ich denke du meinst --oneline? Und es sieht so aus, als wären Sie gefallen Cund Bdas ist nicht die Absicht des OP.
Bstpierre
Hat bei mir nicht funktioniert. Es hat sowohl meinen HEAD als auch meinen Master nach A verschoben, aber D nicht mit A ( git show A) zusammengeführt, und D, C und B gingen in meinem Ref-Log verloren. Musste git rebase Dzurück.
Nate