Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there an easy way to squash many commits in git?

I have many commits that need to be squashed (several thousand, we had a runaway script). Those commits are in groups between 40 to 200.

Using git rebase -i is not feasible since it would involve too much labor. We have a tool, that can output the first and last commit of such a group relative from the branch HEAD (which can be used to get the actual commit by its reference hash).

So as an example I'm looking for something that can squash HEAD~400 to HEAD~200 into a single commit. And can then be run again (with change arguments) to squash HEAD~100 to HEAD~50 into another single commit.

I have thought about about creating a "fake" editor, that essentially fakes the interactiveness, by performing the changes to the rebase file. An abstract example script would look like this (which I could loop until all groups have been squashed):

start=$('get start of oldest group')
end=$('get end of oldest group')
git config core.editor "'~/fakeeditor' -start $start -end $end"
git rebase -i
like image 250
Pingger Shikkoken Avatar asked Nov 02 '25 00:11

Pingger Shikkoken


1 Answers

My personal favorite is using git reset --soft.

So, say you want to squash from HEAD~1000 up to this point (HEAD~1000 being the last surviving commit that won't be squashed):

git reset --soft HEAD~1000
git commit -m "Squashed a lot of stuff"

That's it. You can use a revision ID instead of using HEAD~n references.

I see that you want to do it like by segments.... so, say.... let's squash HEAD~400 to HEAD~200... then from HEAD~200 to HEAD~100... then from HEAD~100 to HEAD. So, let's create a temp branch where we will do our work.

git checkout -b temp HEAD~200
git reset --soft HEAD~400
git commit -m "squashing first segment"
# next segment
git checkout the-original-branch~100
git reset --soft temp
git commit -m "Second segment"
git branch -f temp #set temp over here
# final segment
git checkout --detach the-original-branch
git reset --soft temp
git commit -m "Final segment"
git branch -f temp #set temp over here
# if you like the result, feel free to move whatever branch over here
git branch -f whatever-branch
# and delete temp if so you want
git branch -D temp

Very easy, I think.

After one day, I just realized (by answering another question) that it can be done in a much simpler way:

git branch -f temp $( git commit-tree -p HEAD~400 -m "first squash" HEAD~200^{tree} )
# that set up temp on the first squash
git branch -f temp $( git commit-tree -p temp -m "second squash" HEAD~100^{tree} )
# temp has move to second squash
git branch -f temp $( git commit-tree -p temp -m "final squash" HEAD^{tree} )

Now temp has the squashed commits the way it was requested in my example. Feel free to do a reset --hard over there (the usual warning when using reset --hard).

like image 171
eftshift0 Avatar answered Nov 04 '25 14:11

eftshift0