I have a commit and a merge commit after it. I want to squash the merge commit with the first commit on the same branch. Can someone explain how this can be done?
1 Answers
Merge commits cannot be squashed. The definition of "squashing" a commit starts with separate commits A
and B
that have a parent/child relationship such that A
is the parent and B
is the child:
...--o--A--B--C--...
(with older commits drawn towards the left, and newer ones towards the right).
We then replace the A-B
pair with a single combined commit AB
:
A--B--C--...
/
...--o--AB--C'--...
where the parent of AB
is the same as the parent of A
, but the snapshot of AB
is the same as the snapshot of B
. Because AB
has a different hash ID than both A
and B
, we are forced to replace all "downstream" commits, such as C
, with new commits C'
and so on. The original commit stream ...-A-B-C
continues to exist, but we stop using it in favor of the new-and-improved stream.
What makes a commit a merge commit is that it has not just one, but rather two or more parents. That is, we might have:
...--o--A--B <-- branch1 (HEAD)
/
C <-- branch2
Git is not currently capable of replacing B
with a merge AB
that (1) has the same parents (o
and C
) as A
and B
, but (2) the same snapshot as B
. What we would like to get here is:
...--o--AB <-- branch1 (HEAD)
/
C <-- branch2
Humans can do this, but currently Git has no command built into it to do this.
To do this manually:
- get into detached HEAD state at commit
o
, withgit checkout
orgit switch --detach
; - prepare the merge commit message for new commit
AB
in/tmp/msgfile
; - run
git merge --ff-only $(git commit-tree -p HEAD -p branch2 -F /tmp/msgfile branch1^{tree})
to make commitAB
as a merge commit and fast-forward to it; - run
git checkout -B branch1
orgit switch -C branch1
to make the namebranch1
point to commitAB
.
Note that this particular recipe assumes that there are no commits beyond B
on branch1
. If there are, the recipe gets more complex.
One last note: the result of doing this is likely to be what people call an evil merge. Be sure you understand this, and whether it is reasonable to do that with your particular project, co-workers, etc. Re-performing the merge, as git rebase -r
would do later, is likely to produce a different result: that of dropping commit A
entirely.

- 448,244
- 59
- 642
- 775