How to squash 2 pairs of non-consecutive commits? [duplicate]
I'm a bit new to the whole rebasing feature within git. Let's say that I made the following commits:
A -> B -> C -> D
Afterwards, I realize that D
contains a fix which depends on some new code added in A
, and that these commits belong together. How do I squash A
& D
together and leave B
& C
alone?
You can run git rebase --interactive
and reorder D before B and squash D into A.
Git will open an editor, and you see a file like this, ex: 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
Now you change the file that it looks like this:
pick aaaaaaa Commit A
squash ddddddd Commit D
pick bbbbbbb Commit B
pick ccccccc Commit C
And git will now meld the changes of A and D together into one commit, and put B and C afterwards. When you don't want to keep the commit message of D, instead of squash
, you would use the fixup
keyword. For more on fixup
, you can consult the git rebase
docs, or check out this question which has some good answers.
Note: You should not change commits that have been pushed to another repo in any way unless you know the consequences.
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
Type i
(Put VIM in insert mode)
Change the list to look like this (You don't have to remove or include the commit message). Do not misspell squash
!:
pick C commit_message_for_C
pick B commit_message_for_B
pick A commit_message_for_A
squash D
Type Esc then ZZ
(Save and exit VIM)
# 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
Type i
Change the text to what you want the new commit message to look like. I recommend this be a description of the changes in commit A
and D
:
new_commit_message_for_A_and_D
Type Esc then ZZ
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)
You have now created a new commit E
. Commits A
and D
are no longer in your history but are not gone. You can still recover them at this point and for a while by git rebase --hard D
(git rebase --hard
will destroy any local changes!).