While working with Git version control system you have quite freedom about how you work with commits and branches. But there are universal practices you should know to keep your repository clean. How rebase can help you with this?

Introduction

Rebase is a Git strategy for synchronising work with base branch (main or any other branch) by moving commits from one place to another. It may look scary, but actually it’s pretty straightforward 😎. Let’s take a look at this in practice!

From my experience, most of the developers use merge strategy to synchronise work between branches. It is of course valid usage of Git, but it can quickly get really messy:

  • default merge commit messages are not helpful
  • you end up with spaghetti history 😵‍💫
Git history with merges

In my opinion, rebase is much better solution. It preserves linear history, which allows you to browse commits more effectively. The exact same code (as with merge strategy shown before) in both A and B branches could be achieved by rebasing both of them. History would look like this:

Git history with stacked and rebased branches

But how it should be done? Well, as always it depends 🙂. In the CLI you can rebase branch with simple git rebase <target_branch> command, in our example it would be like this:

$ git checkout A
$ git rebase main

In PHPStorm you can do it by right-clicking on target branch and choosing “Rebase X onto Y” option. This will execute the same CLI command under the hood 😉.

Git rebase in PHPStorm

Effectively you would get branch A on top of main branch. Commits’ content would be the same, but their hashes would be different!

Git rebase changes commits' hashes

Rebasing stacked branches

Things get a little bit more complicated when it comes to rebasing branch B. Since it was based on the branch A before A was rebased, now it contains its own commits (B1-B2) and commits from old branch A (A1-A2). But we need only B, how? --onto to the rescue 😁! First of all, you need SHA of last commit that is not part of your changes. This commit is a base of your work, now you need new base - that’s exactly why this is called rebase 😁. You can get SHA from git log, and when you have it, you just need to execute command:

$ git checkout B
$ git rebase --onto A <previous_base_hash>

In PHPStorm you can get base commit’s SHA by right-clicking it and selecting “Copy Revision Number” option (or using keyboard shortcut). Then you can open Git → Rebase from the menu, and you’ll get rebase modal. Under “Modify options” you’ll find --onto option, and when you select it, you’ll see form field for providing SHA of the old base. The last thing you must provide is a target branch (in our example: A).

Git rebase dialog in PHPStorm

As a side note: you can rebase branch in #Gitlab’s UI by using /rebase command in the comment 🙂. Worth to mention that such rebase:

  • ✅ does not remove approvals (because Gitlab knows that rebased code is exactly the same as before)
  • ❌ does not sign commits with your GPG key

Conflicts during rebase

There’s one thing: when you do git rebase you may encounter code conflicts. But if your branch is conflicted with target branch, you would get exact same conflict during merge, so in fact it does not change anything 🤷‍♂. The only difference is that sometimes during rebase you need to resolve the same conflicts multiple times. This can be really irritating 😩. But fortunately when you rebase often and keep your branch up-to-date, it does not happen much.

You can also enable rerere option in Git and try reusing recorded resolution 🙂.

$ git config --global rerere.enabled true

Other advantages

There are several other advanced Git rebase options that I won’t cover here in details, but here’s a glance what you can do during rebase:

  • --interactive mode allows you to change, reorder, reword, squash or remove commits during rebase. This means you can clean up your branch from time to time when you’re advancing with your work. This makes rebase even more important in terms of readability of Git history. In PHPStorm you can find this option in the Rebase dialog and then you can visually choose what to do with each commit. It’s really useful and comfortable!
  • you can preserve merge commits if needed, which means you can rebase even more complex branches, keeping their history intact.

Summary

So let’s sum it up! Git rebase is intended for keeping linear commit history and is an alternative to merge strategy. Rebase rewrites history because rebased commits have different SHAs, so it should be used mostly on development stage.

I really suggest you to read Git rebase documentation and give rebase a try 😁. I swear it’s not hard, it just requires some kind of mental switch 😉.

When you start using it, suddenly it becomes your best friend in Git.