How do I squash two non-consecutive commits?

Nik Reiman picture Nik Reiman · Oct 13, 2010 · Viewed 30.8k times · Source

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?

Answer

Rudi picture Rudi · Oct 13, 2010

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.