Git Unrelated Histories: Fix Refused Merge
Merge with git pull --allow-unrelated-histories, then resolve everything once.
20+ years shipping production infrastructure and CI/CD at scale. Written from production experience, not tutorials.
- ✓Git installed with init, clone, and remote basics down
- ✓A test repo plus an empty remote you can seed and delete freely
- ✓Comfort reading git log --graph and resolving a text conflict
- 'Refusing to merge unrelated histories' means your local repo and the remote share no common ancestor commit, so Git won't guess how to join them
- The classic trigger is git init locally plus a remote created with a README, license, or .gitignore that seeded its own first commit
- Fix it deliberately with git pull origin main --allow-unrelated-histories, then resolve every conflicted file and commit the join
- Don't use the flag blindly: if one side is nearly empty, recloning or pushing your history is cleaner than a confusing forever-merge
Imagine two families who both happen to be named Smith and someone suggests merging their family trees. The software refuses, because neither tree shares a grandparent — grafting them together would invent a shared past that never existed. Git does the same with two repos that started life separately. You can still staple the trees together with a special flag, but you should first check whether one tree is nearly empty and simpler to regrow from the other.
You initialized a repo, added the remote, ran git pull — and Git slapped your hand: 'refusing to merge unrelated histories'. Your URL is right. Your credentials work. The problem is ancestry: your local history and the remote's history start from different root commits with no shared past, and since Git 2.9 the default is to refuse joining them rather than invent a merge base that doesn't exist.
The standard setup that produces this is painfully ordinary. You ran git init, committed your project, and connected a GitHub repo you created with 'Add a README' checked. That checkbox created a first commit on the server. Now both sides have a root commit, neither descends from the other, and pull has nothing to stand on. The same thing happens when you merge two long-separate projects, vendor a library's repo into yours, or point an old checkout at a freshly re-created remote.
The fix is a deliberate override: --allow-unrelated-histories tells Git to use an empty tree as the fake common ancestor and merge everything. That works, but it merges every file both sides ever created, so you must decide when the flag is right and when a reclone or a fresh push is cleaner. This guide shows you how to diagnose independent roots, apply the flag safely, resolve the resulting join, and choose the alternative that keeps history honest.
What 'Unrelated Histories' Means Under the Hood
Every Git merge needs a merge base: the newest commit both sides descend from, used as the reference for deciding what changed. Normal merges walk both histories back until the lines meet. With unrelated histories the walk never converges — each side reaches its own root commit, a commit with no parents, without ever meeting the other line. Since Git 2.9 the default response is refusal, because diffing two trees against nothing produces an 'everything is new on both sides' result that no algorithm should apply unsupervised.
The --allow-unrelated-histories flag supplies a synthetic base: the empty tree. Both sides' full contents then count as additions against nothing, which is why the merge touches every file and overlapping paths become add/add conflicts. Understanding this predicts the blast radius before you run it. Two roots with ten shared filenames means ten conflicts minimum, and a template-seeded remote against a mature project means conflicts across the entire skeleton.
Check roots directly with git rev-list --max-parents=0 HEAD for your side and the same against origin/main for theirs. Different hashes confirm independence. This thirty-second check separates 'I need the flag' from 'I need a different strategy' and is the cheapest diagnosis in the whole article.
The Classic Collision: Local Init Meets Seeded Remote
The number-one factory for this error is the GitHub 'Add a README file' checkbox. You build a project locally, init, commit, then create the remote with seed files checked. The server mints its own root commit containing README, LICENSE, and .gitignore. Your git remote add plus git pull then faces two roots, and the refusal follows instantly. The same happens with GitLab's 'Initialize with README' and any template-based repo creation.
You have three exits, and the right one depends on what the remote holds. If the remote is only seeds, the cleanest path is cloning fresh and copying your project files over the seed skeleton — two minutes, no merge, history starts from the remote root honestly. If your local history matters (existing commits, branches, tags), push it to a truly empty remote instead: create the remote with no seeds, or delete the seed commit's branch content first.
Reach for the flag only when both sides hold real work you must union. That happens with genuinely parallel starts — two engineers scaffolding the same service over a weekend — or when policy forbids rewriting either side. In that case the merge is real work: resolve every add/add conflict thoughtfully, write a merge message explaining the two roots, and review the union before pushing.
Using the Flag Without Regretting It
When the union is genuinely wanted, run the merge with intent, not haste. git pull origin main --allow-unrelated-histories fetches and immediately opens the join. Overlapping paths stop as add/add conflicts — both sides added the file from nothing, so Git can't prefer either. Open each one, compose the correct surviving content (often theirs-plus-yours, sometimes a full rewrite), stage it, and finish with a merge message that names both roots and explains why they joined.
Scale determines sanity. A dozen conflicts you can read carefully is a fine merge. Hundreds of conflicts is evidence you're unioning two projects, and no human reviews that faithfully — abort and pick reclone or subtree instead. After completing the join, inspect git show --stat HEAD and read the full diff of every conflicted file before pushing. The merge commit is the last checkpoint where the union is reviewable as one unit.
Push the result through a pull request, not directly. Reviewers see the join commit's combined diff and can spot duplicated trees, lost files, or wrong-side resolutions. The docs incident in this article would have been a five-minute review catch instead of a three-day duplication cleanup if the merge had faced one reviewer.
When to Reclone Instead of Merging
Recloning is the honest fix whenever one side's history carries no value worth preserving in the join. Remote holds only template seeds? Clone it, copy your files in, commit once, push. Local repo is three scaffold commits and the remote holds the team's month of work? Clone the remote and port your changes over. In both cases the result is a single-root history anyone can understand, with no synthetic merge base and no phantom conflicts.
The procedure protects your work first: branch or copy your current state aside before deleting anything, so the 'losing' side remains recoverable. Then verify the fresh clone builds and tests pass before porting — there's no point grafting good work onto a broken base. Port changes as files plus a clean commit, or cherry-pick specific commits if the losing side held work worth attributing.
Choose this path aggressively for first-push collisions, tutorial repos, and template starts. The flag exists for histories that both matter. Most unrelated-history encounters in practice involve one side that doesn't, and a ten-minute reclone beats a permanent confusing root-merge that every future git log reader must puzzle over. Future you, reading log --graph at midnight, will thank present you for the clean single line.
Subtree and Submodule: Better Joins for Vendored Code
Joining a library's repo into yours is the one case where independent roots are expected — and raw unrelated merges are still the worst tool for it. git subtree add --prefix=vendor/lib records the join with the prefix boundary intact, so future library updates merge cleanly and git log -- vendor/lib shows the vendored history sensibly. Submodules go further, keeping the library as a separate repo pinned by hash, which suits dependencies that evolve independently.
Compare that with a flag merge: the library's files scatter into your tree with no recorded boundary, future updates have no clean merge path, and history archaeology can't distinguish your code from theirs. The five minutes subtree costs upfront repays itself at the first library update, when git subtree pull --prefix=vendor/lib just works while the raw-merge team hand-resolves the same files again.
Decide by coupling. Tightly coupled code you edit alongside your own belongs in your tree via subtree. Independently versioned dependencies you never edit belong in submodules. Neither belongs in a bare unrelated merge — that option combines the permanence of vendoring with the traceability of nothing. Write the choice into your dependency policy so the next vendor addition starts from the right tool instead of rediscovering this lesson.
Stop Manufacturing Second Roots
Every prevention here is a creation-time habit. Create remotes empty when you'll push existing code — uncheck README, license, and .gitignore seeds on every host. When the remote must carry template files, clone first and build inside the clone so your first commit descends from the remote root honestly. For migrations and re-creations, retire the old remote's URL rather than reusing it under a fresh repo, so stale clones never face a stranger root.
Add the diagnosis to team runbooks: rev-list roots on both sides before any flag use, and require pull-request review for every unrelated join on shared branches. A thirty-second root check plus one reviewer eliminates both the accidental unions and the unreviewed ones.
Finally, never normalize the flag. Don't alias it into pull, don't document it as 'the fix for first pull', and don't teach it before teaching reclone. It's a precision instrument for genuine two-root joins — rare, deliberate, and reviewed. Teams that treat it as routine eventually union something they never meant to, and discover it from users rather than from Git. Keep the guardrail loud and the override quiet. A team that reviews its Git runbook quarterly keeps these habits alive; a team that files it away relearns them per incident.
Docs Deploy Merged a README Twice and Duplicated 200 Pages for 3 Days
- Exit zero is not review. An unrelated-histories merge combines two entire trees, and only a human reading the merge diff can judge whether the join is sane. Route every such merge through a pull request so the union of both histories gets inspected file by file.
- Prefer reclone over union when one side is nearly empty. The remote's template skeleton could have been deleted in one commit or the local repo could have been cloned fresh. A 2-minute reclone beats a 3-day duplication cleanup every time.
- Uncheck the seed files when you plan to push existing code. Creating a remote with README, license, or .gitignore pre-checked manufactures the second root commit that causes this whole category of incident.
| File | Command / Code | Purpose |
|---|---|---|
| fix-seeded-remote.sh | git rev-list --max-parents=0 HEAD | The Classic Collision |
| union-merge-safely.sh | git fetch origin | Using the Flag Without Regretting It |
| reclone-instead.sh | git branch backup-before-reclone | When to Reclone Instead of Merging |
| vendor-with-subtree.sh | git subtree add --prefix=vendor/lib | Subtree and Submodule |
| prevent-second-roots.sh | git rev-list --max-parents=0 HEAD | Stop Manufacturing Second Roots |
Key takeaways
Common mistakes to avoid
6 patternsUsing the flag as the default answer to every refusal
Resolving hundreds of add/add conflicts by accepting one side wholesale
Pushing the join commit directly to a shared branch
Creating remotes with seed files when pushing existing code
Merging a re-created remote into a stale clone
Vendoring with a bare flag-merge instead of subtree
Interview Questions on This Topic
What does 'refusing to merge unrelated histories' mean?
Frequently Asked Questions
20+ years shipping production infrastructure and CI/CD at scale. Written from production experience, not tutorials.
That's Git. Mark it forged?
5 min read · try the examples if you haven't