git interactive rebase squash into next commit
Vampire's answer is right, but I want to offer a different perspective. I think this is where you are getting yourself more wound up than necessary: you start with:
Lets say I want to keep commits 3, 4 and 7.
but then add:
- 1 and 2 go into 3.
- 4 stays
- 5 and 6 go into 7
But this means you want to keep (the contents of) all of 1, 2, 3, 4, 5, 6, and 7 ... just not as separate commits. The interactive rebase squash
does not mean "throw away", nor even "throw away commit message", it just means "combine". It might be better if the verb were "combine" or "meld" or "mix-in" or some such. So the sequence (pick, squash, squash) means: keep all three, while applying them in that order, then make one big commit out of them.
As already noted, once Git goes to make one big commit out of them, Git gives you another chance to edit the three combined commit messages into one big commit message.
When the rebase is done, you haven't kept any of the original commits. Instead, you have made new commits. It doesn't matter precisely how your new first commit was assembled from parts, only what the final source was, and what you put in the one big commit message.
You either need to also reorder the commits so the to-be-kept commit comes before the to-be-squashed commits if this is feasible.
If this is not feasible, because you then would get conflicts you don't want to resolve, just make it
1. pick
2. squash
3. squash
4. pick
5. pick
6. squash
7. squash
When the squashes are done, you can edit the commit message to contain the message you like the final commits to have. Easy as pie. :-)
You might even be able to do
1. pick
2. fixup
3. squash
4. pick
5. pick
6. fixup
7. squash
Then I think there should only once the commit message editor being fired up, as with fixup the previous commit message is simply taken without launching the editor.
On squash when the commit message editor fires, you also get both commit messages, the one from the to-be-squashed-into and the to-be-squashed commit, so you then can simply delete the commit message you don't want to keep.
It is indeed possible to squash a commit into the following commit during interactive rebase, and fully preserve the identity of the second of those commits (including author, date etc.).
The method is somewhat involved however, so native support by git rebase -i
would still be appreciated nevertheless.
I'll demonstrate with just three commits aaaaaaa A
, bbbbbbb B
and ccccccc C
, where we want to fold A
into B
and preserve B
's identity: (The method easily generalises to more commits)
git rebase -i aaaaaaa^
- Modify the edit script to stop after commit
B
and exit the editor:pick aaaaaaa A edit bbbbbbb B pick ccccccc C
- When rebase has stopped after
B
, revert the latest commit twice, then continue:
Because reverting the revert restores the previous state, all following commits will apply cleanly. The sequence ofgit revert HEAD git revert HEAD git rebase --continue
B
andRevert "B"
together has no effect, but the first of those commits carries the full identity of commit B (in fact, at this stage it still is commitB
).B
andRevert "B"
could be squashed together to form a no-op commit carrying the identity of commit B forward with a separaterebase -i --keep-empty aaaaaaa^
. This is very useful and recommended to prevent bogus merge conflicts, especially in more complex cases. However we'll skip that step here and just keep the commits together.
The important commit now isRevert "Revert "B""
which will carry all the changes originally made byB
. - Now
rebase -i aaaaaaa^
again to moveA
past bothB
andRevert "B"
, while squashingB
into bothRevert "B"
andA
:
Because the sequence ofpick bbbbbbb B squash b111111 Revert "B" squash a222222 A squash b333333 Revert "Revert "B"" pick c444444 C
B
andRevert "B"
has no effect, you can move any commits past it while creating only trivially resolvable merge conflicts. - When rebase stops with a merge conflict, use
git mergetool
with a tool that can automatically resolve trivial conflicts, and let the tools do just that. - Rebase will stop again to let you edit the commit message of the squashed commit. Edit to your taste.
git rebase --continue
and you are done.