Like it!

Join us on Facebook!

Like it!

Squash commits into one with Git

A nice way to group some changes together, especially before sharing them with others.

In Git you can merge several commits into one with the powerful interactive rebase. It's a handy tool I use quite often; I usually tidy up my working space by grouping together several small intermediate commits into a single lump to push upstream.

Step 1: choose your starting commit

The first thing to do is to invoke git to start an interactive rebase session:

git rebase --interactive HEAD~[N]

Or, shorter:

git rebase -i HEAD~[N]

where N is the number of commits you want to join, starting from the most recent one. For example, this is a hypothetical list of commits taken from the git log command, while I'm working on a generic feature Z:

871adf OK, feature Z is fully implemented      --- newer commit
0c3317 Whoops, not yet...
87871a I'm ready!
643d0e Code cleanup
afb581 Fix this and that
4e9baa Cool implementation
d94e78 Prepare the workbench for feature Z
6394dc Feature Y                               --- older commit

And this is what I would like to do:

871adf OK, feature Z is fully implemented      --- newer commit --┐
0c3317 Whoops, not yet...                                         |
87871a I'm ready!                                                 |
643d0e Code cleanup                                               |-- Join these into one
afb581 Fix this and that                                          |
4e9baa Cool implementation                                        |
d94e78 Prepare the workbench for feature Z     -------------------┘
6394dc Feature Y                               --- older commit

Obtaining:

84d1f8 Feature Z                               --- newer commit (result of rebase)
6394dc Feature Y                               --- older commit

So in this case the command would be:

git rebase --interactive HEAD~[7]

because I want to combine the last seven commits into one, and d94e78 Prepare the workbench for feature X is the seventh one.

I have tons of commits to squash, do I have to count them one by one?

A downside of the git rebase --interactive HEAD~[N] command is that you have to guess the exact number of commits, by counting them one by one. Luckily, there is another way:

git rebase --interactive [commit-hash]

Where [commit-hash] is the hash of the commit just before the first one you want to rewrite from. So in my example the command would be:

git rebase --interactive 6394dc

Where 6394dc is Feature Y. You can read the whole thing as:

Merge all my commits on top of commit [commit-hash].

Way easier, isn't it?

Step 2: picking and squashing

At this point your editor of choice will pop up, showing the list of commits you want to merge. Note that it might be confusing at first, since they are displayed in a reverse order, where the older commit is on top. I've added --- older commit and --- newer commit to make it clear, you won't find those notes in the editor.

pick d94e78 Prepare the workbench for feature Z     --- older commit
pick 4e9baa Cool implementation 
pick afb581 Fix this and that  
pick 643d0e Code cleanup
pick 87871a I'm ready! 
pick 0c3317 Whoops, not yet... 
pick 871adf OK, feature Z is fully implemented      --- newer commit

[...]

Below the commit list there is a short comment (omitted in my example) which outlines all the operations available. You can do many smart tricks during an interactive rebase, let's stick with the basics for now though. Our task here is to mark all the commits as squashable, except the first/older one: it will be used as a starting point.

You mark a commit as squashable by changing the work pick into squash next to it (or s for brevity, as stated in the comments). The result would be:

pick d94e78 Prepare the workbench for feature Z     --- older commit
s 4e9baa Cool implementation 
s afb581 Fix this and that  
s 643d0e Code cleanup
s 87871a I'm ready! 
s 0c3317 Whoops, not yet... 
s 871adf OK, feature Z is fully implemented      --- newer commit

[...]

Save the file and close the editor.

Step 3: Create the new commit

You have just told Git to combine all seven commits into the the first commit in the list. It's now time to give it a name: your editor pops up again with a default message, made of the names of all the commits you have squashed.

You can leave it as it is and the commit message will result in a list of all the intermediate commits, as follows:

Prepare the workbench for feature Z
Cool implementation 
Fix this and that  
Code cleanup
I'm ready! 
Whoops, not yet... 
OK, feature Z is fully implemented

Usually I don't care to keep such information, so I wipe out the default message and use something more self-explanatory like Implemented feature Z.

Sources

Stackoverflow - Git rebase interactive the last n commits (link)
Stackoverflow - How can I merge two commits into one? (link)
Gitready - Squashing commits with rebase (link)

comments
surendhra on July 06, 2018 at 11:03
Neat and clean explanation ..Great job...Thanks for the article..
Aleksey on October 22, 2018 at 15:30
Very good article, thank You very much for detailed explanation! The only difference for me was: I needed to squash all into the first commit. in this case need to use --root argument:

git rebase -i --root

Also SmartGit performs very convenient UI Squash... command for commits.
Marie on October 24, 2018 at 05:06
Very well explained! Good job and thanks a lot!
Mihai on December 02, 2018 at 08:16
Excellent presentation of this topic. Thanks.
Micah on February 15, 2019 at 09:34
Thanks for this!
Ben on February 20, 2019 at 09:40
Thanks a lot for this tutorial, clean and very well explained.
Worked like a charm
CaffeinePwrdAl on March 06, 2019 at 15:20
Nicely done :) Thanks
Pernelius on March 14, 2019 at 11:14
clear and precise :) Ty
Vadim on March 28, 2019 at 04:53
Respect!
Volodymyr on May 02, 2019 at 22:10
Really nice article!
I did it with your help
Nakoon on May 06, 2019 at 10:13
Perfect! Thanks
Darshan Timbadia on May 16, 2019 at 19:41
Very well explained. Thanks very much!
Thanks so much on May 28, 2019 at 12:10
Great article.
Respect.
Amjad on June 01, 2019 at 10:14
Thanks for the simple and to the point explanation. After reading multiple resources on this topic, I finally get it!
g-dawg on July 03, 2019 at 10:35
Thanks for sharing.
Very good article on July 11, 2019 at 21:27
I'll just add that if you want to save and quit from editor you need to:
press escape
and type
":wq"
Shane on July 11, 2019 at 21:41
I had to `git push -f origin` after it was done squashing or else the remote wouldn't correctly squash if I just did a normal push/pull. It would just look like a merge and everything squashed would look like it was in a separate branch.
mike on July 12, 2019 at 01:18
Very helpful, very easy, thank you
Triangles on July 12, 2019 at 10:14
@Very good article guy: that's the VIM quit command and it works only if you are using VIM as the commit message editor, of course.
Feeerdo on July 18, 2019 at 15:13
God bless you for this squash guide :D :D
Sathya on July 19, 2019 at 13:04
Very simple and clear.