What you'll learn
Quick Answer
Git refuses to merge two branches that share no common ancestor commit. It happens most often when you run git init locally, then create a repository on GitHub with a README, and try to pull. The two histories were created independently so Git cannot work out what changed. Passing --allow-unrelated-histories tells Git you accept that and want a merge anyway. Before you use it, confirm the two histories are supposed to be different rather than the same project that lost its history.
What Git is actually complaining about
$ git pull origin main
fatal: refusing to merge unrelated historiesTo merge two branches, Git first finds their merge base: the most recent commit both branches have in common. That shared point is what makes a three-way merge possible, because Git can compare each side against it and work out what each side changed.
When the two branches have no commit in common at all, there is no base. Git would have to treat every file on both sides as newly added, and any file that exists on both sides with different content becomes a conflict from the very first line. That is almost never what you meant, so Git stops.
Older versions of Git performed this merge silently and produced exactly that mess. Modern Git refuses by default and makes you say the word, which is a good change even though it surprises people.
You can confirm the diagnosis yourself. git merge-base prints the shared commit, or prints nothing and exits non-zero when there is not one:
git fetch origin
git merge-base main origin/main
# no output = genuinely unrelated
git log --oneline main | tail -3
git log --oneline origin/main | tail -3
# compare the two root commitsIf the oldest commit hash on each side is different, the histories really are independent. That is a fact about your repository, not an opinion Git is having, and it is worth establishing before you type any flag.
The exact sequence that causes it
In nearly every case it is the same story, and it happens on the very first push of a new project.
# on your machine
mkdir fee-tracker
cd fee-tracker
git init
# write some code
git add .
git commit -m "initial commit"Then on GitHub you create the repository through the web form and tick Add a README file, or pick a .gitignore template or a licence. That tick is the whole problem. GitHub makes a commit of its own, so the remote now has a root commit that your local repository has never seen.
git remote add origin git@github.com:you/fee-tracker.git
git pull origin main
# fatal: refusing to merge unrelated historiesTwo independent root commits, no shared ancestor, refusal. Note that git push gives you a different error first, usually a rejection telling you the remote contains work you do not have locally, which is what sends people to git pull in the first place.
The second common route is deleting the .git folder. Someone hits a confusing Git state, deletes .git to start clean, runs git init again and commits everything. The files are identical but the history is brand new, so the local repository is now unrelated to the remote it came from. This one is worth recognising because the fix is different: you have not created two projects, you have thrown one project's history away.
A third route is adding the wrong remote URL, typically by copying a URL from a different repository or from a template you forked. Here the histories are unrelated because they are genuinely different projects, and merging them is the last thing you want.
Using --allow-unrelated-histories safely
The flag does not repair anything. It removes the safety check and lets the merge proceed, with every file on the remote side treated as new.
git pull origin main --allow-unrelated-histories
# or, if you prefer explicit steps
git fetch origin
git merge origin/main --allow-unrelated-historiesIf both sides contain a file with the same name, such as README.md or .gitignore, you get a conflict immediately. Resolve it the ordinary way, then finish the merge:
git status # lists the conflicting files
# edit each file, remove the <<<<<<< markers
git add README.md
git commit # completes the merge
git push -u origin mainFor the classic first-push case there is a cleaner option that most tutorials skip. If the remote contains nothing but an auto-generated README and licence, you are not preserving anything of value by merging. Copy whatever text you want out of it, then overwrite the remote:
git push -u origin main --forceUse this only when you are certain the remote holds no work anyone needs, which for a repository created five minutes ago is easy to be certain about. Never force-push a branch that teammates have already pulled from, because it rewrites the history under their feet and gives every one of them a different mess to untangle.
The habit that avoids the whole situation: create the GitHub repository empty, with no README, no .gitignore and no licence. GitHub then shows you the exact three commands to run, and there is no second root commit to collide with.
When the flag is the wrong answer
Pasting the flag without thinking is how repositories end up with duplicated files, doubled directory trees and a history that nobody can bisect. Three situations call for a different response entirely.
You have a shallow clone. Cloning with --depth 1, which CI systems and slow connections both do, downloads only recent commits. The shared ancestor exists on the server but was never fetched, so Git honestly reports the histories as unrelated. The flag would merge the two truncated histories together. The fix is to download the rest:
git fetch --unshallow
# or, for a specific branch
git fetch --depth=1000 origin mainSomeone rewrote the shared history. If a teammate ran a history-rewriting tool to strip a leaked API key or a large binary, every commit hash changed, and your local branch now descends from commits that no longer exist upstream. Merging welds the old and new histories together and reintroduces exactly the commits that were removed. The correct response is to re-clone, or to reset your branch onto the new upstream after saving any unpushed work:
git fetch origin
git branch backup-my-work # keep your commits somewhere
git reset --hard origin/mainYou are pointed at the wrong repository. Check before doing anything else:
git remote -vIf the URL is not the project you think you are working on, fix the remote rather than the merge. git remote set-url origin <correct-url> takes a second; unpicking a merge of two unrelated codebases does not.
The short test is this. If the two histories should have a common ancestor and do not, something has gone wrong that the flag will bury. If they were genuinely never related, for instance you are deliberately joining a docs repository into a monorepo, the flag is exactly the right tool.
Undoing a bad merge, and avoiding the next one
If you have already merged and the result is wrong, and you have not pushed, the merge commit is easy to drop:
git merge --abort # during an unfinished merge with conflicts
git reset --hard HEAD~1 # after the merge commit was createdgit reset --hard discards uncommitted changes, so commit or stash anything you want to keep first. If you have already pushed and other people have pulled, use git revert -m 1 <merge-commit> instead, which adds a new commit undoing the merge rather than rewriting shared history.
To stop this recurring, adopt one of two orders of operations and stick to it. Either create the repository on GitHub first and git clone it, so there is only ever one root commit, or create it locally and make the remote repository completely empty before pushing. Trouble only arrives when both sides commit independently.
Set your default pull behaviour explicitly as well, because an unconfigured git pull can merge or rebase depending on version and settings, and the error you get differs accordingly:
git config --global pull.rebase false # always merge
# or
git config --global pull.rebase true # always rebaseFinally, resist deleting the .git folder when Git confuses you. It looks like a reset button and it is closer to a shredder: every commit, branch and stash lives in there, and once it is gone the only recovery is whatever you have already pushed. Nearly every state that tempts you to delete it, including detached HEAD, a stuck rebase and a conflicted merge, has a two-command exit. Ask before you delete, and the unrelated-histories error stops appearing in your life.
