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

Notice how a rebase generates a new commit with a new hash (84d1f8 in the example above). 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 Z 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 word 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 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.
Ethan on July 31, 2019 at 17:06
This is very helpful.

Thanks so much!
Meag on August 13, 2019 at 21:19
Thanks
Anant on August 17, 2019 at 17:16
Terrific article!
General question about Git. If not already there, why is there no option to merge the latest commit from the feature branch to the mainline. The advantage I see is that all my N commits won't pollute the mainline; at the same time, I have access to the commits in my feature branch, which is lost in rebase/squash.
Patrik Jonsson on August 20, 2019 at 15:44
First article of many that did a great job of explaining this. Well done!
Triangles on August 21, 2019 at 10:11
@Anant good idea, I don't think there's such a command on Git (yet?). You could create a "temporary" branch out of our feature one, squash it and merge it into the mainline...
Sourabh on August 22, 2019 at 10:04
@Anant,
There is indeed a way to do that.
You can cherry-pick the latest commit from the feature branch and apply it to the mainline(master).
In case you are not authorized to push directly to master, you can create a new branch, cherry-pick the latest commit from your feature branch to the newly created branch, and then raise a pull request of your newly-created branch.

Command -
[newly-created-branch] git cherry-pick
Suvradeep on August 27, 2019 at 08:30
I've been struggling with git interactive rebase for quite a long. I was able to do it somehow but everytime some problem remains and it was not upto my satisfaction. Your post really walked me through neatly and I've made a first clean git interactive rebase. Thanks a lot mate... :)
Victor on September 12, 2019 at 15:14
Thank you! Best explanation of git -i rebase I have read so far!
Philip on September 23, 2019 at 18:21
Thank you for this nice and clean tutorial as others have stated
Trung on September 30, 2019 at 04:23
Thank you. It is good document with git rebase
Elson on October 01, 2019 at 21:40
I wish all the tutorials online could be written like this. Very easy to follow and understand. Good job!
Mak on October 03, 2019 at 02:58
Excellent article. Not found such a clear explanation so far. Thank you so much.
Elwee on October 04, 2019 at 07:40
This is a very useful guide. Thanks! It helps a lot to clean up a local git repo before pushing to remote. Kudos!
Krishnamurti on October 08, 2019 at 09:41
Clear explanation, thanks.
Abhi on October 10, 2019 at 00:56
Typo in the command git rebase --interactive HEAD~[7]
You forgot to remove the brackets :)

Great step by step instructions though, it helped. Thanks.
Dibs on October 10, 2019 at 20:00
Best guide out there :)
Thanks a lot, you saved me a lot of time
Yan on October 17, 2019 at 17:08
Thx for a clear explanation.

I want to notice though that if you do it by hash, you should select the hash of the commit before the first one. When I used the hash of the first one, it was left out and everything after got squashed.
QuachGia on October 28, 2019 at 10:17
need "git push -f " if wan't to change origin log
Prasanna Hegde on November 07, 2019 at 11:51
Very helpful. You can add as the last step to force push to remote branch (git push --force). This will be required when you've pushed your previous commits into remote.
MRC on November 11, 2019 at 04:35
This guide is helpful, thank you.

