Squashing Git Commits Using 'git rebase'

There's many times when collaborating on Git that you may need to combine, or "squash" multiple commits into a single commit.

For example, the repository I'm currently working on at my internship with BuzzFeed has 8,500+ commits and hundreds of open pull requests at any given time. If these PRs did not squash their commits, the repo's changelog would be incredibly difficult to keep track of due to the insane amount of commits it would be tracking.

This post is going to walk you through a simple Git repository rebase.

Git Configuration

First, be sure that Git is configured to use the editor you typically work in. I use Sublime Text, so I've set my core.editor configuration to subl, as seen here:

$ git config --global core.editor "subl -w"

More importantly, you'll see I've added the -w flag to this argument, which is a command line argument that tells Sublime to wait for the files to be closed before returning. Without this flag, Sublime will immediately return after it opens a temporary file, which would result in the rebase command finishing before you've had a chance to change anything.

Rebasing

Note: Keep in mind that when you use the rebase command you are literally rewriting your branch's history. Use this command wisely to avoid accidentally losing commits.

To squash your commits, you'll want to use the following command:

$ git rebase -i HEAD~X
where X is the number of commits to squash.

The -i flag runs the command in interactive mode, which allows us to modify individual commits instead of blindly squashing them. If you're unsure of how many commits you've made or want to squash, you can always use $ git log to get a history of all of the commits in your current branch.

For example, if I want to squash my project's last three commits into a single commit before pushing it to my master branch, I would run the following:

$ git rebase -i HEAD~3

This command will open a temporary file in your editor that will look something like this:

pick 7f35639 did some lot
pick 7f6f5be got rid of stuff
pick 3aff294 update and fix other stuff

# Rebase 93eedd4..3aff294 onto 93eedd4
#
# 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
#
#

To squash the commits, simply change pick in the second and third lines to squash, like so:

pick 7f35639 did some lot
squash 7f6f5be got rid of stuff
squash 3aff294 update and fix other stuff

Once you save and close that file Git will automatically open another temporary file that will look something like this:

# This is a combination of 3 commits.
# The first commit's message is:
did a lot

# This is the 2nd commit message:

got rid of stuff

# This is the 3rd commit message:

update and fix other stuff

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# rebase in progress; onto 93eedd4
# You are currently editing a commit while rebasing branch 'master' on '93eedd4'.

Here you can edit/remove the uncommented lines to create your new commit message. If you decide not to change anything and save the file as is, all three of your commit messages will be contained in your new squashed commit.

Save and close that file after making your changes. You can use $ git log to see how your commit history has changed.

Pushing Changes

Now that you've rebased, you can push your changes to a remote using the following:

$ git push origin <branch> where <branch> is the name of the branch you want to commit to.

It's likely that you'll need to use the -f flag to force this push because your remote and local branches have different commit histories and won't automatically merge.

For example, in order to push my squashed commit to my master branch I would use the following:

$ git push -f origin master

Your repository on Github will now reflect the changes you've made and you'll be able to see your squashed commit.

Concluding Thoughts

For a more in-depth understanding of Git's rebase command, be sure to check out Atlassian's tutorial on rewriting history. You can also read Git's official documentation of the command here.

If you enjoyed this post, be sure to reach out to me on Twitter @brodan_ to stay up to date with my blog posts. Thanks for reading!