fatal: refusing to merge unrelated histories
Git is protecting you from combining two repositories that share no common ancestor. Sometimes that is exactly what you want.
The short answer
fatal: refusing to merge unrelated histories
The two branches have no common ancestor commit. Git will not merge them by default because the result is almost always a mistake.
If you genuinely want to combine two separate histories:
git merge --allow-unrelated-histories origin/main
Before you do that, work out why they are unrelated, because in most cases the answer is that you are on the wrong repository or you re-initialised one by accident.
Tested on git 2.47.
What "unrelated" means
Git history is a directed graph. A merge works by finding the most recent commit reachable from both branches, the merge base, and computing what changed on each side since then.
With no common ancestor there is no merge base. Git cannot tell what changed, so every file on both sides looks like a new file, and every file present in both looks like a conflict.
The flag exists because there are legitimate cases. The default is a refusal because the common case is a mistake.
Why it happened
You initialised a local repository and then added a remote that already had commits
The most frequent cause.
mkdir project && cd project
git init
# write code, commit
git remote add origin [email protected]:me/project.git
git pull origin main # remote has a README from the web UI
Your local history starts at your first commit. The remote's starts at the README commit GitHub created. Two roots, no relationship.
If your local work is the real content and the remote only has a README or a licence:
git pull origin main --allow-unrelated-histories
# resolve conflicts, usually keeping both
Or if you do not care about the remote's initial commit:
git push --force-with-lease origin main
--force-with-lease rather than --force. It refuses if someone else pushed since you last fetched, which is the difference between overwriting your own history and overwriting a colleague's.
You re-ran git init
Deleting .git and running git init again creates a completely new history. The files are identical, the commits are not.
Usually done to escape a confusing state. Check for the old repository before assuming it is gone:
ls -la .git
git reflog # if .git still exists, your commits are probably there
Nine times out of ten the original history is recoverable and re-initialising was the wrong move. The reflog holds a lot more than people expect.
You are on the wrong remote
git remote -v
Fetching from a fork you did not intend, or a repository with a similar name. The histories genuinely are unrelated because they are different projects.
A squashed or rewritten history on the remote
Someone ran a history rewrite, git filter-repo, or force pushed a rebased branch. Every commit sha changed, so your local commits no longer share ancestry with the new remote state.
Confirm:
git log --oneline origin/main | tail -5
git log --oneline main | tail -5
Different root commits means the history was rewritten. Coordinate with whoever did it rather than merging, because merging produces a duplicated history that is genuinely unpleasant to untangle.
The legitimate cases
Merging one repository into another as a subdirectory
You have two projects and want to combine them, preserving both histories.
git remote add other ../other-project
git fetch other
git merge --allow-unrelated-histories other/main
That merges the other project's files into your root, which is usually not what you want. To put it in a subdirectory with its history intact, use a subtree merge:
git remote add other ../other-project
git fetch other
git merge -s ours --no-commit --allow-unrelated-histories other/main
git read-tree --prefix=packages/other/ -u other/main
git commit -m "merge other-project into packages/other"
-s ours takes your tree and records the other branch as a parent, then read-tree places its files under a prefix. History from both sides is reachable, and git log --follow works on the moved files.
For a monorepo migration, git subtree add does the same thing with a friendlier interface:
git subtree add --prefix=packages/other ../other-project main
Adopting an orphan branch
Documentation sites frequently live on an orphan branch:
git checkout --orphan gh-pages
git rm -rf .
# add site files
git commit -m "initial site"
An orphan branch has no parent by design. If you later want to merge it into main, you need the flag.
Combining a template with an existing project
You started from a template repository and now want to pull in upstream template changes. The histories diverged at the point the template was copied rather than forked.
What the merge looks like
Prepare for a lot of conflicts. Every file present in both trees conflicts, because git has no base to compare against.
git merge --allow-unrelated-histories other/main
# CONFLICT (add/add): Merge conflict in README.md
# CONFLICT (add/add): Merge conflict in .gitignore
# CONFLICT (add/add): Merge conflict in package.json
add/add is the signature. Both sides added the file, git has no idea which is correct.
To resolve wholesale in one direction:
git checkout --ours README.md # keep your version
git checkout --theirs LICENSE # keep theirs
git add README.md LICENSE
Note that --ours and --theirs mean the opposite of what you might expect during a rebase, where the sides are swapped. During a merge, ours is the branch you are on.
For package.json and lockfiles, resolve the manifest by hand and then regenerate the lockfile rather than merging it:
rm package-lock.json
npm install
Merging lockfiles by hand produces dependency trees that satisfy neither side, and it is a reliable way to create a build that works locally and fails in CI.
Backing out
If the merge goes badly:
git merge --abort
If you already committed:
git reset --hard ORIG_HEAD
ORIG_HEAD points at where you were before the last merge or rebase, which makes this the fastest recovery available.
Preventing it
Clone rather than init when the remote exists.
git clone [email protected]:me/project.git
Obvious, and the mistake happens because people create the repository on GitHub with a README while also having local work.
When creating a repository on GitHub for existing local work, create it empty. No README, no licence, no gitignore. Then git remote add and push cleanly.
Check before you pull into a fresh repository:
git log --oneline origin/main -1
git log --oneline main -1
Two different root commits is your warning.
Do not delete .git to fix a problem. Almost every git state is recoverable, and re-initialising throws away the recovery path along with the problem.