Failure is inevitable

In my last post, I alluded to combining commits before pushing them to the shared repository. The magic command that can make that happen is “rebase,” and I’m going to show you how I use it every day.

What is Rebase?

First, let’s get something out of the way: history cannot be changed. That’s true even in Git. While you will hear people say “rebase let’s you rewrite history!” that’s not really what it’s doing. You’ll see why soon.

What’s the point of rebase? Perhaps it’s best illustrated with, well, an illustration. It turns this:

Into this:

A standard ‘git rebase master’ will take your changes and re-play them on top of the current tip of the master branch.

A rebase is a bit like a merge, but a merge is more like this:

While a rebase is more like this:

In a best-case scenario, a ‘git merge’ and a ‘git rebase’ will yield the exact same results. When things have diverged though (such as when team members commit code to the shared master branch while you are committing locally), ‘git rebase’ will yield much cleaner results than ‘git merge,’ which could produce something like this (or worse):

My Workflow

I always do a ‘git rebase’ to prepare my local feature branches before I push them to the shared repository. Here’s my workflow:

When I commit a feature, my goal is to make the changes I made easy to review and comprehend for someone else. In situations where I’ve made multiple commits in my feature branch, I’ll usually do an interactive rebase next to combine commits:

Assuming you are using Git Extensions, the ‘git rebase master -i' command will display a dialog to let you alter your commit history:

These commands will turn this:

Into this:

This cuts down on noise in the main repository and makes the change easier for someone else to review and comprehend. That said, there are times where I will choose not squash commits, such as when I’m making a simple refactoring in one of my commits. That refactoring is easier to review on its own without my real changes mixed in.

You Can’t Really Change The Past

Rebase allows you to create new history; an alternate timeline, if you will. The original timeline is still accessible for a bit. It’s hanging out there, “detached,” like this:

Eventually it will be garbage-collected and destroyed unless you attach a branch to it, but it’s there long enough for you to undo a bad rebase should you screw something up. To do that, just run the ‘git reset --HARD ORIG_HEAD’ command.

If it’s been a while since you did the rebase, you might have to dip in to the reflog. That’s beyond the scope of this article, but it’s not nearly as daunting to recover your missing changes as you might think.

The ‘git rebase’ command alone is worth the cost of switching to git. It is powerful though, and powerful things can bring pain. Use it wisely.

About Matt Honeycutt...

Matt Honeycutt is a software architect specializing in ASP.NET web applications, particularly ASP.NET MVC. He has over a decade of experience in building (and testing!) web applications.
He’s an avid practitioner of Test-Driven Development, creating both the SpecsFor and SpecsFor.Mvc frameworks.

He's also an author for Pluralsight,
where he publishes courses on everything from web applications to testing!