Git "fatal: refusing to merge unrelated histories": when the flag is safe, and when it isn't
Fix Git "fatal: refusing to merge unrelated histories": why it happens, how to use --allow-unrelated-histories safely, and how to spot a wrong remote or shallow clone.
by OhSudo ··
Checked on real software: Git · November 2026How we check
Part of a learning path:Git for everyday work
$ git pull --no-rebase origin mainfatal: refusing to merge unrelated historiesCommits in Git form a chain back to a starting point, and a merge normally looks for the last commit the two sides share. Here it found none: the two branches started separately. Since Git 2.9, it refuses by default, because that’s usually a mistake.
The most common reason is entirely innocent: you ran git init locally, then created the repository on GitHub with a README. That made two first commits, each with its own history.
- Local historystarted with git initand
- Remote historystarted with the README commitmerge looks for a shared commit(fails)
- No common commitrefusing to merge unrelated histories(breaks here)
TL;DR
Check that you have the right repository, then merge them:
Solve any conflicts, then:
git push -u origin main1. Check before you merge
The flag does exactly what you tell it, including merging two projects that have nothing to do with each other. So take a moment to look:
git remote -vgit fetch origingit log --oneline --all --graph -n 20You should see two separate lines. Then see what’s in the other side:
Is that what you expected? Just a README.md and a LICENSE is the harmless case. If you see some other project’s files, your remote is wrong. Fix that instead of merging:
git remote set-url origin git@github.com:acme/the-right-repo.git2. New repository: merge the two
git pull --no-rebase origin main --allow-unrelated-histories--no-rebase makes the choice explicit. Without it, and with no default set, current versions of Git stop earlier with fatal: Need to specify how to reconcile divergent branches.
If both sides have a file with the same name, such as a README, you’ll get a conflict:
CONFLICT (add/add): Merge conflict in README.mdAutomatic merge failed; fix conflicts and then commit the result.Edit the file to keep what you want, and finish the merge:
git add README.mdgit commitgit push -u origin mainYour history now has both starting points, joined by a merge commit. That’s perfectly fine.
3. New repository: throw away the remote’s first commit
If the remote holds nothing you need, such as an auto-generated README, you can replace it. This overwrites the remote, so check that nobody else has pushed there first:
git log --oneline origin/maingit push --force-with-lease origin main--force-with-lease refuses if the remote has changed since you last fetched, which makes it safer than --force. A protected branch will refuse it anyway, and that’s the correct behavior.
The other way is to start from the remote, which keeps one clean history:
git clone git@github.com:acme/app.git app-cleanrsync -a --exclude .git app/ app-clean/ # your files on top, without the old historyNext time, avoid all of this: create the GitHub repository empty, with no README, license or .gitignore. Or clone first, and work in the clone. The .gitignore Generator can write that file before your first commit.
4. A shallow clone
Here’s one that looks the same with a very different cause. With a shallow clone, the shared commit isn’t on your machine, so Git thinks the histories are unrelated:
git rev-parse --is-shallow-repositorytrue means this is your case. Don’t use the flag: get the rest of the history instead.
git fetch --unshallowIt’s common in CI, where a checkout usually fetches one commit. For example, in GitHub Actions:
- uses: actions/checkout@v4 with: fetch-depth: 05. History was rewritten
Someone rewrote the history with git filter-repo, or recreated the repository to remove a leaked secret. Your copy and the remote now have different commits all the way back. Merging would bring the old history back, including whatever was removed.
So if you have no unpushed work, reset to the new history. This discards your local commits and uncommitted changes:
git statusgit fetch origingit reset --hard origin/mainIf you do have local work, save it as a patch before that, with git format-patch origin/main --stdout > my-work.patch, and apply it afterwards. Asking the person who rewrote it is a good idea too.
6. Combining two real projects
If you do want to merge two real repositories, keep them apart by moving one into its own folder first, in the repository you’re merging from:
mkdir legacy-appgit mv -k * legacy-app/git commit -m "Move everything into legacy-app/"Then, in the main repository:
git remote add legacy ../legacy-appgit fetch legacygit merge legacy/main --allow-unrelated-historiesYou then have both, with their histories, without conflicts.
Warning
Don’t make --allow-unrelated-histories a habit, or add it to scripts or CI. It turns off a check that’s there to catch a wrong remote. In an automated pull, that can merge the wrong project without anyone noticing.
Shell, we have a problem!
Similar, but different: Updates were rejected because the remote contains work that you do not have locally is the normal case where the histories are related: see git push rejected. fatal: Need to specify how to reconcile divergent branches is about choosing merge or rebase, and a plain git pull can show it for these same repositories, before it gets as far as this error. detected dubious ownership is about the folder’s owner: see that article. And if .gitignore is ignored, see why .gitignore doesn’t work. All the everyday commands are in the Git Cheat Sheet.
$ ./practice.sh
Practice this in the terminal
Open the Terminal Playground with this article’s scenario loaded. It is safe and runs in your browser.
Found this useful? Share it.
Found a mistake? Let us know at contact@ohsudo.com.