FYI, I notice a small typo:
"because I want to combine the last seven commits into one, and d94e78 Prepare the workbench for feature X is the seventh one"
-> "feature X" should be "feature Z"
Ggicci on November 12, 2019 at 08:31
The most comprehensible article introducing "git squash" I've ever read!
Triangles on November 13, 2019 at 11:45
@MRC typo fixed. Thanks!
slunker on November 13, 2019 at 11:52
The best explanation I've ever read. Thanks!
Filipe Bezerra on November 17, 2019 at 00:42
Wow, that's pretty awesome. Congrats man
Tom Early on November 20, 2019 at 11:35
Worked a treat. Better than the other articles I read!
DrLightman on November 26, 2019 at 04:16
Thanks, amazing guide!
But as others mentioned, this needs to be fixed, square brackets removed:
git rebase --interactive HEAD~[7]
>>
git rebase --interactive HEAD~7
Elton King on November 28, 2019 at 06:08
Nice article. Very well written.
Triangles on November 28, 2019 at 16:03
@DrLightman thanks, fixed!
Shivakumar on December 04, 2019 at 13:48
Very handy for beginners
Dimitris on December 05, 2019 at 14:28
Thank you!
Giridhar on December 10, 2019 at 09:46
Thank You
rajQSL on December 21, 2019 at 02:28
good info. best squash instructions I have seen.
Carlos on December 27, 2019 at 00:11
This great! All the important details that one would have to know.
Mary on December 30, 2019 at 11:39
Thanks, great and clear explanation!
Jerin on December 31, 2019 at 12:41
Detailed and easy to understand. Thank you !!
Amr on January 08, 2020 at 15:07
Thanks. Really easy to understand
Ml on January 13, 2020 at 00:54
Thanks
Mori on January 13, 2020 at 01:43
Great write-up! Would be nice to include the result of "git log" at the very end.
Martin on January 17, 2020 at 15:16
Super clear and easy! Thanks of Spain XD
another Carlos on January 17, 2020 at 17:29
Solid explanation, thanks!
Jörg on January 22, 2020 at 23:07
Really really good tutorial. Thanks a lot!
Rohan Dsouza on January 25, 2020 at 14:29
Nice explanation. Now how do I push the local squashed branch to the remote branch that already exists. git push will give an error as there is no tracking of commits anymore as multiple commits were squashed. Should I do a git push -f ?
JJ on January 29, 2020 at 11:34
Very good article
Triangles on February 02, 2020 at 12:26
@Rohan Dsouza exactly, git push --force is the way to go!
Michael Milette on February 05, 2020 at 20:00
If you don't want to keep all of the commit messages in between, you can use the f (fixup) instead. It does the same thing as s (squash) but discards the commit log message.
kayut on February 18, 2020 at 11:03
Thanks for the article. I think I found a typo. You are saying that your newer commit is 84d1f8 Feature Z. However, looking at the result of git log, your newer commit is 871adf Feature Z.
yykk on February 20, 2020 at 02:53
thanks. very clear tutorial
Miguel on February 26, 2020 at 14:41
Thanks, very good explanation!
Triangles on March 07, 2020 at 10:49
@kayut good point: actually Feature Z is the result of a git rebase, which creates a new commit. I will update the article, thanks!
anonymous on March 23, 2020 at 12:10
Fantastic tutorial
hana on March 31, 2020 at 07:04
Thanks for the blog. It really helped!
Diego Machado Dias on April 13, 2020 at 20:08
Fantastic. Thanks for clear tutorial!
Christian K. on May 04, 2020 at 13:05
Many thanks for this helpful article!
sathiya baskar on May 12, 2020 at 15:12
nice explanation... working to me... tq bro

