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以降は安全装置として自動で拒否するようになりました。
# 通常のマージ: 共通祖先 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パターンから自分の状況を特定しましょう。
注意:「単にローカルが古いだけ」は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つに統合されます。
# 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.md/git checkout --theirs README.mdで一気に解決できます。
解決②:ローカルを破棄して再cloneする
「GitHub側を正とする」「ローカルの作業はまだ捨てられる」場合、再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 subtreeやgit submoduleで別サブツリーとして取り込むほうが履歴がきれいに保てます。
# 他プロジェクトを 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
# サブモジュールとして追加 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な履歴でも、コミット単体は別の場所に適用できます。
# 相手のリポを 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+ファイルコピー:最もクリーン(解決②)
# マージコミットを作らず、相手の変更を自分の次のコミットに押し込む 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
シナリオ③ 間違ったリモートを指定していた
# 正しいリモートに変更 git remote set-url origin https://github.com/USER/CORRECT_REPO.git git remote -v # 改めてpull(正しい履歴なら unrelated にならない) git pull origin main
シナリオ④ filter-repo後のリモートと整合させたい
# 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でリポ作成時にテンプレートを使う場合は影響範囲を事前確認
# 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はマージを許可する、--forceはpushでリモートを上書きする。用途が違うので混同しないよう注意してください。
マージ後に大量のconflictを放置
--allow-unrelated-historiesでマージするとREADME・.gitignoreなど同名ファイル全部でconflictが発生するケースが多いです。慌ててgit merge --abortや無理なforce pushで逃げず、1ファイルずつ丁寧に解消してください。--ours/--theirsで片方採用すれば素早く片付きます。
–rebase=ignore などの誤オプション
ネットの古い記事にgit pull --rebase=ignoreといった存在しないオプションを勧めているものがあります。現行のGitで--rebaseが取る値はtrue/false/merges/interactiveのいずれかです。unrelated historiesの解決には--allow-unrelated-historiesを使ってください。
再cloneで未push作業を失う
「再cloneがクリーン」ということでいきなりrm -rfすると未pushのコミットがすべて消えます。必ずバックアップを取り、重要なブランチはpush済みか、あるいはコピーしてから実行しましょう。reflogもディレクトリごと消えるので、削除後の復旧はほぼ不可能です。
よくある質問
git pull --rebase origin mainを普通に実行すれば相手コミットが順番に適用されます。ただしマージコミットは作られず、相手の履歴情報はリニアに並べ直される点に注意。git push --forceで押し込むと成立しますが、リモートの履歴を破壊するので最終手段。共有リポではrevertや--allow-unrelated-historiesでのマージを優先しましょう。git checkout --theirs README.md、「ローカル側を採用」ならgit checkout --ours README.mdのあとgit add README.mdで解消できます。手動編集ならVS CodeのAccept系ボタンが便利です。git push -u origin mainが一番スムーズです。このエラーに遭遇する必要がそもそもなくなります。--allow-unrelated-historiesでマージするのが最も履歴を残せる選択です。マージコミットが残ってそれぞれの初期コミットに遡れるため、「いつ/どこから合流したか」も分かります。pullが走ります。古いコミットが消える等の違和感があればgit reflogで直前位置を確認→git reset --hard HEAD@{N}で戻せます。履歴食い違いの対処はリモートとローカルの履歴が食い違ったときの同期方法も参照。関連記事
- 【Git】pushしようとしたら”non-fast-forward”で拒否されたときの解決方法 — 別系統のpushエラー
- 【Git】リモートとローカルの履歴が食い違ったときの同期方法 — 通常のズレ対処
- 【Git】「Pulling is not possible because you have unmerged files」の解決方法 — マージ中conflictの別エラー
- 【Git】「does not appear to be a git repository」エラー — リモート未登録エラー
- 【Git】履歴に含まれる機密情報を完全に削除する方法 — filter-repo後の注意
- 【Git】マージの取り消し方法 — マージ結果が気に入らない場合
- 【Git】submoduleの使い方と管理のベストプラクティス — 別リポを分離保持する選択肢
- 【Git】よく使うgitコマンドまとめ — 日常コマンドの早見表
まとめ
- 「unrelated histories」は共通祖先(merge-base)不在のマージを拒否する安全装置
- 診断は
git merge-baseとgit 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で始めれば、このエラーには遭遇しなくなります。
