6
commit b0e5db36ed68d4562275adeb08001b1316a4da52
Merge: ea38baa 8220bb1

commit ea38baa3f46a48722987b8dd3892d2b8d81c4d1a

In this case how do I squash these two commits

I am using

git rebase -i HEAD~2

but this doesn't work as it removes the Merge commit and that is not available for squashing

jessehouwing
  • 106,458
  • 22
  • 256
  • 341
Soumya Mohapatra
  • 85
  • 1
  • 1
  • 5
  • 'Please Help.' Everytime I read this I got something going on in my throat... I flag this question as a duplicate: https://stackoverflow.com/questions/30136558/git-how-to-squash-commits-which-have-merge-commit-in-between – ckruczek Dec 19 '17 at 13:46
  • Possible duplicate of [Git: How to squash commits which have merge-commit in between?](https://stackoverflow.com/questions/30136558/git-how-to-squash-commits-which-have-merge-commit-in-between) – ckruczek Dec 19 '17 at 13:46
  • Clearly *not* a duplicate of "How to squash commits which have merge-commit in between", because this question is about how to squash *with the merge commit itself*. (To be clear: the same *answer* might work, but it's not the same question and someone searching for this isn't likely to find the other question and think it's the solution.) – Mark Adelsberger Dec 19 '17 at 14:44
  • I'd say "dont't", but maybe if you tell why do you need there is something else you should do – max630 Dec 19 '17 at 17:22

2 Answers2

11

So two factors to consider here:

First, in a few ways rebase doesn't always play nice with merge commits. I'll come back to that a couple times.

Second, it's not entirely clear what result you're expecting. If right now you have something like

x -- x -- M -- C <--(master)
 \       /
  A --- B

are you trying to end up with

x -- x -- ABC <--(master)

or

x -- x -- MC <--(master)
 \       /
  A --- B

If you want the version with ABC, it's pretty simple. While M doesn't appear in the TODO list of a rebase, all of the commits that were brought into the mainline by M (i.e. A and B in this example) are. So just mark B and C for "squash". The only thing to remember is that this is a history rewrite, so if you have already pushed any refs that can reach A, B, M, or C then some clean-up may be needed (see the rebase docs under "recovering from upstream rebase").

If you want the version with MC, then there are a lot of problems. Not that you can't get it, but I don't think you can get it by doing a rebase; and also, MC would be an "evil merge", which could cause problems with future rebase attempts as well (among other things).

By default rebase tries to produce a linear history and won't produce merge commits. You can get it to produce merge commits with the --preserve-merges option, but this doesn't interact well with -i (and if you were to try to modify a merge by doing it, I anticipate several possible problems).

If you're not worried about the problems of hiding changes in a merge commit, and really want to produce a commit like MC, then the way to do it is:

First, move the master ref back to M while keeping the changes from C in the index.

git checkout master
git reset --soft HEAD^

Then re-apply the changes directly to the merge commit

git commit --amend
Mark Adelsberger
  • 42,148
  • 4
  • 35
  • 52
3

As the accepted answer says, git rebase -i (aka --interactive) supports an additional option -p (aka --preserve-merges). However, unlike the accepted answer, I wanted to point out that this actually seems to work fine to achieve exactly the desired result, so that if you do git rebase -i -p HEAD~2 the merge commit appears in the interactive rebase file:

pick abcdeff Merge branch 'feature' into develop
pick abcdefc Extra changes

Now you can edit this rebase file to mark the second commit for squashing and finish the rebase. It all seems to work fine.

However, I do not understand the intricacies of why the git documentation warns against using using -i -p (and in some, but not all, versions of the documentation says that -p is deprecated) so your mileage may vary. It seems that the main problems come when trying to re-order the commits displayed when using this... so don't do that!

The recommended alternative to the deprecated -p option seems to be git rebase -i -r; but that produces a very long and complex interactive rebase file which looks nothing at all like what I am expecting (i.e. the simple rebase file shown as text above).

If anybody can add an answer explaining in any detail whether and why git rebase -i -p is (or isn't) okay in this case, that would be very helpful.

MikeBeaton
  • 3,314
  • 4
  • 36
  • 45