How You Can Clean Up Your Commit History with Git Rebase
We’ve all been there — you’re finally done working on a feature, but something about your commit history just makes you cringe.
It might be the typos in your commit messages, the “garbage” commits you made to just save your work, or the 3 commits you made to simply remove logging code.
Maybe your commits aren’t that bad… but if looking at your commit history brings you any amount of shame, you could always interactive rebase to rewrite and edit commits. With interactive rebase, you can amend commits, rename poor commit messages, and meld commits together.
Before we get into interactive rebasing, let’s talk about plain old git rebase:
$ git rebase master <feature-branch>
A rebase solves the same problem as a merge — we integrate one branch into another. A key difference is that rebasing re-writes commit history, commit by commit. Merging will integrate one branch into another using an additional commit, called a merge commit. No history is re-written with a git merge.
Take this feature branch, for example. It branched off of master at commit 4d63, and we’re now ready to merge it back into master.
After merging one feature branch into another, commits will appear in chronological order, regardless of the branch they belong to. So, the result of merging the “feature” branch into “master” above will be:
This could make master’s commit history very difficult to read — developers may not easily be able to distinguish which commits belong to each feature without scrolling through the history.
It’d be nice if we could group commits by branch, so that all commits from “feature” appear at the HEAD of master.
That’s where rebasing comes in — the result of running
$ git rebase master feature
Congrats! You’ve now grouped your atrocious commits and put them at the HEAD of master.
But that doesn’t make your commit messages any prettier. To go through commit by commit and edit history as you integrate branches, use interactive rebase:
$ git checkout feature
$ git rebase -i master
Run that, and you’ll end up with a file that looks something like this:
pick 6n45 Commit message #1
pick 7y32 Commit message #2
pick 3b34 Commit message #3
This file lists commits from feature that will be moved to master. We specify what to do with each commit (“pick” is the default, which will simply apply the commits to master without editing them). There are some tools we can use other than pick which will let us rewrite history:
- “edit” will allow you to ammend the commit
- “reword” will allow us to rephrase a commit message
- “squash” will merge a commit into the previous one
- “fixup” will behave the same way as squash, but discard the commit’s message
pick 6n45 Commit message #1
reword 7y32 Commit message #2
squash 3b34 Commit message #3
Will merge commit 3 into commit 2, and then allow you to rephrase commit #2.
Commit 1 will not be changed.
This is the power of interactive rebasing . We can meld, edit, and re-write commit history to be cleaner - and push all feature commits to the HEAD of master.
I’ve said it before and I’ll say it again — rebasing will rewrite git history! So use with caution — introducing an error in an earlier commit (using edit, for example) will cause all following commits to be erroneous as well.
This article is intended to inform you about what can be done with rebasing, but only scratches the surface — you should go through the docs to see some more detailed examples! Happy rebasing.