Using Git to Squash Commits with Confidence
Table of Contents

Photo by Johnny Brown on Unsplash
TL;DR
For me, one big gotcha is that the chronilogical order of your commits matters. First thing is to note that git log
and git rebase -i master
will list your commits in an opposite order.
When reading from top-to-bottom:
- A list of commits in
git log
will sort newest to oldest - But a list of commits in
git rebase -i master
is oldest to newest
To squash commits, use git rebase -i master
in a feature branch.
- Always
pick
the oldest commit - Mark all other commits with the word
squash
The What and Why of Squashing Commits
In Git, squashing is a way to combine commits — newer commits you choose to squash
will meld into older commits. Doing this results in a cleaner commit history.
When I was at IBM working on Carbon Design System, we made it a rule that contributors and maintainers should use rebase
and should squash commits for every pull request.
The reason for this had to do with new releases of our npm packages.
Since each commit on master
triggers a new release, all commits in a pull request are squashed into one commit so that a single new release is triggered per pull request. The alternative would be triggering multiple granular releases per commit when new code is merged to master
.
Other teams I worked on in my career usually opted to standardize around a rebase
workflow to have the option to rollback commits in case anything bad leaks into master
. For what it’s worth, I don’t think any of the teams I’ve worked with ever rolled back commits on a project. And if they did, we were all using merge
so 🤷♀️
Bottom line: whether you’re mandated to use a rebase
workflow or not, there are valid justifications for it and against it.
How to Squash Commits
Let’s say I have a master
branch with some commits and I create a new branch called feature
. I write some code, commit my changes, and now I have some new commits on my feature
branch.
Doing git log
will list all the commits.
git log
# or for prettier git logs:
# git log --pretty=format:'%C(red)%d%Creset %C(yellow)%h%Creset %s' --graph --abbrev-commit
* (HEAD -> feature) 510a97b change 4
* 65677aa change 3
* 6929064 change 2
* 1914635 change 1
* (origin/master, master) a6334e3 change dog func
* 6a31c00 rewrite index.js
* 3d6a19c remove index.js
* f16edf4 first commit: add index.js
I can squash the commits on the feature
branch into one commit using this command:
git rebase -i master
- The
-i
or--interactive
flag let’s ussquash
commits or perform other edits. - The
master
argument tells git which branch to rebase against.
Anytime I want to squash commits I use git rebase -i master
because it gives me all the commits for my feature
branch no matter how many commits I make. It’s more common that I would want to see all the commits I made in my feature
branch all at once.
When you run git rebase -i master
, the default editor that is configured with your git will open with a list of commits and some options for how you can edit your commits. On my computer, vi
is the default editor and it looks like this:
pick 1914635 change 1
pick 6929064 change 2
pick 65677aa change 3
pick 510a97b change 4
...
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup <commit> = like "squash", but discard this commit's log message
# x, exec <command> = run command (the rest of the line) using shell
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# . create a merge commit using the original merge commit's
# . message (or the oneline, if no original merge commit was
# . specified). Use -c <commit> to reword the commit message.
Remember to always pick
the oldest commit and squash
all the other commits.
pick 1914635 change 1
squash 6929064 change 2
squash 65677aa change 3
squash 510a97b change 4
# or you can use p and s for pick and squash
# p 1914635 change 1
# s 6929064 change 2
# s 65677aa change 3
# s 510a97b change 4
This is the part that confused me when I started learning rebase.
Notice here that the list of commits are sorted opposite from oldest to newest?
This is different from git log
which will sort from newest to oldest when reading top to bottom.
Changing Your Default Editor for Git
Git may be configured to use vi
as the default editor.
You can change this to use vscode like this:
git config --global core.editor "code --wait"
For other options, check out this stackoverflow post and do a cmd + f
to search for your editor of choice.
Using Vi Editor
If for some reason you’re stuck with vi
, here’s how you use it:
- press
i
to make edits - when you’re done editing, press
esc
- when you’re ready to save and go to the next step, press
:wq
and thenenter
orreturn
Last step is to edit the final commit message for the commit. Edit the commit message or don’t. When you’re done, :wq
.