How to Squash Multiple Commits Into One with Git Squash

This is an awesome feature of rebase that can be used in the interactive mode. To squash the last n commits into one, run the following command:

git rebase -i HEAD~n

That will open up a text-editor with something similar to the following:

pick commit_1
pick commit_2
pick commit_3
...
pick commit_n
# Bunch of comments

Leave the first commit alone, and change the rest of the picks to squash. Save and exit the editor.

So if you wanted to squash the last three commits, you’ll first run git rebase -i HEAD~3 and then you’ll want to edit your commits to look something like this:

pick dd661ba Commit 1
squash 71f5fee Commit 2
squash f4b4bf1 Commit 3

If you’ve already pushed to a remote before squashing your commits, you’ll have to push to the remote again, with the -f flag, otherwise git will throw an error at you.

It is strongly suggested that you read the information in the opened file as there are many things you can do.

More detail on Git Squash

One of the things that developers hear quite often regarding their pull requests is something like “That looks good to me, please squash and merge”. The fun part is that there is no such command like git squash (unless you create an alias to it). To squash pull request means commonly to compact all the commits in this request into one (rarely to other number) to make it more concise, readable and not to pollute main branch’s history. To achieve that developer needs to use interactive mode of Git Rebase command.

Quite often when you develop some new feature you end up with several intermittent commits in your history - you develop incrementally after all. That might be just some typos or steps to final solution. Most of the time there is no use in having all these commits in final public version of code, so it’s more beneficial to have all of them compacted into one, single and final.

So let’s assume you have following commit log in the branch you’d like to merge as part of pull request:

$ git log --pretty=oneline --abbrev-commit
30374054 Add Jupyter Notebook stub to Data Science Tools
8490f5fc Minor formatting and Punctuation changes
3233cb21 Prototype for Notebook page

Clearly we would prefer to have only one commit here, since there is no benefit in knowing what we started on writing and which typos we fixed there later, only final result is of importance.

So what we do is starting interactive rebase session from current HEAD (commit 30374054 ) to commit 3233cb21 , with intention to combine 3 latest commits into one:

$ git rebase -i HEAD~3

That will open an editor with something like following:

pick 3233cb21 Prototype for Notebook page
pick 8490f5fc Minor formatting and Punctuation changes
pick 30374054 Add Jupyter Notebook to Data Science Tools
# Rebase
#
# Commands:
#  p, pick = use commit
#  r, reword = use commit, but edit the commit message
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#  f, fixup = like "squash", but discard this commit's log message
#  x, exec = run command (the rest of the line) using shell
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out

As always, Git gives us very nice help message where you can see this squash option we are looking for.

Currently the instructions for interactive rebase say to pick every specified commit and preserve corresponding commit message. That is - don’t change anything. But we want to have only one commit in the end. Simply edit the text in you editor replacing pick with squash (or just s ) next yo every commit we want to get rid of and save/exit the editor. That might look like this:

s 3233cb21 Prototype for Notebook page
s 8490f5fc Minor formatting and Punctuation changes
pick 30374054 Add Jupyter Notebook to Data Science Tools

When you close your editor saving this changes it will be reopened right away suggesting to choose and reword commit messages. Something like

# This is a combination of 3 commits.
# The first commit's message is:
Prototype for Notebook page

# This is the 2nd commit message:

Minor formatting and Punctuation changes

# This is the 3rd commit message:

Add Jupyter Notebook to Data Science Tools

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.

At this point you can delete all the messages you don’t want to be included in the final commit version, reword them or just write commit message from scratch. Just remember that new version will include all the lines that are not starting with # character. Once again, save and exit your editor.

Your terminal now should show a success message including Successfully rebased and updated <branch name> and the git log should show nice and compacted history with only one commit. All intermediary commits are gone and we are ready to merge!

Warning about local and remote commit history mismatch

This operation is slightly dangerous if you have your branch already published in a remote repository - you are modifying commit history after all. So it’s best to do squash operation on local branch before you do push . Sometimes, it will be already pushed - how would you create pull request after all? In this case you’ll have to force the changes on remote branch after doing the squashing, since your local history and branch history in the remote repository are different:

$ git push origin +my-branch-name

Do your best to make sure you are the only one using this remote branch at this point, or you’ll make their life harder having history mismatch. But since squashing is usually done as the final operation on a branch before getting rid of it, it’s usually not so big of a concern.

4 Likes

i’ve tried everything to save and close the editor but nothing happens so far, CTRL+O,CTRL+X,CTRL+G, the only command that worked was CTRL+Z and it stopped the editor and once i tried $git -rebase --continue (due this was the recommended action) it said that it had a merge conflict with my first commit. So i need help

What editor was that?

As for the conflict, you will need to find it. does it not tell you where it is?
I personally use atom and it has a plugging that allows me to easily find the merging conflicts. You could try something similar with your editor of choice.

It’s Vim editor. You need press “i” to Insert words, “esc” back to normal mode, “:wq”, save and exit, “:q!” quit without save. For more tips ,you may go to google Vim basic.

It’s Vim editor. You need to know some basic commands to use it.

git rebase master
would rebase all your latest commits on the current branch from the master branch.
Merging the branch after giving this command would help to maintain the linear commit history