Jerome on May 19, 2020 at 10:07
Thank you for the very useful explanation !
Tiny typo : I think you meant "changing the _word_ pick into squash" (not "work")
Triangles on May 31, 2020 at 10:00
@Jerome thanks, typo fixed! :)
Adam Stankiewicz on June 01, 2020 at 21:49
I've written a tool for squashing all commits on some branch (it works just like "squash and merge" of GitHub): https://github.com/sheerun/git-squash
Tal on June 09, 2020 at 10:37
Very nice and clear, best squash tutorial I've seen yet :)
CicheR on June 17, 2020 at 17:20
If it's only about squashing the lasts N commits, maybe the easyest way is:
`git reset --soft HEAD~N`
That will remove the last N commits and leaves all the changes of the commits as staged changes.
Then, simply commit again:
`git commit -m"New commit message"`
Habib De León on June 20, 2020 at 07:43
Thank you so much!
muhammadali on June 29, 2020 at 15:57
Thanks for this extremely helpful tutorial.
Jon Snores on July 01, 2020 at 14:39
For years I have not understood squashing! Every tutorial I met would say "the first commit" and I would be confused as to which one is the first!!!! That simple clarification you made, and the first that you even addressed the reverse order made this super simple to me after years of shame! Thank you!
Mehdi Griche on July 02, 2020 at 12:57
i had to run "git push -f origin" in order to complete the task, thanks for the effort <3
that dude on July 19, 2020 at 14:01
Warning: Read this tutorial CAREFULLY.
Note that in the last code block, the commit hash order is REVERSED. If you do not reverse your commits, all of your work will be DELETED FOREVER.
Sreeni on July 20, 2020 at 11:17
such a wonderful explanation. Thank you :)
Gabriel on July 22, 2020 at 11:42
Thanks for this explanation !
Joey on July 23, 2020 at 00:56
Neat and clean, great job. Thank you so much.
WinNgo on July 24, 2020 at 09:19
You save my life :)
Thanks!
Sakib on July 28, 2020 at 12:01
Very nicely and simply explained. Helpful :)
David Buyinza on July 28, 2020 at 21:48
I always refer learners to this page when it comes to squashing commits. You present it so well for a newbie.
Kari on July 29, 2020 at 11:15
Thank you so much, really helpful!
Amit on August 03, 2020 at 12:56
that's neat, thanks!
Andre Chiarelli on August 06, 2020 at 14:40
Excellent article!
John on August 14, 2020 at 14:03
Can you please add that you have to do a "git push -f" after the rebase? I did it without first and it just looked like a regular commit.
Priya on August 21, 2020 at 03:00
Thank you so much, Very Helpful. Even a beginner can do it, very explanatory thanks a lot
Clint on September 04, 2020 at 22:41
A fantastically written article. Your attention to detail and consistent referral back to your example made this very straightforward. Thank you for your efforts!
Nabin on September 09, 2020 at 21:00
Clear and concise explanation. Thanks mate. It was very helpful
Tom on September 10, 2020 at 12:14
Thank you, it's very helpful!
cuongpt on September 11, 2020 at 03:11
Perfect
Varun on September 14, 2020 at 02:15
Need to add one more step/command at the end
git push --force
Varun on September 14, 2020 at 02:18
Very nicely explained, just one more step/addition/command at the end would be
git push --force
Arun on September 17, 2020 at 06:58
Excellent work. Even beginners can understand this easily. I agree with John's comment, It should add.
Xtbl on September 17, 2020 at 16:01
Great article, best squash explanation I've found. Thanks!
Nacho on September 26, 2020 at 17:56
Awesome guide! Thanks!
Mohamed Abdulbaset on October 07, 2020 at 10:53
Thanks man, great article!
xcdaqwe on October 16, 2020 at 15:49
pick hash1 commitmessage1 (oldest)
pick hash2 commitmessage2
pick hash3 commitmessage3
pick hash4 commitmessage4 (newest)

how do I squash hash4 into hash1 in rebase --interactive mode?

