【Git】「refusing to merge unrelated histories」エラーの原因と対処法|allow-unrelated-histories・再clone・subtreeの使い分け

GitHubに新規リポジトリを作成し、ローカルからgit pullした瞬間に次のようなエラーが出て止まった経験はありませんか。

典型的なエラー
$ git pull origin main
fatal: refusing to merge unrelated histories

これは「マージしようとしている2つのブランチが共通の祖先を持たない」つまり「無関係な歴史」を Git が検出して、安全のため拒否したメッセージです。GitHubでREADME付きリポジトリを作った後にローカルをgit initしてpullした、別々に管理していた2つのリポジトリを統合した、といった場面で頻出します。

この記事では「refusing to merge unrelated histories」エラーの本当の原因状況別の対処法を整理します。--allow-unrelated-historiesの正しい使い方だけでなく、「そもそもマージすべきか」の判断軸、subtree/submodule/再clone等の代替アプローチまでまとめ、実務で迷わず選べるようにします。

この記事で学べること

  • 「unrelated histories」エラーがなぜ発生するか(merge-base不在)
  • 6つの典型的な発生シナリオ(init+pull/fork失敗/filter-repo後 など)
  • 主流の解決法:--allow-unrelated-historiesの正しい使い方
  • 代替:ローカル破棄+再clone/subtree/submoduleによる統合
  • マージ後に残るマージコミットの意味と扱い
  • 同じエラーを出さないための予防策
  • GitHubでREADME付きリポジトリを作ってしまった時のスマートな回避
スポンサーリンク

エラーの正体:共通の祖先が無い2つの履歴

Gitのマージはmerge-base(共通祖先コミット)を基準に差分を合成します。この基準が存在しない、つまりお互いに関係のない履歴をマージしようとするとデータを壊す危険があるため、Git 2.9以降は安全装置として自動で拒否するようになりました。

merge-base が無いとは
# 通常のマージ: 共通祖先 C を基準に変更を合成
#        A1 - A2 - A3   ← mainブランチ
#       /
#   C -
#       \
#        B1 - B2        ← feature/xxx
# → merge-base = C

# unrelated histories: 共通祖先が無い
#   A1 - A2 - A3   ← ローカル main
#   B1 - B2 - B3   ← リモート main
# → merge-base が無い → refusing to merge

ポイント:「無関係な履歴」の本質はmerge-base不在です。片方はgit initから始まり、もう片方は別のgit initから独立して成長した——この状態を見抜くのが解決の出発点。git log --oneline --all --graphで両方の履歴が「完全に平行」に並んでいれば確定です。

典型的な発生シナリオ

どのシナリオに該当するかで最適な対処法が変わります。以下6パターンから自分の状況を特定しましょう。

シナリオ 発生経緯
① GitHubでREADME付きリポ作成+ローカルinit GitHub側でもコミットが発生、ローカルにも別のコミットがある
② 別々にinitした2プロジェクトを統合 モノレポ化の際に多い
③ 意図せずfork外のリポを追加してpull remoteを取り違えた/似名リポの誤指定
④ filter-repo/filter-branch後 履歴書き換えで全SHAが変わり、旧リモートとunrelated化
⑤ 空リポから clone → init でやり直し .git を削除してやり直すと元リモートと関係を失う
⑥ 別リポのブランチを fetch → merge 複数リモート運用で誤って無関係ブランチをマージ

注意:「単にローカルが古いだけ」はunrelated historiesではなくnon-fast-forwardエラーになります。混同しないよう、まずgit log --oneline --all --graph -30で両履歴の関係を確認しましょう。non-fast-forwardは“non-fast-forward”で拒否されたときの解決方法を参照。

STEP 0:本当に unrelated か診断する

解決に進む前に、本当に両者が無関係なのかを確認します。共通祖先があるのに他の理由で拒否されているケースもあるためです。

共通祖先の有無を確認
# 両ブランチの履歴を図で見る
git fetch --all
git log --oneline --all --graph --decorate -30

# 共通祖先の直接確認
git merge-base HEAD origin/main
# → 共通祖先のSHAが出れば「関係あり」
# → fatal: Not a valid object name / 結果なしなら「unrelated」確定

# ブランチごとの最古コミットを確認
git log --oneline --reverse HEAD | head -1
git log --oneline --reverse origin/main | head -1
# 初期コミットのSHAが完全に違っていたら unrelated

