gitgit-mergegit-rebasegit-squashgit-rerere

Squashing first few dozens of git commits that contain merge commits


I have an existing repository that has root R, then a few dozens of commits including multiple merges, up to X, and then linear history up to Y. I'd like to squash everything from R to X into a single commit and force push it. How can I do it without a lot of effort involving re-resolving merges?

Alternatively, this problem could be phrased as changing the root commit from R to X and cutting off the graph before X.

Here is an illustration simplifying the commit graph:

R           ---- I want to squash from here...
|
A
|\
B C
| |
D E
| |\
F G H
| |/
I J
|\ \
K L M
|/  |
N  /
|/
O 
|
X           ---- to here.
|
P
|
Q
|
Y

Squashing everything with regular rebase would require re-resolving multiple merge commits. I am aware of git rerere, but I do not know how to use it in this situtation. It was not enabled when commiting all that.


Solution

  • This can be done with rebasing but it's much easier to do by hand

    git checkout --orphan temp X
    # now you are on a brand new branch with no history, and your working tree is just like X
    git commit -m "Single shot"
    # now let's carry over X up to Y
    git cherry-pick X..Y
    

    If you like the result, set your branch over here and live happily ever after.

    Update: If the history after D is complex, it might be better to run a rebase for the last step:

    git rebase --rebase-merges X Y --onto temp
    

    And if that history is complex and it includes merges with conflicts, I can offer this script to take care of that last step to avoid having to redo the merges with conflicts:

    https://github.com/eantoranz/git-replay