Is this even possible?
James Howlett on October 19, 2020 at 16:24
Excellent guide. Thanks for taking the time to put it together.
Tammy on October 23, 2020 at 18:50
Very clear, descriptive and to the point. Thank you!
Nick on November 09, 2020 at 08:21
Good document :+1
Thanks
Ray on November 09, 2020 at 17:45
Perfect. Thanks.
Vadim on December 02, 2020 at 14:00
Thanks for your article, helped me!
Maja on December 17, 2020 at 10:29
Excellent explanation through clear steps, thank You!
Found a small typo in Step 3: "into first commit".
Triangles on December 21, 2020 at 09:49
Thank you @Maja, typo fixed!
Adam G on January 20, 2021 at 18:13
Thank you. This article was very clear and helped me today.
Sreenivas Sama on February 09, 2021 at 08:50
fabulously neat and elegantly explained... Kudos.
an on March 03, 2021 at 00:07
Excellent. Thank you.
sluge on March 25, 2021 at 05:12
Thanks, good!
massenzio on April 01, 2021 at 18:38
the best article on this topic, this is what i was searching for a long time. Thank you very much
Dhanesh on April 14, 2021 at 12:42
Great article! Thanks for help.
Kshitij on April 18, 2021 at 08:58
best thing was - I understood everything in one go. Sweet!
Royner Chavarria on May 19, 2021 at 22:11
Great article!! Thanks
andymnc on June 11, 2021 at 10:18
very good post, but you should also explain what's next. I mean, how to push after alla of that, why and what will happen to other people using that branch
VT on June 15, 2021 at 16:15
Great post. Very helpful. Thank you.
Andrey on June 24, 2021 at 18:37
Everything went well, but i still see all my commits in the history. Expecting to see only one
Karthi on July 01, 2021 at 19:16
Explanation is pretty clear. It helped me
Santiago on July 07, 2021 at 20:15
This post helped me a lot, squash is a powerful command within the "rebase --interactive" tool Git offers. Thanks for the explanation!
Thao Chau on July 12, 2021 at 11:25
Great! It's handy to me
Greg on July 28, 2021 at 15:08
Extremely helpful post, than you very much!
Ash on August 06, 2021 at 02:22
fantastic - just what i needed. thanks much.
Ardi on August 11, 2021 at 04:35
Thank you very much for this post! I learned so much because of it
harmeek on September 07, 2021 at 12:13
Thanks . very nice explained
mp on September 14, 2021 at 22:23
Thanks. Very crisp and nicely explained. :)
cookie on September 23, 2021 at 14:57
You saved my day bro, nice ;)
KK on September 24, 2021 at 15:53
Indeed !!
Viktor on October 20, 2021 at 10:28
Thanks! Saved as bookmark :)
krodriguez on November 19, 2021 at 09:00
Is it possible to squash all the commits into the latest one instead?
Marco on December 20, 2021 at 18:14
I only wanted to thank you for this article, I have come back to it every time I need to squash.
Leo on February 08, 2022 at 16:21
Your are a god at explaining things!!! Thanks!
SM on February 17, 2022 at 19:51
Nice article with clear examples. Thank you!
Zwakele on February 24, 2022 at 04:19
Best explanation I've come across, am sharing with my whole team. Thanks!!!
NDB on February 24, 2022 at 08:15
Precise and very beautifully explained. Thank you!
Nelu on February 24, 2022 at 15:12
Thanks! Nice and simple :)
Lokesh on March 18, 2022 at 14:10
Thank you so much. Such a precise,clean and Beautiful Explanation
Thai on June 17, 2022 at 23:54
Great explanation for Git rebase interactive and squash commits. I've dreaded this Git feature for a long time until now. Thanks a lot!
tristan on June 29, 2022 at 22:39
thank you man, very clear and easy to follow
Juliano K. on July 14, 2022 at 00:14
I would also suggest adding to the article that it does not finish unless you do "git push -f origin" as the last step. Seems like an essential missing part in my opinion.
PS on July 27, 2022 at 13:59
Thanks. This was helpful.
Michael Ou on August 17, 2022 at 06:03
Clear and Simple! Very good article!
Elkarni Bachar on August 25, 2022 at 15:39
Thanks man you truly saved me
Faris Abuali on September 14, 2022 at 16:04
Thanks for sharing! much informative
Davila on September 16, 2022 at 11:22
Great tutorial, really helpful
Joe on November 13, 2022 at 22:41
Really clear article. Helped a lot!
Santeri on December 22, 2022 at 10:33
ty
Michael Deyaso on March 03, 2023 at 14:35
Awesome article. Clear explanation. Hats off!
Hossam A. on May 05, 2023 at 15:42
Thanks for the great info. It was really helpful.
Robin Bastiaan on June 01, 2023 at 10:26
Thank you for your explanation. Very clear and concise. Using rebase makes my commit history more clean and ready for a code review.

One thing I need to always take into account tho, is that I should not do rebases when I already pushed or shared my code since I will be changing history and that can lead to all kinds of inconsistenties, merge conflicts and other headaches.
Rush on July 11, 2023 at 05:17
Good explanation!