DEV Community

Tim Abell
Tim Abell

Posted on • Edited on • Originally published at timwise.co.uk

Should you rebase or merge to update feature branches in git?

You have a "feature branch" in git that you've been working on for a while but now
master has moved on.

You need to bring your feature branch up to date with with master to flush
out any incompatibilities and deal with any merge conflicts..

You have two common choices:

  • Merge origin/master into your branch.
  • Rebase your branch onto origin/master and force-push.

TLDR

Try rebase, if it dissolves into conflict resolution hell give up and merge
master into your branch and move on.

The trade-offs

A blanket rule here either for merge or rebase is unhelpful because there are
trade-offs to be made that vary depending on the specific circumstances. (Isn't
that always the answer with git?!)

Merge

git fetch
git merge origin/master
git push

Merge - the good

  • Reliable no-brainer that anyone can follow by rote
  • Resolve conflicts only once
  • Accurate representation of what happened over time
  • Avoids retrospectively introducing bugs and test failures into commits that used to be valid
  • Avoids re-writing previously shared branch, which can confuse less experienced git users if they are working with you on the branch

Merge - the bad

  • Multiple of these makes it harder to then go back and clean your branch's commits with a git rebase --interactive
  • Tends to generate wide tramlines in the commit history that can be very hard to follow when looking back to find out when/why something was done. (mitigated by git log --first-parent, until you need to dig into a branch)

Rebase

git fetch
git rebase origin/master
git push --force-with-lease

Rebase - the good

  • Avoids tramlines generated by long-lived feature branches
  • Can result in having to fix the same merge conflict for every commit in your branch
  • Makes resultant history in master much easier to follow
  • Reflects the intention more clearly of "merge these commits into master" as opposed to "here's how I flailed my way to a working thing"
  • Could in somewhat rare circumstances retrospectively introduce bugs and test failures into commits that used to be valid

Rebase - the bad

  • Can confuse less experienced git users if they are working with you on the branch (the answer is usually for them to run git pull --rebase)
  • Loses chronological order of creation of code (personally I think this is less important than a series of clean intentional patches to be applied to the codebase when merged to master)

Heuristics to use

Try rebase, if it dissolves into conflict resolution hell give up and merge
master into your branch and move on.

This is my preferred approach up until the point the rebase becomes too costly
to fix up due to conflicts with master or I become aware of an
incompatibility with master that changes the meaning of the previous commits
and needs serious work to resolve in a merge commit.

You can usually make a difficult rebase work, and I've hunkered down and
tackled probably more than I should have in the name of perfect history graphs.
The problem is if you are doing this for business and not just for fun then
there is a major time cost for only a marginal benefit.

A note for the brave and foolish on hard rebases

You can battle on and make your rebase work, fixing conflicts carefully for
every commit as they cascade from one to the next If you do decide to battle
through a nasty rebase then my tip is not to jump to the answer when fixing
each commit, as that guarantees the next commit won't apply, instead make each
commit apply with its original meaning and nothing more. It's worth remembering
that each commit on your branch that describes how to change the source code
from a before to an after state, and if you change the before state then the
patch no longer applies.

How to avoid the pain entirely

Pain around this topic is likely a symptom of not breaking down your stories /
pull requests / features into small enough chunks. On a fast moving team
master is very fluid and any large & long-running branches will be hard to
review and merge. Try to chip off smaller increments and ship those, maybe
using feature flags or hidden features (those with no visible way of getting to
them).

More

In general merge vs rebase generates much debate, such as that found on
stackoverflow:
https://stackoverflow.com/questions/804115/when-do-you-use-git-rebase-instead-of-git-merge
but it is often lacking context.

There are many other articles on the merge/rebase topic such as
https://derekgourlay.com/blog/git-when-to-merge-vs-when-to-rebase/ but I
couldn't see anything that matched my heuristic for tackling feature branch
updates so I wrote this one.

Originally posted at https://timwise.co.uk/2019/10/14/merge-vs-rebase/

Top comments (5)

Collapse
 
nflamel profile image
Fran C. • Edited

Great article! Knowing the tradeoffs is always a super useful info! A couple of things I've found about this topic on my one experience:

git rerere can help a bit if you decide to use rebase and have the same conflicts repeated a lot of times.

Also, if you use github, rebases won't show your team mates the differences between their last code review session and your new code.

Collapse
 
timabell profile image
Tim Abell

Thanks, that's really useful info. I knew of rerere but never got round to using it. I think you have to turn it on before you resolve so it can capture the resolution.

Good point about GitHub pull requests too.

Collapse
 
jessekphillips profile image
Jesse Phillips

Use --fixup in reviews, rebase when review is complete. Git hub/lab should make that stand operation requirements.

Collapse
 
timabell profile image
Tim Abell

Cheers for the comment.

About fixup: explainshell.com/explain?cmd=git+c...

So using fixup to alter existing commits during code review would be towards a goal of "perfect first time" commits in a feature branch, resulting in a clean and intentional history for extra cost and effort, as opposed to the rapid fire "follow a quick process, merge, and don't worry too much about the resultant history" end of the spectrum.

Depending on goals, a argument for not using fixup is it makes it harder for a reviewer to validate the changes you make as a result of receiving feedback. As Fran mentioned they would have to re-review the whole patch again.

Thread Thread
 
jessekphillips profile image
Jesse Phillips

I suggested fixup to remove the need to rereview the whole patch, the fixup commit is separate in history so you can see the changes in context of the request to change.

Also this process creates clearer understanding of the challenges being made. Mistakes are found when breaking apart commits. Even doing a code review was easy when I decided to rebase and split up a big commit.