Using Git to Squash Commits with Confidence
Table of Contents
Photo by Johnny Brown on Unsplash
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 logwill sort newest to oldest
- But a list of commits in
git rebase -i masteris oldest to newest
To squash commits, use
git rebase -i master in a feature branch.
pickthe oldest commit
- Mark all other commits with the word
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
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
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
--interactiveflag let’s us
squashcommits or perform other edits.
masterargument 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:
ito make edits
- when you’re done editing, press
- when you’re ready to save and go to the next step, press
Last step is to edit the final commit message for the commit. Edit the commit message or don’t. When you’re done,