Squashing Commits


In the fourth novel of Douglas Adams' Hitchhiker's Guide to the Galaxy trilogySo Long and Thanks for All the Fish – there is a chapter in which the author humorously lays down the reasons for not describing everything that Arthur Dent does. Chapter 25. Read it. And while you're at it, read the rest of the novel, and the other four. Because Douglas Adams. Can. Write.

Douglas Adams' thesis is this: he could explain how Arthur Det brushes his teeth. He could explain how he rolls around in his sleep. But it doesn't advance the action. It's a level of minute detail that is completely irrelevant.

And so it is reading a rambling commit log in git. It's tedious. It's boring. And it's completely unnecessary. It doesn't advance the action.

Take for example this (made up) series of commits to a git repo.

Sorry, what? I fell asleep. Compare that to this commit message.

Ah. No teeth brushing or sleep rolling. Just advancing the history of the project.

Git is a communication tool, not a verbose record of every tedious change that occurs on a project. Keep it tight.

So how do we squash those commits? How do we turn our history from a rambling stream-of-consciousness tome into a sharp-witted novella?

I know of two ways. As this is git we're talking about, there are likely 40 ways I don't know about. But I know two.

The interactive rebase

The interactive rebase was the first method I learned for squashing commits, and still one I fall back on from time to time.

It's particularly useful when you want to rewrite commits on your current branch. So, for example, if you're humming along on master and suddenly realize you're producing more wordy cruft than you should be, whip out the interactive rebase and rewrite your history. (But as always, be sure not to rewrite published history.)

So how do you use this?

$ git rebase -i SOME_PREVIOUS_COMMIT

As the section header would suggest, that -i stands for "interactive". The command will drop you into an interactive editor where you can reorder commits, choose which commits you want to squash together, rewrite the message for commits, and leave some commits untouched.

When you exit the editor, the history gets rewritten to your will, and you're done. Boom. A readable history.

The squash merge

If you frequently use feature branches, the squash merge may be for you. A squash merge works like a normal merge: you're on the master branch, and you want to merge in changes from the feature/confirm-request branch.

$ git checkout master
$ git merge --squash feature/confirm-request

But the little --squash in there tells git that you don't actually want a merge commit. Instead, you want to take all of the commits in feature/confirm-request, squash them into a single commit, and put that commit on top of master. Essentially, your feature branch turns into a single commit.

If this is what you want – a single commit – then perfect. But the drawback compared to the interactive rebase is that the squash merge always results in a single commit, where the interactive rebase can result in a series of logical commits. It's a matter of using the right method at the right time.

Your flow

Git is only a tool. Your workflow is what matters. So I can't tell you what the best method to use is; try it out and see what works for you. But please do us all a favor and save us from the "how Arthur Dent brushes his teeth" commit. Make your history tight.