Evan Coury – A Simple Explanation of Git-rebase

What is Git Rebase?

Git rebase organizes branching paths and makes the repository structure linear. It allows you to patch changes onto your origin or master branch.

In December of 2011, Evan Coury posted an article on his now-defunct blog (original link is a 404) titled “A simple explanation of git-rebase,” and his Twitter post still comes up in searches. I really like this post for its clarity. In Evan’s own words:

…this post was mostly meant to help you feel confident that you really understand what it is that git-rebase is doing, why git-push won’t work properly after a rebase if you’ve already pushed any of the rebased commits, and most importantly, how to avoid one of the most common problems I’ve seen with people using git-rebase…

Its main virtue is that it does a great job of explaining the concept without over-complicating it. It’s not a how-to. It’s specifically written to help people who are already trying to learn git-rebase understand the concept. I’ve sent out a link to it in the Internet Archive to people learning git-rebase plenty of times already, so I thought I’d republish it here, and hopefully give it a new life until and unless Evan decides to bring back blog.evan.pro. What follows is taken verbatim from the original post:

Posted by Evan Coury on December 2, 2011

Using git-rebase without actually understanding what it does can cause headaches for you and the developers you’re working with. I don’t like headaches, so let’s take a minute to review what exactly git-rebase does.

Let’s say master is at commit C, and you create a branch feature/my-topic. So you have two branches pointed at the same commit like this:

          feature/my-topic
         /
A---B---C master

Next, you make two commits D and E in feature/my-topic and push it to your origin (we’ll say GitHub):

           D---E feature/my-topic
         / 
A---B---C master

Meanwhile, let’s say master also gets two commits, D' and E':

           D---E feature/my-topic
         / 
A---B---C---D'---E' master

So now you want to rebase your feature/my-topic branch onto master, so you run git rebase master. The result is this:

                    F---G feature/my-topic
                  / 
A---B---C---D'---E' master

The key here is to recognize that commits D and E from feature/my-topic no longer exist, and have been re-written as F and G respectively, with master’s HEAD (E') as the new base. This means that some history has been re-written. Because of this, if you were to try running git push origin feature/my-topic, you would be greeted with a non-fast-forward error. Instead, if you really need to push the rebased branch up to your origin, you’ll need to run git push -f (see note below).

DO NOT PULL, MERGE, OR REBASE FROM origin/feature/my-topic AT THIS POINT!!!

Why not? Because Git simply thinks, “Oh look, origin/feature/my-topic has two commits (D and E) that we don’t have here locally in feature/my-topic. Let’s merge them in!”… That’s a problem though, because we do have those commits, but their hashes were re-written to F and G when we ran git rebase.

So what would happen if you actually did merge? Your history would look something like this mess:

                    D---E---X---F--G feature/my-topic
                  / 
A---B---C---D'---E' master

In the above result, D is a duplicate of F, E is a duplicate of G, and X is a useless merge commit gluing the whole mess together. Using git-rebase may avoid the merge commit, but you’ll still have those pesky duplicates, so you’re no better off.

NOTE: If there are other developers deriving work from your feature/my-topic branch that is pushed; you must be careful about performing a git push -f, as it can cause problems for those other developers. Also, git push -f can be a somewhat destructive operation if used carelessly. In the context I described, it should be pretty safe, but unless you have a specific reason, it’s generally better to just leave it to the person merging your pull request to perform the rebase locally on their end. This results in master being able to simply fast-forward to the new commit (guaranteed no merge conflicts when merging into master) and no re-written history is forcefully pushed to any public repositories.

Questions

If you have any questions, feel free to ask in a comment below or contact our team directly.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top