When do you use Git rebase instead of Git merge?

Viewed 730201

When is it recommended to use Git rebase vs. Git merge?

Do I still need to merge after a successful rebase?

16 Answers

Short Version

  • Merge takes all the changes in one branch and merges them into another branch in one commit.
  • Rebase says I want the point at which I branched to move to a new starting point

So when do you use either one?

Merge

  • Let's say you have created a branch for the purpose of developing a single feature. When you want to bring those changes back to master, you probably want merge (you don't care about maintaining all of the interim commits).

Rebase

  • A second scenario would be if you started doing some development and then another developer made an unrelated change. You probably want to pull and then rebase to base your changes from the current version from the repository.

To complement my own answer mentioned by TSamper,

  • a rebase is quite often a good idea to do before a merge, because the idea is that you integrate in your branch Y the work of the branch B upon which you will merge.
    But again, before merging, you resolve any conflict in your branch (i.e.: "rebase", as in "replay my work in my branch starting from a recent point from the branch B).
    If done correctly, the subsequent merge from your branch to branch B can be fast-forward.

  • a merge directly impacts the destination branch B, which means the merges better be trivial, otherwise that branch B can be long to get back to a stable state (time for you solve all the conflicts)


the point of merging after a rebase?

In the case that I describe, I rebase B onto my branch, just to have the opportunity to replay my work from a more recent point from B, but while staying into my branch.
In this case, a merge is still needed to bring my "replayed" work onto B.

The other scenario (described in Git Ready for instance), is to bring your work directly in B through a rebase (which does conserve all your nice commits, or even give you the opportunity to re-order them through an interactive rebase).
In that case (where you rebase while being in the B branch), you are right: no further merge is needed:

A Git tree at default when we have not merged nor rebased

rebase1

we get by rebasing:

rebase3

That second scenario is all about: how do I get new-feature back into master.

My point, by describing the first rebase scenario, is to remind everyone that a rebase can also be used as a preliminary step to that (that being "get new-feature back into master").
You can use rebase to first bring master "in" the new-feature branch: the rebase will replay new-feature commits from the HEAD master, but still in the new-feature branch, effectively moving your branch starting point from an old master commit to HEAD-master.
That allows you to resolve any conflicts in your branch (meaning, in isolation, while allowing master to continue to evolve in parallel if your conflict resolution stage takes too long).
Then you can switch to master and merge new-feature (or rebase new-feature onto master if you want to preserve commits done in your new-feature branch).

So:

  • "rebase vs. merge" can be viewed as two ways to import a work on, say, master.
  • But "rebase then merge" can be a valid workflow to first resolve conflict in isolation, then bring back your work.

I just created an FAQ for my team in my own words which answers this question. Let me share:

What is a merge?

A commit, that combines all changes of a different branch into the current.

What is a rebase?

Re-comitting all commits of the current branch onto a different base commit.

What are the main differences between merge and rebase?

  1. merge executes only one new commit. rebase typically executes multiple (number of commits in current branch).
  2. merge produces a new generated commit (the so called merge-commit). rebase only moves existing commits.

In which situations should we use a merge?

Use merge whenever you want to add changes of a branched out branch back into the base branch.

Typically, you do this by clicking the "Merge" button on Pull/Merge Requests, e.g. on GitHub.

In which situations should we use a rebase?

Use rebase whenever you want to add changes of a base branch back to a branched out branch.

Typically, you do this in feature branches whenever there's a change in the main branch.

Why not use merge to merge changes from the base branch into a feature branch?

  1. The git history will include many unnecessary merge commits. If multiple merges were needed in a feature branch, then the feature branch might even hold more merge commits than actual commits!

  2. This creates a loop which destroys the mental model that Git was designed by which causes troubles in any visualization of the Git history.

    Imagine there's a river (e.g. the "Nile"). Water is flowing in one direction (direction of time in Git history). Now and then, imagine there's a branch to that river and suppose most of those branches merge back into the river. That's what the flow of a river might look like naturally. It makes sense.

    But then imagine there's a small branch of that river. Then, for some reason, the river merges into the branch and the branch continues from there. The river has now technically disappeared, it's now in the branch. But then, somehow magically, that branch is merged back into the river. Which river you ask? I don't know. The river should actually be in the branch now, but somehow it still continues to exist and I can merge the branch back into the river. So, the river is in the river. Kind of doesn't make sense.

    This is exactly what happens when you merge the base branch into a feature branch and then when the feature branch is done, you merge that back into the base branch again. The mental model is broken. And because of that, you end up with a branch visualization that's not very helpful.

Example Git History when using merge:

Example Git History when using merge

Note the many commits starting with Merge branch 'main' into .... They don't even exist if you rebase (there, you will only have pull request merge commits). Also many visual branch merge loops (main into feature into main).

Example Git History when using rebase:

Example Git History when using rebase

Much cleaner Git history with much less merge commits and no cluttered visual branch merge loops whatsoever.

Are there any downsides / pitfalls with rebase?

Yes:

  1. Because a rebase moves commits (technically re-executes them), the commit date of all moved commits will be the time of the rebase and the git history might look like it lost the initial commit time. So, if the exact date of a commit is needed in all tooling for some reason, then merge is the better option. But typically, a clean git history is much more useful than exact commit dates. And the author-date field will continue to hold the original commit date where needed.

  2. If the rebased branch has multiple commits that change the same line and that line was also changed in the base branch, you might need to solve merge conflicts for that same line multiple times, which you never need to do when merging. So, on average, there's more merge conflicts to solve.

Tips to reduce merge conflicts when using rebase:

  1. Rebase often. I typically recommend doing it at least once a day.
  2. Try to squash changes on the same line into one commit as much as possible.

While merging is definitely the easiest and most common way to integrate changes, it's not the only one: Rebase is an alternative means of integration.

Understanding Merge a Little Better

When Git performs a merge, it looks for three commits:

  • (1) Common ancestor commit. If you follow the history of two branches in a project, they always have at least one commit in common: at this point in time, both branches had the same content and then evolved differently.
  • (2) + (3) Endpoints of each branch. The goal of an integration is to combine the current states of two branches. Therefore, their respective latest revisions are of special interest. Combining these three commits will result in the integration we're aiming for.

Fast-Forward or Merge Commit

In very simple cases, one of the two branches doesn't have any new commits since the branching happened - its latest commit is still the common ancestor.

Enter image description here

In this case, performing the integration is dead simple: Git can just add all the commits of the other branch on top of the common ancestor commit. In Git, this simplest form of integration is called a "fast-forward" merge. Both branches then share the exact same history.

Enter image description here

In a lot of cases, however, both branches moved forward individually.

Enter image description here

To make an integration, Git will have to create a new commit that contains the differences between them - the merge commit.

Enter image description here

Human Commits & Merge Commits

Normally, a commit is carefully created by a human being. It's a meaningful unit that wraps only related changes and annotates them with a comment.

A merge commit is a bit different: instead of being created by a developer, it gets created automatically by Git. And instead of wrapping a set of related changes, its purpose is to connect two branches, just like a knot. If you want to understand a merge operation later, you need to take a look at the history of both branches and the corresponding commit graph.

Integrating with Rebase

Some people prefer to go without such automatic merge commits. Instead, they want the project's history to look as if it had evolved in a single, straight line. No indication remains that it had been split into multiple branches at some point.

Enter image description here

Let's walk through a rebase operation step by step. The scenario is the same as in the previous examples: we want to integrate the changes from branch-B into branch-A, but now by using rebase.

Enter image description here

We will do this in three steps

  1. git rebase branch-A // Synchronises the history with branch-A
  2. git checkout branch-A // Change the current branch to branch-A
  3. git merge branch-B // Merge/take the changes from branch-B to branch-A

First, Git will "undo" all commits on branch-A that happened after the lines began to branch out (after the common ancestor commit). However, of course, it won't discard them: instead you can think of those commits as being "saved away temporarily".

Enter image description here

Next, it applies the commits from branch-B that we want to integrate. At this point, both branches look exactly the same.

Enter image description here

In the final step, the new commits on branch-A are now reapplied - but on a new position, on top of the integrated commits from branch-B (they are re-based).

The result looks like development had happened in a straight line. Instead of a merge commit that contains all the combined changes, the original commit structure was preserved.

Enter image description here

Finally, you get a clean branch branch-A with no unwanted and auto generated commits.

Note: Taken from the awesome post by git-tower. The disadvantages of rebase is also a good read in the same post.

When do I use git rebase? Almost never, because it rewrites history. git merge is almost always the preferable choice, because it respects what actually happened in your project.

Rebase is useful when you are working on a branch and have merged some other work in between - the merges will create changes which will make your diff polluted and thus harder to read.

If you rebase your branch, then your commits will be applied on top of the branch you are rebasing to, which makes it easier to review and the diff output is cleaner.

Related