Every now and then, you’ll make a mistake in your project’s Git tree. Probably not because you wanted to, but because you just haven't mastered Git yet. And that’s okay.
Maybe you needed to make a lot of commits because you forgot to remove comments in the code. Or you have a tight delivery of a very important feature, and you need to make sure everything is saved before signing out for the day. That’s okay. In the end, maybe you should have had fewer commits than you actually have, and you don’t know how to fix it. No problem! Git Squash is here to help you!
And this post will explore exactly that: what Git Squash is, why you should use it, some ways to perform it and a deeper explanation of two of those ways.
What Is Git Squash and Why Would I Use It?
Git Squash is a Git feature that allows a dev to simplify the Git tree by merging sequential commits into one another. Basically, you start by choosing a base commit and merging all changes from the next commits into this one. This essentially makes it the same as having all the changes you made in several commits in just one commit—the base commit.
Git Squash can be used with a simple merge to simplify your project’s Git tree. A little bit of context: on many occasions, I would not use Git tags in a specific repository to tag software versions. A commit in the default branch would itself be considered a tag for a new version. Git Squash can help here by substituting the whole feature branch into a single big commit in the default branch, thereby signaling a new version in the software.
But just merging the branch will be expensive and will make your tree messy. What if you could just merge one big commit with all those changes? You use Git Squash to make it happen and then you merge it back to your default branch. This way, you’ll achieve a simple and very straightforward tree. And if you need to roll back a version, you can easily do so by just resetting that squashed commit.
The Ways of Achieving Git Squash
Let’s discuss the ways you can achieve a Git Squash.
There are actually a few ways to get there. The easiest one is to take advantage of Git repository servers like GitHub that typically have this built in within the pull/merge request feature. Usually you just need to tick a box saying you want to squash or to choose squash merge strategy and you’re good to go. When it’s merged, your branch gets squashed. GUI tools like SourceTree or GitKraken offer a similar option.
To be honest, both repository servers and GUI tools are nice, since it is so simple to do there. But working in the terminal can make us feel powerful. (If you ask me, I would say I surely do!) And that’s why I’m going to explain step-by-step how to do a Git Squash in the terminal.
There are actually two options for terminal people like me.
The first one is to use the git merge command with the squash flag (two dashes there).
git merge --squash
And the second one is through an interactive rebase.
git rebase -i
The first option (merge) is very simple to perform. It’s clean and fast, but it gives you almost no control on what you want to do. Also, you will perform a merge—and that might not be what you want. The rebase option is usually considered to be the dangerous one, as you can lose commits or change everything in a way you didn’t intend. Despite this, it’s the one I prefer, and it gives you total control over the actions you need to perform.
As previously mentioned, I’m going to explain how to perform a Git Squash with all the options mentioned above except the UI one. Each UI tool has its own way of doing a squash; therefore, it’s not really within the scope of this post to go through them all. The pull/merge request approach is pretty much as already explained: you tick a box or choose the squash merge strategy, and when you merge that branch, it’s done. There’s no catch. Any repository server you use will handle it for you.
Git Merge Approach
The git merge approach is as follows:
git checkout <branch_name_into_which_you_want_to_merge>
git merge --squash <branch_name_to_be_squashed>
At this point, you might have to fix some conflicts. Do so.
Use git commit if you want to edit a pre-formatted message with all squashed commit messages. Or use
git commit -m “<your_commit_message”>if you want to override the pre-formatted message.
git push --force-with-lease
Interactive Rebase Approach
The interactive rebase approach goes like this:
git checkout <branch_name_to_be_squashed>
Check your Git tree, identify the first commit of the branch, and save its sha512 id. Or count from there to the last one of the branch and save the number of commits there are, including the first one.
If you went with the sha512 id:
git rebase -i <sha512_id>. If you went with the count:
git rebase -i HEAD~<count>(respect the spaces, as these are important).
Your configured text editor will open with the action to take to each sha512 id commit and its respective message. All commits are organized from older to newer, top-down. There’s also a commented message (# in the beginning of the line) on how to proceed for each commit. You can do a lot of stuff with them, but we’ll focus on the squash action.
Keep in mind that you need at least one commit to be picked before the one you want to squash in order to be able to do so, which means you can’t choose to squash the first one. Every commit you squash will be squashed into the previous one that was executed. So, for example, if you squash the second one, it goes into the first one.
Pick your actions for each commit. I strongly advise against drops and deleting lines, which will discard that commit (unless you’re really sure on what you want to do), and changing the order of the commits, since they will be reapplied in a different position than they were created. Save the file and quit the text editor.
A new text editor will appear. In this one, you’ll need to write the messages for the new commits. This text editor will appear for each new commit you have and will show the messages of all squashed commits into that one and the message for that one. You can either choose one of those or write one of your own. Again, save the file and quit the text editor.
At this point, you might get a message saying the rebase was successful. If so, the only thing left is to run
git push --force-with-leaseand the squash is finished.
Git Squash is a pretty powerful tool inside Git. It allows you to basically pick up a bunch of commits and just squash them together into a big commit.
At first, it seems like nonsense, as we’re often told and taught that small atomic commits with clear commit messages seem to be the best way of going along. But there also comes a time when the so-called “better ways” are not exactly the best ways of going forward.
When that happens to be the case, just remember that Git Squash is your friend and is here to be of service when called out. But hey, remember to take it easy and don't rely on Git Squash too much. If you're treating your new friend too harshly and they get angry, they’ll take the first opportunity you’re distracted to destroy you and your life’s work. At that point, standing still is probably your best option. (Or perhaps searching for answers in another post related to Git reset? If I had to guess? Probably.)