本当にunrelatedか判定チェック

  • git merge-baseが何も返さない → unrelated確定
  • 両ブランチの最初のコミットSHAが異なる → unrelatedの可能性大
  • git log --graphで2本のツリーが平行に並んでいる → unrelated
  • 共通祖先があれば別のエラー(non-fast-forward等)なので対処法も変わる

解決①:–allow-unrelated-histories を使う(主流)

もっとも使われるのが--allow-unrelated-historiesオプションです。「無関係なのは分かっている、それでも合成して良い」とGitに明示的に許可を出します。結果として通常のマージコミットが作られ、両方のツリーが1つに統合されます。

–allow-unrelated-histories の使い方
# pullで一気に取り込む(内部的にはfetch+merge)
git pull origin main --allow-unrelated-histories

# fetch と merge を分けて段階的に
git fetch origin main
git merge origin/main --allow-unrelated-histories

# rebase で取り込みたい場合
git pull --rebase origin main
# ※ rebase系ではこのフラグは不要(内部的に単にコミットを並べる)

# コンフリクトが起きたら通常通り解決
git add .
git commit

ポイント:--allow-unrelated-histories明示的オプト-インの安全装置で、毎回つけ忘れても害はありません。「本当に混ぜていいのか?」を確認する機会を強制するためにGit 2.9で追加されたものなので、むしろエラーが出るのは健全な挙動と考えましょう。

GitHubでREADME付きリポを作ってしまったケース

最頻出パターンの修正
# 状況:GitHubでREADME付きリポを作成
# ローカルでも git init してコミット済み

# GitHub側のREADMEと統合する
git remote add origin https://github.com/USER/REPO.git
git pull origin main --allow-unrelated-histories

# コンフリクト解消(READMEが両方にあれば)
# 手動でファイル編集 → add → commit

# push
git push -u origin main

注意:マージ後はREADMEや.gitignoreといった同名ファイルが両方からマージされてコンフリクトが発生しやすいです。片方を完全採用したいならgit checkout --ours README.mdgit checkout --theirs README.mdで一気に解決できます。

解決②:ローカルを破棄して再cloneする

「GitHub側を正とする」「ローカルの作業はまだ捨てられる」場合、再cloneが最もクリーンです。マージコミットが履歴に残らず、あとから見返したときに分かりやすい履歴になります。

再cloneする
# ローカルの作業を念のため退避
cp -r your-project your-project.backup

# 既存ローカルを削除
rm -rf your-project

# 改めてclone
git clone https://github.com/USER/REPO.git your-project
cd your-project

