0

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?

Screenshot of git rebase -i -r

1 Answers1

1

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:

  1. get into detached HEAD state at commit o, with git checkout or git switch --detach;
  2. prepare the merge commit message for new commit AB in /tmp/msgfile;
  3. run git merge --ff-only $(git commit-tree -p HEAD -p branch2 -F /tmp/msgfile branch1^{tree}) to make commit AB as a merge commit and fast-forward to it;
  4. run git checkout -B branch1 or git switch -C branch1 to make the name branch1 point to commit AB.

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.

torek
  • 448,244
  • 59
  • 642
  • 775