Rebase for dummies: A Comparison with Merge

Have you ever tried rebase in a Git repo and failed so badly that you never wanted to use it again? Well, you’re not alone. There should be supporting groups out there (similar to the ones about “how to exit Vim?”, but that’s a story for another time), so the goal here is to help you fight that fear by understanding how it works and when to use it.

Git merge

First of all, let’s chat about merge. Merging must be the most used feature to combine branches. When we use this, we put forked branches (or histories) back together again. And to do so, Git replays all the changes made in the given branch since the moment the histories diverged, and incorporates those changes into the current branch. It also records the result in a new commit, usually called merge commit.

git checkout feature
git merge main

If there are conflicts, we’ll need to solve them once and at the end of the process. Once it finishes, you can safely push the result to the remote.

It’s easy to use, but sometimes, the output might be… chaotic since it does not represent a linear timeline. To visualize this, let’s take this example: we have a feature branch created off main, and we want to update with the latest changes from main, since there was another branch called anotherFeature which started simultaneously but is now merged.

When using merge, this is how it would look like if we merge main into feature:

And then if we finish working on this and merge it into main, this would be the outcome:

As you can see, this is what we call “non-linear history”. It doesn’t seem that chaotic in this example, but it can get easily worse when more branches are involved.

So here’s where rebase comes in, to generate a cleaner and sometimes more sophisticated way to tell the story of our code (that sounds so poetic!).

Git rebase

I like to think of rebase as a “Back to the Future” feature since it rewrites history. Seriously, it literally changes the base of a branch to another one (it rebases the branch). The result is a cleaner repository history since it avoids merging commits and makes the history sequential.

git checkout feature
git rebase main

But (there’s always a “but”), there are two important issues you’ll need to take care of:

  • Since it rewrites history, you’ll need to force-push the branch. So, it’s not recommended when someone else works on the same branch.
  • Since it creates a brand-new commit for each change, you might have to resolve conflicts multiple times.

The last part sounds scary, but it gets easier with practice. Also, Git is super intuitive about how to work with this. When you rebase, it will create a new commit for each in the given base, and if it finds conflicts, you’ll need to manually solve them (same thing as when using merge) and then just run:

git add .
git rebase --continue

The process will be repeated until the rebase finishes (based on the changes, you may also find scenarios when it just goes automatically without conflicts). You can abort the whole thing at any point with the following command:

git rebase --abort

Intuitive, right?

Going back to our previous example, let’s see how it would look using rebase instead of merge. Let’s start again with the same scenario:

And now, we rebase feature with main so it gets the latest changes:

It changed its base, so now feature‘s origin points to the end of main. If we now want to merge this into main, this is the result:

And there you go, a nice, clean, and linear history!

And the winner is… 🥁

As always, it depends. There’s no correct answer, and both are good merging strategies. The real answer is “Which fits better for your project?”. But in a general rule of thumb, when working in small teams rebase is generally more suitable, whereas merge works well when the team is composed of several people working on the same features.

The choice comes down to how transparent, easy to read, and accessible you want the logs to be.

Both rebase and merge are great tools for managing histories in a Git repository. While rebase offers a cleaner project history by avoiding unnecessary merge commits, merge preserves the history ending up in safer conditions when working with more people on the same branch. Also, it’s not about one or the other, but when and why to use each. That’s one of the most important things about Git: it’s powerful and gives you several tools to maintain version control of your code properly.

I encourage you to check the official documentation about merge and rebase. And, before you leave, which merging strategy do you prefer? Would you give rebase a chance?