# ローカルの変更が必要ならbackupからコピー
cp -r ../your-project.backup/src/* src/
git add .
git commit -m "feat: ローカル作業を取り込み"
git push

ポイント:再cloneは「ローカルの.git+すべての履歴」を捨てる破壊的な操作です。未pushの作業がある場合は、必要なファイルだけバックアップしてから実行してください。迷ったら--allow-unrelated-historiesで一度マージし、後で不要なファイルを削除するほうが安全な場合もあります。

解決③:2つのプロジェクトは統合せず分離保持する

「別プロジェクトだが1リポジトリで管理したい」という場合、unrelatedなのは自然な状態です。無理にマージせずgit subtreegit submoduleで別サブツリーとして取り込むほうが履歴がきれいに保てます。

subtreeで別リポを取り込む
# 他プロジェクトを sub-project/ ディレクトリとして取り込み
git remote add -f other-repo https://github.com/USER/OTHER.git
git subtree add --prefix=sub-project other-repo main --squash

# subtreeの更新取り込み
git subtree pull --prefix=sub-project other-repo main --squash

# subtreeから他リポへの送信
git subtree push --prefix=sub-project other-repo main
submoduleで分離保持
# サブモジュールとして追加
git submodule add https://github.com/USER/OTHER.git sub-project
git commit -m "feat: sub-project を submodule として追加"
git push

# クローン時
git clone --recurse-submodules https://github.com/USER/REPO.git
# 後から初期化する場合
git submodule update --init --recursive

subtree と submodule の使い分け

  • subtree:取り込んだコードが本体リポの履歴に含まれる。clone/pullの追加作業が不要
  • submodule:サブプロジェクトは別リポのまま参照。本体リポにはコミットSHAだけ記録
  • シンプルなコード共有ならsubtree、厳密にバージョンロックしたいならsubmodule
  • submoduleの詳細運用は公式ドキュメントやチームポリシーに合わせる

解決④:手動で必要なコミットだけcherry-pick

「全部マージしたいわけではなく、一部のコミットだけ取り込みたい」場合は、cherry-pickで個別コミットを選んで持ち込みます。unrelatedな履歴でも、コミット単体は別の場所に適用できます。

cherry-pick で部分取り込み
# 相手のリポを remote として追加
git remote add other https://github.com/USER/OTHER.git
git fetch other

# 取り込みたいコミットの範囲を確認
git log --oneline other/main -20

# 単一コミットを持ってくる
git cherry-pick <SHA>

# 複数コミットを順番に
git cherry-pick <SHA1> <SHA2> <SHA3>

# 範囲指定
git cherry-pick <古いSHA>..<新しいSHA>

# コンフリクト時は通常通り解決
git add .
git cherry-pick --continue

ポイント:cherry-pickは「履歴を混ぜずにコードだけ取り込む」方法です。他リポの一部を参考に採用したい、特定のバグ修正だけ欲しい、といった場面に最適。大量のコミットを個別にpickするのは大変なので、多量なら--allow-unrelated-histories、少量ならcherry-pickと使い分けましょう。

マージ後に残るマージコミットの扱い

--allow-unrelated-historiesでマージした場合、履歴には2つの初期コミット(親を複数持つマージコミット)が残ります。「きれいな一本線」になれないため、見返したときに違和感を感じる人もいます。

マージコミットを許容するか/綺麗にするか

  • そのまま残す:統合の事実を履歴に刻む。チーム開発では自然
  • filter-repoで片方の履歴を書き換え:複雑かつ破壊的、最終手段
  • squash合流--squashで1コミットに圧縮して新履歴として残す
  • 再clone+ファイルコピー:最もクリーン(解決②)
squashでの合流
# マージコミットを作らず、相手の変更を自分の次のコミットに押し込む
git merge --squash origin/main --allow-unrelated-histories
git commit -m "feat: 他リポの内容を取り込み(squash)"

# pull時にsquash
git pull --squash origin main --allow-unrelated-histories

注意:squashは相手の履歴情報が失われるのと引き換えにリニアな履歴を得ます。「誰がいつ何をコミットしたか」がまとめられて1行のメッセージになるため、後から追跡しにくくなるデメリットがあります。

実践シナリオ

シナリオ① GitHub側にREADMEを作ってしまった

最も多いパターンの解決
# 現状確認
git log --oneline --all --graph
# → ローカルとorigin/mainが平行に並んでいる

# 統合
git pull origin main --allow-unrelated-histories

# README重複でconflict
# どちらを採用するか決めて編集 → add → commit

git push -u origin main

シナリオ② 別々に管理していた2リポジトリを統合

モノレポ化
# 統合先(メインリポ)
cd main-repo
git remote add sub ../sub-repo
git fetch sub

# 統合(履歴も残す)
git merge sub/main --allow-unrelated-histories

# ファイル衝突があれば解消
git add .
git commit

# 整理後 push
git push

シナリオ③ 間違ったリモートを指定していた

remote を付け替える
# 正しいリモートに変更
git remote set-url origin https://github.com/USER/CORRECT_REPO.git
git remote -v

# 改めてpull(正しい履歴なら unrelated にならない)
git pull origin main

シナリオ④ filter-repo後のリモートと整合させたい

履歴書き換え後は再cloneが最安全
# filter-repoでmainを書き換え後、push --force で反映
git push --force-with-lease origin main

# 他メンバーは再cloneが安全
# (無理にpullするとunrelatedエラー)
rm -rf your-project
git clone https://github.com/USER/REPO.git

履歴書き換え関連は履歴に含まれる機密情報を完全に削除する方法も参照。

同じエラーを出さないための予防策

ベストプラクティス

  • GitHubで新規リポを作る時は「Initialize this repository with a README」のチェックを外す
  • ローカル未作業ならgit clone、ローカルに既存コードがあるなら空リポにgit push
  • remote URLはgit remote -vで必ず確認してからpull
  • 複数リポ統合が多いチームはモノレポ設計/subtree運用を検討
  • filter-repo後は全メンバー再cloneをアナウンス必須
  • GitHubでリポ作成時にテンプレートを使う場合は影響範囲を事前確認
GitHubへ既存ローカルを初回プッシュする定番手順
# 1. GitHubで空リポ作成(READMEチェックなし)
# 2. ローカルで初期化してpush
cd your-project
git init
git branch -M main
git add .
git commit -m "feat: initial commit"
git remote add origin https://github.com/USER/REPO.git
git push -u origin main
# → unrelated historiesエラーが発生しない

やってはいけない落とし穴

エラーを見ずに –force push で押し切る

「エラーが出たから強制push!」でgit push --forceすると、リモートの履歴を丸ごと上書きしてしまいます。GitHub側のREADMEだけでなく、他メンバーの作業が消える可能性も。まずは--allow-unrelated-historiesか再cloneを検討しましょう。

-f / –force をallow-unrelated-historiesと混同する

どちらも「無理やり操作する」系ですが、対象が違います。--allow-unrelated-historiesマージを許可する--forcepushでリモートを上書きする。用途が違うので混同しないよう注意してください。

マージ後に大量のconflictを放置

--allow-unrelated-historiesでマージするとREADME・.gitignoreなど同名ファイル全部でconflictが発生するケースが多いです。慌ててgit merge --abortや無理なforce pushで逃げず、1ファイルずつ丁寧に解消してください。--ours--theirsで片方採用すれば素早く片付きます。

–rebase=ignore などの誤オプション

ネットの古い記事にgit pull --rebase=ignoreといった存在しないオプションを勧めているものがあります。現行のGitで--rebaseが取る値はtruefalsemergesinteractiveのいずれかです。unrelated historiesの解決には--allow-unrelated-historiesを使ってください。

再cloneで未push作業を失う

「再cloneがクリーン」ということでいきなりrm -rfすると未pushのコミットがすべて消えます。必ずバックアップを取り、重要なブランチはpush済みか、あるいはコピーしてから実行しましょう。reflogもディレクトリごと消えるので、削除後の復旧はほぼ不可能です。

よくある質問

Q毎回 –allow-unrelated-histories を付けないとダメ?
A必要なのは「最初の1回」だけです。合流した後は共通祖先ができるため、以降のpull/merge/rebaseでは不要になります。「毎回必要」な状態なら履歴設計が間違っている可能性があるので、subtree/submodule化を検討してください。
Qrebase で –allow-unrelated-histories 相当はある?
Arebaseはそもそも共通祖先不要で動くコマンドなので、同名オプションはありません。git pull --rebase origin mainを普通に実行すれば相手コミットが順番に適用されます。ただしマージコミットは作られず、相手の履歴情報はリニアに並べ直される点に注意。
Qpushでも unrelated histories になる?
Apush自体のエラーは通常「non-fast-forward」です。共通祖先が無い場合はgit push --forceで押し込むと成立しますが、リモートの履歴を破壊するので最終手段。共有リポではrevert--allow-unrelated-historiesでのマージを優先しましょう。
QREADMEのconflictを手早く解消したい
A「GitHub側を採用」ならgit checkout --theirs README.md、「ローカル側を採用」ならgit checkout --ours README.mdのあとgit add README.mdで解消できます。手動編集ならVS CodeのAccept系ボタンが便利です。
Qローカルで init して空のGitHubリポにpushした方が良い?
Aはい。既存のローカルファイルがあるなら、GitHub側は空リポ(READMEなし)にしてgit push -u origin mainが一番スムーズです。このエラーに遭遇する必要がそもそもなくなります。
Q2つのリポを完全統合したいが履歴は残したい
A--allow-unrelated-historiesでマージするのが最も履歴を残せる選択です。マージコミットが残ってそれぞれの初期コミットに遡れるため、「いつ/どこから合流したか」も分かります。
Qエラーは出ないが pull でおかしなことになる
A共通祖先がある状態なら通常のpullが走ります。古いコミットが消える等の違和感があればgit reflogで直前位置を確認→git reset --hard HEAD@{N}で戻せます。履歴食い違いの対処はリモートとローカルの履歴が食い違ったときの同期方法も参照。

関連記事

まとめ

  • 「unrelated histories」は共通祖先(merge-base)不在のマージを拒否する安全装置
  • 診断はgit merge-basegit log --graph --allで確実に
  • 主流の解決:git pull origin main --allow-unrelated-histories
  • ローカルを捨てられるなら再cloneが最もクリーン
  • 別プロジェクトなら統合せずsubtree/submoduleで分離保持も選択肢
  • 一部コミットだけ欲しいならcherry-pickで個別取り込み
  • 予防:GitHubでリポ作る時はREADMEを初期化しない/既存ローカルなら空リポへpush

このエラーに出会う最多パターンは「GitHubでリポジトリを作ってしまった後にローカルからpull」です。--allow-unrelated-historiesでマージが王道、「ローカルは捨てる」なら再clone、「別プロジェクトとして残す」ならsubtree/submoduleと、意図に合わせて3方向から選べます。次回からはGitHub側を空リポにしてgit push -u origin mainで始めれば、このエラーには遭遇しなくなります。