How to Squash Git Commits

If you have ever searched for how to squash multiple git commits into a single commit, you probably have come across something like this StackOverflow question where there are many different ways people have of accomplishing it.

After having to Google it and “re-learn” it every time I needed to squash commits, I finally decided to just create a bash function I can use to easily squash the last N commits without having to remember the specific git commands needed to do so.

Here is the line I add to my ~/.bashrc:

squash() { git reset --soft HEAD~$1; git commit --edit -m"$(git log --format=%B --reverse HEAD..HEAD@{1})"; }

To use it, you just do squash 2 or squash 3 to squash the last 2, 3, or however many commits you want to sqaush.

Let’s try it out:

First, create a repository.

Side note:

The squash function requires there be at least one commit already in your repository, so it would not work for example to squash ALL commits in a repository into a single commit.

This usually isn’t a problem because most repositories will have already been initialized with a README.md or have other pre-existing commits.

git init
touch README.md && git add . && git commit -m "Initial commit" 

Next, add a few commits

touch a && git add . && git commit -m "Added file a"
touch b && git add . && git commit -m "Added file b"
touch c && git add . && git commit -m "Added file c"

If we check the git log using git log we see something like this:

commit da1d340b4f1811a1f494567885004ef65e6029b3 (HEAD -> master)
Author: Brian Pursley <bpursley@cinlogic.com>
Date:   Tue Feb 18 10:08:41 2020 -0500

    Added file c

commit a60aaaa8ac2749e0cab856b44370b7f8a14beaaa
Author: Brian Pursley <bpursley@cinlogic.com>
Date:   Tue Feb 18 10:08:41 2020 -0500

    Added file b

commit de6dcdc06f58235b8e395faed77b5b95c53a064b
Author: Brian Pursley <bpursley@cinlogic.com>
Date:   Tue Feb 18 10:08:41 2020 -0500

    Added file a

commit 938e052e1f15002e005a1dff6462617d4df5ae30
Author: Brian Pursley <bpursley@cinlogic.com>
Date:   Tue Feb 18 10:08:35 2020 -0500

    Initial commit

Let’s say we want to squash our last three commits into a single commit, using the squash function we would juste execute:

squash 3

That will being up an editor showing the three commit messages, where you can make any changes you want for the new commit message:

Added file a

Added file b

Added file c

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# On branch master
# Changes to be committed:
# new file:   a
# new file:   b
# new file:   c
#

Now if you run git log again you will see our three commits have been combined into a new, single commit:

commit f720a1cbcbc5570c25654f7b1a361475dc44ad6d (HEAD -> master)
Author: Brian Pursley <bpursley@cinlogic.com>
Date:   Tue Feb 18 10:11:26 2020 -0500

    Added file a
    
    Added file b
    
    Added file c

commit 938e052e1f15002e005a1dff6462617d4df5ae30
Author: Brian Pursley <bpursley@cinlogic.com>
Date:   Tue Feb 18 10:08:35 2020 -0500

    Initial commit

One final note:

If you are squashing commits that have been previously pushed to a remote repository, you will need to use the -f flag to force push your changes back to the remote.

For this reason, this squash function is not really suitable for use on shared branches where multiple developers could be working.

This isn’t usually a problem if you are in the habit of doing your work in a separate branch and using pull requests or merging to master (or some other trunk branch). You can use this on your own separate branch before merging into a shared branch.