Merge vs rebase for long-running branches Jump to heading

Most merge-versus-rebase advice assumes branches that live a day or two. Long-running branches — a framework migration, a large refactor, a feature that needs months behind a flag, an integration branch shared by a team — change the trade-offs. Rebasing a three-month branch means replaying hundreds of commits, resolving conflicts in each, and forcing everyone working on it to reset afterwards. Never integrating means a merge at the end that touches everything at once. The practical answer for most long branches is to merge main in regularly while the branch lives, and decide at the end how its history should land. This page sets out that routine and the exceptions, within merge vs rebase decision matrix.

When to use this approach Jump to heading

  • A branch will live for weeks or months while main keeps moving.
  • Several people commit to the branch, or other branches are based on it.
  • Previous long branches ended with a painful “big bang” merge.
  • You have confirmed the branch really must be long-lived; shorter alternatives are in limiting feature branch lifetime.

Step 1 — Compare the options over the branch’s whole life Jump to heading

The choice is not one operation but a pattern repeated for weeks. Compare the patterns, not single steps.

Three patterns for keeping a long branch currentNever integrating defers all conflicts to one large merge at the end. Rebasing regularly keeps history linear but replays every commit each time and forces collaborators to reset. Merging main in regularly resolves each conflict once, as it appears, and never rewrites shared history.Conflict effortCollaboratorsnever integrateall at the endunaffected until thenrebase weeklyreplayed each timereset every weekmerge main weeklyonce, as it appearsjust pullfor shared long branches, merging main in is almost always the least total work Three patterns for keeping a long branch currentNever integrating defers all conflicts to one large merge at the end. Rebasing regularly keeps history linear but replays every commit each time and forces collaborators to reset. Merging main in regularly resolves each conflict once, as it appears, and never rewrites shared history.Conflict effortCollaboratorsnever integrateall at the endunaffected until thenrebase weeklyreplayed each timereset every weekmerge main weeklyonce, as it appearsjust pullfor shared long branches, merging main in is almost always the least total work

Step 2 — Merge main in on a regular rhythm Jump to heading

Pick a rhythm — weekly, or whenever main has a significant change in an area the branch touches — and merge main into the branch. Each merge resolves only the conflicts introduced since the last one.

git switch feature/framework-v5 && git pull --ff-only
git fetch origin
git merge --no-ff origin/main -m "Merge main into feature/framework-v5 ($(date +%F))"
make test
git push
# How far behind is the branch right now?
git rev-list --count feature/framework-v5..origin/main
A long branch kept current by merging main inMain moves on every week. The long-running branch merges main in each week, creating merge commits W1 to W3, each resolving only that week's conflicts. At the end the branch is a short step from main and lands with a small final merge.small regular merges instead of one large onemainm1m2m3m4long branchb1W1W2W3final→ maineach W merge is small because the previous one already absorbed everything before it A long branch kept current by merging main inMain moves on every week. The long-running branch merges main in each week, creating merge commits W1 to W3, each resolving only that week's conflicts. At the end the branch is a short step from main and lands with a small final merge.small regular merges instead of one large onemainm1m2m3m4long branchb1W1W2W3final→ maineach W merge is small because the previous one already absorbed everything before it

rerere helps here too: if you try a merge, abort it, and redo it later, previous resolutions are reused. Combine with throwaway test merges between real ones, as in rerere for long-lived topic branches.

Step 3 — Keep the branch’s own work separable Jump to heading

With merges of main interleaved, the branch’s own changes can be hard to see. Two commands recover the view: the diff against the merge base, and the branch’s own commits excluding merges.

git diff origin/main...feature/framework-v5 --stat          # what the branch changes, net
git log --oneline --no-merges origin/main..feature/framework-v5   # the branch's own commits

Reviewers of a long branch should read the net diff rather than walking commit by commit through weeks of merges.

Step 4 — Know when rebasing is still right Jump to heading

Rebasing a long branch makes sense in two situations: the branch has only one author and nobody builds on it, or the branch is about to land and the team wants its commits linear on main. In the second case, rebase once at the end — and consider squashing groups of commits first to avoid replaying the same conflicts repeatedly.

# Final, one-time rebase of a single-author long branch, with history tidied first
base=$(git merge-base origin/main HEAD)
git rebase -i "$base"          # squash noise; merges of main are dropped by default
git rebase origin/main

The repetition problem is explained in repeated conflicts when rebasing vs merging.

Step 5 — Land the branch: merge, squash, or rebase Jump to heading

At the end, choose how the branch’s history lands on main, independently of how it was kept current.

How should a long branch land?If the branch's commits tell a useful story and the team keeps merge commits, merge it with a merge commit. If only the final result matters, squash it into one commit with a thorough message. If main requires linear history and the commits matter, rebase once and fast-forward.What should main's history show?the branch's storyMerge commitread via --first-parentonly the resultSquashone well-written commitlinear + individual commitsRebase oncethen fast-forwardhow you kept it current and how it lands are separate decisions How should a long branch land?If the branch's commits tell a useful story and the team keeps merge commits, merge it with a merge commit. If only the final result matters, squash it into one commit with a thorough message. If main requires linear history and the commits matter, rebase once and fast-forward.What should main's history show?the branch's storyMerge commitread via --first-parentonly the resultSquashone well-written commitlinear + individual commitsRebase oncethen fast-forwardhow you kept it current and how it lands are separate decisions

For very large branches, a squash produces a commit too large to review or bisect usefully; a merge commit preserves the internal steps. That is often the deciding factor for migrations.

Step 6 — Shorten the next one Jump to heading

After the branch lands, look at why it had to live so long. Most long branches could have been split behind a flag or an abstraction, delivering the same change as a series of short branches.

# How long did it live, and how many merges of main did it need?
git log --merges --oneline --grep='Merge main into feature/framework-v5' | wc -l

The techniques are in branch by abstraction for large changes.

Validation checklist Jump to heading

Frequently Asked Questions Jump to heading

Doesn’t merging main in make the branch’s history messy? Jump to heading

It adds merge commits to the branch. If main uses squash merging, they disappear when the branch lands. If main keeps merge commits, --first-parent on the branch shows its own story cleanly.

How often should main be merged in? Jump to heading

Often enough that each merge is small — weekly for most teams, more often when main is changing the same area heavily. A nightly throwaway merge can tell you when a real one is due.

Can other branches be based on the long branch? Jump to heading

Yes, and merging main in rather than rebasing is what makes that workable: dependent branches never see the long branch’s history rewritten.