Keeping a long-lived branch in sync with main Jump to heading

A branch that lives for three weeks does not become hard to merge on the last day. It becomes hard gradually, and invisibly, while everyone involved is looking at the feature rather than at the distance from main. By the time the merge is attempted the conflict surface spans files the author never touched, and the resolution is being done by the person least equipped to judge it. This recipe keeps that distance small, extending the practice in Feature Branch Isolation.

When to use this approach Jump to heading

  • A branch has been open for more than a few days and main is moving.
  • A refactor on main touched files your branch also changes.
  • More than one person has the branch checked out, which changes what you are allowed to do to it.
  • The merge is already frightening, which is the symptom this recipe exists to prevent recurring.
  • If your team merges within a day, none of this applies — see Trunk-Based Development, which removes the problem rather than managing it.

Step 1 — Measure the drift before deciding anything Jump to heading

# How far apart are the two branches, in both directions?
git fetch origin
git rev-list --left-right --count origin/main...HEAD
# Output "83  12" means: 83 commits on main you do not have, 12 of yours

# Which files does the divergence actually touch on both sides?
comm -12 \
  <(git diff --name-only origin/main...HEAD | sort) \
  <(git diff --name-only HEAD...origin/main | sort)

What changed: nothing — but that second command is the real risk measure. Two branches can be a thousand commits apart and merge cleanly if they touch different files; twenty commits apart with ten shared files is the dangerous case.

Conflict cost against days since the last syncResolution effort stays near zero for the first few days, rises noticeably after a week, and becomes severe beyond two weeks as the number of overlapping files and the unfamiliarity of the changes both grow.days since the last sync with maineffort01371421sync here, dailythe conflicts span filesyou have never read

Step 2 — Sync on a cadence, not when it hurts Jump to heading

# A daily sync, run at a fixed time — cheap because it is small
git fetch origin
git merge --no-edit origin/main       # or rebase; see Step 3

# Verify the branch still builds after the sync, before continuing work
npm test --silent

What changed: the branch absorbed one day’s worth of main while both sides are still fresh in someone’s memory.

# Confirm the drift is back to near-zero on the incoming side
git rev-list --left-right --count origin/main...HEAD    # first number should be 0

The value of a fixed cadence is that it removes the decision. “Sync when it starts to feel risky” reliably means syncing after the risk has already materialised, because the feeling arrives late.

Step 3 — Choose merge or rebase by who else uses the branch Jump to heading

The sync method is decided by collaborators, not tasteIf you are the only person with the branch checked out, rebase keeps it a clean series on top of current main. If anyone else has it, rebasing rewrites commits they already hold and forces them to recover, so merge is the only safe option.who has this branchchecked out?only megit rebase origin/maina clean series on current mainreview reads as one storyforce-push needed each timesomeone else toogit merge origin/mainhistory is noisierbut nobody's clone breaksno force-push at all
# Solo branch: rebase, then force-with-lease
git rebase origin/main
git push --force-with-lease

# Shared branch: merge, and push normally
git merge --no-edit origin/main
git push
# Before rebasing, confirm nobody else is on the branch
git log --format='%an' origin/feat/payments | sort -u

--force-with-lease rather than --force matters even on a solo branch: it refuses the push if the remote moved since your last fetch, which is how you find out that “solo” was wrong before you overwrite someone’s work. The broader trade-off is laid out in the Merge vs Rebase Decision Matrix.

SAFETY WARNING — never rebase a branch someone else has checked out. Their clone still holds the pre-rebase commits, and their next pull merges the old and new histories together, producing duplicated commits and a diff that makes no sense to anyone. If you are unsure whether a colleague has it, merge instead; the cost is a slightly noisier history, and the cost of the alternative is somebody’s afternoon.

Step 4 — Make repeated conflicts cheap Jump to heading

A rebase replays every commit on the branch, so a conflicting hunk conflicts once per replayed commit. Git can remember the resolution.

# Record conflict resolutions and replay them automatically
git config rerere.enabled true
git config rerere.autoUpdate true
# On the next rebase, the same conflict resolves itself
git rebase origin/main
# Expect: "Resolved '<path>' using previous resolution."

What changed: the second and subsequent occurrences of an identical conflict are applied from the recorded resolution instead of being re-resolved by hand. The mechanism, its limits, and how to forget a bad resolution are covered in Reusing Conflict Resolutions with rerere.

Step 5 — Shrink the branch rather than maintaining it Jump to heading

Three short branches instead of one long oneA single branch open for three weeks accumulates conflict risk the whole time. The same work split into three slices, each merged within a few days behind a feature flag, never diverges far enough to be difficult, and each slice is small enough to review properly.one branch, three weeksmaindivergence grows every day; one enormous merge at the endthree slices, a few days eachmainschemaAPI, flag offUI, then flag oneach slice is reviewable on its own, and none of them is ever more than a day or two from main
# Split the work: land the parts that are safe to ship dormant
git checkout -b feat/payments-schema origin/main
git cherry-pick <schema-commits>
git push -u origin feat/payments-schema      # review and merge within a day
# Verify each slice is independently mergeable
git merge-base --is-ancestor origin/main HEAD && echo "no divergence to resolve"

Syncing is a treatment; a short-lived branch is the cure. If a piece of work genuinely cannot be split, keeping it behind a flag so incomplete code can merge dormant is usually possible — and it converts a three-week merge risk into three ordinary reviews.

Validation checklist Jump to heading

Frequently Asked Questions Jump to heading

How often should a long-lived branch sync with main? Jump to heading

Daily, and at a fixed time rather than when someone notices a problem. Conflict cost grows faster than linearly with divergence, so ten small syncs are dramatically cheaper than one big one. A daily sync also surfaces an incompatible change on main within a day, when the person who made it still remembers the context.

Merge or rebase for the sync? Jump to heading

Rebase if you are the only person working on the branch: it keeps the branch a clean series of commits on top of current main. Merge if anyone else has it checked out, because rebasing rewrites commits they already have and forces them to recover. The number of collaborators, not personal preference, is what decides this.

Why do the same conflicts keep reappearing? Jump to heading

Because a rebase replays every branch commit over the new base, so a conflicting hunk conflicts once per replayed commit. Git can record your resolution and replay it automatically — that is exactly what rerere is for, and enabling it turns the tenth occurrence of a conflict into a no-op.