How to squash commits in Git
Today, I'd like to show you a quick tip that can help you during big git merges, future rebases or just to keep your branches and commit list cleaner. I'm a big fan of commiting often and with small changes. But, when inevitably merging to the main branch, I like to have only meaningful commits.
Let's say you created a feature branch based on the main branch and have many commits already, most of them still work in progress (the feature is still not fully implemented). If you use git log --pretty=oneline
you would have something similar to this:
2ac7ce3 (HEAD -> feature-branch) Finishes feature implementation
8e417e1 Fixes a bug
68e88a0 Adds another thing
eea6731 wip Adds something
bde8671 (main) #123 Feature XYZ
The latest four commits are part of the feature branch and are together they represent the final feature. So, before merging it to the main branch, if you'd like to combine those commits with a meaningful message, you can run:
git rebase -i HEAD~4
Which means you will rebase interactively (the -i flag) and want to start from the current commit (HEAD) until the fourth commit from that reference, which in this example is eea6731
.
That command will open up the list of commits with pick
in front of the hash, like this:
pick eea6731 wip Adds something
pick 68e88a0 Adds another thing
pick 8e417e1 Fixes a bug
pick 2ac7ce3 Finishes feature implementation
# Rebase bde8672..2ac7ce3 onto bde8672 (4 commands)
Now, you will need to select the commits you want to squash into one, always from the newest to the oldest. To do that, type "i" to go into "insert mode" and replace "pick" with "s":
pick eea6731 wip Adds something
s 68e88a0 Adds another thing
s 8e417e1 Fixes a bug
s 2ac7ce3 Finishes feature implementation
# Rebase bde8672..2ac7ce3 onto bde8672 (4 commands)
You need to leave at least one commit without "s" because that's where the other commits will "fall onto". After selecting the ones you want, type esc
and :wq
to save the changes and leave the editor. This will open the editor again, and this time you can change the commit messages. Since this is the commit that will end up landing on master, you can be explicit about which feature you are implementing here, with a change set or whatever you think is necessary:
# This is a combination of 4 commits.
# This is the 1st commit message:
wip Adds something
# This is the commit message #2:
Adds another thing
# This is the commit message #3:
Fixes a bug
# This is the commit message #4:
Finishes feature implementation
If you want to omit a few of the messages, you can just add a #
in front of the message. This will turn that message into a comment and won't show up in the commit list:
# This is a combination of 4 commits.
# This is the 1st commit message:
#wip Adds something
# This is the commit message #2:
#Adds another thing
# This is the commit message #3:
#Fixes a bug
# This is the commit message #4:
Implements Feature XYZ (issue #42)
* Adds A, B and C
* Removes X
* Fixes bug Y
When you finish, you can leave the editor the same you you did before (`esc` then :wq
) and you'll have finished squashing the commits. Now, when you use git log
you will notice that all the commits you squashed are gone, leaving only the ones you want with the message you want. And that's a wrap for today's post. See you on the next one 👋