DEV Community

Cover image for Git Merge vs Rebase
Cihat Gündüz
Cihat Gündüz

Posted on • Updated on

Git Merge vs Rebase

An FAQ that explains and answers when to use which and why.

There's a common discussion among developers about how teams should use Git to make sure everyone is always up-to-date with the latest changes in the main branch. The typical situation this question arises is when someone worked on a new branch and then once the work is done and ready to be merged, the main branch had changes in the meantime in a way that the work branch is outdated and now has merge conflicts.

Obviously, they need to be resolved before the work branch can be merged. But the question is: How should this situation be resolved? Should we merge the main branch into the work branch? Or should we rebase the work branch onto the latest main branch?

In my opinion, there's only one correct answer to this question. From my experience, the main reason why so many discussions arise around this topic is that there's a lot of misunderstandings out there about how merge and rebase differ from each other in this context and a general lack of understanding, what a rebase even is.

So I created an FAQ for my team which tries to clarify things. 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 work branches whenever there's a change in the main branch.

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

  1. The git history will include many unnecessary merge commits. If multiple merges were needed in a work branch, then the work 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 work branch and then when the work 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  raw `merge` endraw

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

Example Git History when using rebase:

Example Git History when using  raw `rebase` endraw

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 loses the initial commit time. So, if the exact date of a commit is needed for some reason, then merge is the better option. But typically, a clean git history is much more useful than exact commit dates.
  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.

I hope this FAQ helps some teams out there. Like ❤️ it if you liked it! 😉

_This article was written by Cihat Gündüz

Top comments (2)

Collapse
 
darkwiiplayer profile image
𒎏Wii 🏳️‍⚧️ • Edited

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 loses the initial commit time. So, if the exact date of a commit is needed for some reason, then merge is the better option. But typically, a clean git history is much more useful than exact commit dates.

This is a non-issue, as git distinguishes between commit time and author time, the latter of which is kept when rebasing (or maybe there was a special flag for this to happen?), so you can clearly see when a commit was first created as well as when it was (last) put into the tree.

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.

I'm pretty sure git has a somewhat obscure option to remember merges, so it can figure out similar merges in the future automatically, precisely for this specific case. Can't remember what it's called (because I never really needed it), but I'm sure you can google it.


Overall, here's my take: Use rebase unless there was an actual branch in your work. At my workplace there's always lots of "merge changes from http://gitlab/some/project" type merge commits with one or two small commits on either side. Personally, I'd much rather just slap a --rebase on my git pull and have a less cluttered commit log, and leave the merges for cases where there was an actual branch in development, that is, one dev (or group thereof) was working on a feature separate from the main development version for a prolonged time. This way the commit graph paints a better picture of the actual development process, instead of giving you pointless insight into what coworker wanted to quickly commit a bunch of changes on the live system before pulling the newest main branch (something which should never really happen in the first place)

Collapse
 
ddaypunk profile image
Andy Delso

This has generally be my mentality as well. Rebase just looks so much cleaner in git UIs as referenced by your images, as well as on the command line. Great article!