Why a Merged Branch Suddenly Conflicts: Squash-Merge Divergence

Date:

You squash-merged a branch cleanly, kept working on the same branch, opened a new pull request — and it says CONFLICTING, on files nobody else touched. The branch isn’t broken; squash merge quietly spent it.

The short answer: A squash merge puts a single new commit on your default branch that combines your feature branch’s changes — but your branch’s original commits never become ancestors of the default branch. So the branch and the default branch now share only an old common base: git sees them as diverged, and if you keep working on that branch and re-PR, it tries to re-introduce changes already present (via the squash) while the default branch has moved on, producing conflicts in files you already merged. The fix is not to resolve them one by one — it’s to cut a fresh branch off the current default branch and re-apply only your new work. A squash-merged branch is spent; treat it as disposable.

Squash merge is the popular “clean history” option: instead of preserving a feature branch’s commit-by-commit history, GitHub collapses the whole branch into one tidy commit on the default branch. The tidiness has a cost that only shows up if you reuse the branch afterward.

The surprise: a merged branch that won’t merge again

The sequence looks innocent. You open a PR, it’s squash-merged, everything’s green. You stay on the same branch, add more commits, push, and open a second PR — and GitHub reports it CONFLICTING, or your tooling refuses it. You check the files and nothing you changed overlaps anyone else’s work. The instinct is to hunt for the conflicting edit, but there isn’t a normal one; the whole branch has diverged, and the conflicts are in the very files your first PR already merged.

A squash-merged branch looks merged but is, to git, an orphan sharing only an old ancestor with main.

Why squash merge diverges the branch

Everything follows from what squash merge actually does to history. A normal merge makes the default branch a descendant of your branch — your commits are now in main’s ancestry, so main and your branch share a recent tip. A squash merge does not: it writes a brand-new commit on main whose content equals your branch’s changes but whose identity is unrelated to your branch’s commits. Your branch’s commits are nowhere in main’s history.

So after the squash, your branch and main have diverged from whatever commit they last shared — often far back. Your branch still “contains” its original commits; main contains a different commit with the same net changes. Add new work to the branch and re-PR, and git must reconcile a branch that reintroduces already-merged changes against a main that has advanced independently. It can’t tell “this is the same change, already in via the squash” from “this is a new, conflicting change,” so it surfaces conflicts — precisely in the files that were part of the first merge.

Squash-merge divergence – and the clean fixA squash-merged branch is spent; re-apply the work, not the branch.
1. The squash merge

GitHub writes ONE new combined commit on main. Your branch's original commits never become ancestors of it.

2. The divergence

Branch and main now share only an OLD common base. Git counts the branch many commits 'behind' – though its content was merged.

3. The phantom conflict

Keep working, re-PR: git reintroduces already-merged changes against a moved-on main -> CONFLICTING in files you already merged. Not a real conflict.

4. The fix: fresh branch

Cut a NEW branch off current main, re-apply only your new work (it applies cleanly – main has the squash), abandon the stale branch.

A merged branch is spent: one branch per PR, never reused across merges.

How to recognize it

Three tells, together, are diagnostic:

  • The PR is CONFLICTING but you know your new edits don’t overlap active work.
  • The branch shows as many commits “behind” the default branch (often a large number), even though its content was “merged.”
  • The conflicts are in files your already-merged PR touched, not in genuinely contested new files.

If all three hold, you’re not looking at a real content conflict — you’re looking at squash divergence.

The fix: cut a fresh branch, re-apply only the new work

Don’t resolve the phantom conflicts commit by commit — you’d be re-litigating changes already in main. Re-base the work, not the branch:

  1. Fetch the current default branch.
  2. Create a new branch off it (git switch -c <fresh> origin/main).
  3. Re-apply only your genuinely new changes onto it — cherry-pick the new commits, or simply take the final state of the files you changed and commit them fresh.
  4. Push the new branch, open its PR, and abandon the stale one.

Because the fresh branch starts from a main that already contains the squashed changes, your new commits apply cleanly on top — the divergence is gone, and the diff shows only the new work. Verify first that main didn’t independently change the same files (a quick diff of your files against origin/main); if it didn’t, your re-applied versions land without conflict.

Prevention: a merged branch is spent

The durable habit: after a squash merge, stop using that branch. Delete it, update your local default branch, and start the next piece of work from a fresh branch off the updated default. One branch per PR, never reused across merges. Squash merge deliberately trades your branch’s commit identity for one clean commit on main — which means the branch has already done its whole job the moment it merges. Reusing it is reusing something git now considers unrelated to the history it was folded into.

The short version

Squash merge writes a new combined commit on main and leaves your branch’s original commits out of main’s history, so a reused branch diverges from an old common base. Keep working on it and re-PR, and git reintroduces already-merged changes against a moved-on main, conflicting in files you already merged — a phantom conflict, not a real one. Don’t resolve it; cut a fresh branch off the current default, re-apply only your new work (which now applies cleanly because main already has the squash), and abandon the stale branch. Prevent it by treating a squash-merged branch as spent: one branch per PR, start the next work from an updated default.

Frequently Asked Questions

I squash-merged and kept working on the branch — why does the new PR conflict?

Because the squash put a new commit on main and left your branch’s commits out of main’s ancestry. The branch diverged from an old shared base, so re-PRing reintroduces already-merged changes against a moved-on main and conflicts.

Are these real conflicts I should resolve by hand?

No. They’re phantom conflicts from divergence, in files already merged. Resolving them re-litigates changes main already has. Re-apply your new work onto a fresh branch instead.

What’s the clean fix?

Cut a new branch off the current default branch, re-apply only your genuinely new commits (cherry-pick or re-commit the final file state), push that, and abandon the stale branch.

How do I avoid it entirely?

Don’t reuse a squash-merged branch. Delete it after merge, update your default branch, and start the next work from a fresh branch off it — one branch per PR.

Does a normal (non-squash) merge have this problem?

No. A normal merge makes main a descendant of your branch, so the branch’s commits are in main’s history and it doesn’t diverge. The trade-off is a less tidy history — which is why teams choose squash and inherit this reuse caveat.

Share post:

Subscribe

Popular

More like this
Related

How to Merge Spreadsheets by URL (and Why Our Tool Reported 5 Misses That Weren’t)

Matching two spreadsheets on a URL column is the same job as matching a stock list to a price list by product code. It goes wrong in the same two places: how you clean the code, and how you count what didn't match.

Many-to-One: Why a Mapping Backlog Outgrows Its Target Taxonomy

You're mapping a messy source vocabulary onto a clean target taxonomy. The target has about 5,600 slots, so the job must be roughly 5,600 decisions — right? The backlog is already past 8,900, and still climbing.

The Fix That Didn’t Take: When a Content-Hash Gate Skips Your Code Change

You shipped a fix to how one field is computed, re-ran the pipeline across everything, and it reported a clean run. Then the consistency check still flags those records as wrong. The fix is in the code, the rebuild "ran" — and the records never changed. Your own optimisation skipped them.

Your Dedup Scan Is Over-Counting — Because It Forgot What You Already Fixed

The report said 80 duplicate groups still to resolve. You'd been resolving them for days. You run it again after a big batch and it *still* says 80-ish. The backlog isn't stubborn — the scan is counting work you already finished.