【Git】コンフリクトを解消したのにpullできないときの5パターン診断と解決・予防完全ガイド

【Git】コンフリクトを解消したのにpullできないときの原因と対策 Git

「conflictマーカーを全部消して保存したのに、なぜかpullやpushが通らない」——Gitでコンフリクト解消後に頻発する悩みです。エディタ上では変更が完了しているのに、Gitには「解消したこと」が認識されていない状態。典型的なエラーは次のように出ます。

よくあるエラー
error: You have not concluded your merge (MERGE_HEAD exists).
hint: Please, commit your changes before merging.

fatal: Exiting because of unresolved conflict.

この記事では「解消したのに進めない」5つの典型パターンを診断フロー付きで解説します。マーカー取り残しcommit忘れrebase中のcontinue忘れpush時のnon-ffstash apply途中など原因はさまざま。状態を正しく読めば、慌てずに解消→完了→同期の流れに戻せます。

この記事で学べること

  • 「解消したのに進まない」5つの典型パターン
  • git statusgit diff --checkによる漏れ検出
  • MERGE_HEAD/REBASE_HEADの扱いと完了/中断の切替
  • conflict markerの取り残しをgit grepで確実に排除する方法
  • 解消後にpushが拒否される(non-fast-forward)原因の見分け方
  • GUIツール・3-way merge editorを使った再発防止
  • rerereで同じconflictを2度目以降自動解決する仕組み
スポンサーリンク

「解消したのに進まない」5パターン早見表

エラーメッセージのキーワードgit statusの出力から、どのパターンに該当するか特定しましょう。

パターン エラー/現象 対処
① 解消後にcommit忘れ MERGE_HEAD exists git addgit commit
② conflictマーカーが一部残る commit後もビルド失敗/git diff --checkで検出 マーカー除去→再add→commit
③ rebase中のcontinue忘れ “You are currently rebasing” git addgit rebase --continue
④ 解消後にpushでnon-ff rejected (non-fast-forward) pull --rebase→push
⑤ stash apply/cherry-pick中の未完了 “currently cherry-picking”等 --continue--abort

ポイント:大半は「続きの操作を忘れている」だけです。git statusの最後の2行(hint部分)に次に何をすべきかが書かれていることが多いので、焦らず読み込みましょう。

STEP 0:Gitの状態を正確に読む

診断コマンドセット
# 基本の状態確認
git status
# "All conflicts fixed" や "You are currently rebasing"などヒントが出る

# マージ進行中を示すファイルの有無
ls .git/MERGE_HEAD 2>/dev/null && echo "マージ中"
ls .git/REBASE_HEAD 2>/dev/null && echo "rebase中"
ls .git/CHERRY_PICK_HEAD 2>/dev/null && echo "cherry-pick中"

# 未解決ファイル一覧
git diff --name-only --diff-filter=U

# conflictマーカー取り残しを検出(重要)
git grep -n "<<<<<<< " -- '*'
git grep -n "=======" -- '*'
git grep -n ">>>>>>> " -- '*'

# 空白やマーカー由来の問題を一気に検出
git diff --check

状態を示す重要ファイル

  • .git/MERGE_HEAD:マージ進行中(消えていればマージ完了)
  • .git/REBASE_HEAD:rebase進行中
  • .git/CHERRY_PICK_HEAD:cherry-pick進行中
  • .git/REVERT_HEAD:revert進行中
  • .git/MERGE_MSG:マージcommitメッセージのテンプレ

パターン①:解消したのにcommitしていない

もっとも多いパターンです。エディタでマーカーを消して保存したものの、git addgit commitを実行していない状態。Gitは「まだマージ進行中」と見なし、他のマージやpullを拒否します。

完了させる手順
# 解消したファイルをステージ
git add path/to/conflicted_file
git add .                          # 一括ならこちら

# マージコミットを作成(メッセージはテンプレが入る)
git commit
# またはメッセージ指定
git commit -m "Merge branch 'feature' into main"

# 状態確認
git status
# → nothing to commit, working tree clean

# ここで MERGE_HEAD は自動削除される
ls .git/MERGE_HEAD 2>/dev/null
# → 何も出なければマージ完了

mergeコミットの扱い

  • git commit引数なしでエディタが開く(テンプレ入り)
  • そのまま閉じれば既定メッセージでcommit完了
  • メッセージを編集したければエディタで修正
  • マージコミットを残したくない場合はgit merge --squashで実行(別履歴設計)

パターン②:conflictマーカーが一部残っている

ファイルの一箇所は直したが、別のconflict箇所のマーカー削除を忘れてcommitしてしまうパターン。Gitはaddされていればcommitを通しますが、マーカーが残ったコードは構文エラーになりビルド失敗・テスト失敗・実行時クラッシュを招きます。

マーカー残りを検出
# conflictマーカー3種を全ファイルから検索
git grep -nE "^(<<<<<<< |=======|>>>>>>> )"
# 何か出れば残っている

# git diff --check でもマーカー検出可能
git diff --cached --check
git diff HEAD --check

# 検索対象を限定
git grep -nE "<<<<<<< " -- "*.py" "*.js"
取り残しを直して再commit
# マーカーを手動またはツールで削除
vim path/to/file.py

# 変更をstage
git add path/to/file.py

# amendで直前commitを上書き
git commit --amend --no-edit

# push済みの場合は個人ブランチのみforce-with-lease
git push --force-with-lease origin <branch>

注意:チームでpush済みのcommitにマーカーが残っていた場合、慌ててforce pushしないでください。他メンバーの作業を破壊します。共有ブランチでは修正コミットを追加fix: conflict markerを除去)するのが安全。amend+force-with-leaseは個人ブランチに限る運用に。

ポイント:pre-commit hookでgit grep "<<<<<<< "を実行させるとマーカー残りを自動検出できます。huskylint-staged等で簡単に導入可。

パターン③:rebase中にcontinueを忘れている

pull –rebaseやrebase -iでconflict解消した後、git rebase --continueを実行せずにpullしようとすると「進行中」として拒否されます。

rebaseを正しく終わらせる
# 状態確認
git status
# "You are currently rebasing branch 'xxx' on 'yyy'"

# 解消したファイルをstage
git add path/to/conflicted_file

# 続行(残りのコミット適用)
git rebase --continue

# 次のコミットでもconflictが出たら同様に解決→continue

# やり直したいとき
git rebase --abort

# 現在のcommitを捨てたいなら(慎重に)
git rebase --skip

rebase中の詳しいトラブル対処はリベース途中でエラーになったときの復旧方法、unmerged状態で詰まる別ケースは「Pulling is not possible because you have unmerged files」の解決方法を参照してください。

パターン④:解消&commit済みだがpushでnon-fast-forward

マージ自体は正しく完了しているのに、pushで「rejected」と出るパターン。解消作業中に他メンバーが先にpushしてリモートが進んでしまった、という時に発生します。conflict解消の罠とは別問題ですが、混同されやすいので押さえておきましょう。

non-ff エラーの対処
git push origin main
# → rejected (non-fast-forward)

# リモートの最新を取り込む
git pull --rebase origin main

# 再度conflictが出たら同じ手順で解消
git add path/to/file
git rebase --continue

# 再push
git push origin main

non-ffの詳細はpushしようとしたら”non-fast-forward”で拒否されたときの解決方法も参照。

パターン⑤:stash applyやcherry-pick中の未完了

マージやrebaseだけでなく、stash applycherry-pickrevert中にもconflictは発生します。これらもMERGE_HEAD相当の進行中ファイルが残るため、解消→完了までやらないと次の操作が拒否されます。

各オペレーションの完了/中断
# cherry-pick
git add path/to/file
git cherry-pick --continue
# または中止
git cherry-pick --abort

# revert
git add path/to/file
git revert --continue
git revert --abort

# stash apply中にconflict
# stashは"pop"せずに"apply"のままstash entry残る
git add path/to/file
git commit            # applyの結果を保全するcommit
git stash drop        # 必要ならstash entry削除

各操作の進行中ファイル

  • merge → .git/MERGE_HEAD
  • rebase → .git/REBASE_HEAD.git/rebase-merge/
  • cherry-pick → .git/CHERRY_PICK_HEAD
  • revert → .git/REVERT_HEAD
  • 一覧確認:ls .git/ | grep -iE "HEAD|rebase|merge"

再発防止:rerere と pre-commit hook

rerere(同じconflictを覚える仕組み)

「同じ場所で何度もconflictに遭う」ならrerere(REuse REcorded REsolution)を有効にすると、一度解決した結果を記録し、次回同じ衝突に出会ったら自動で同じ方法で解決します。

rerere の有効化
# 全リポジトリで有効化
git config --global rerere.enabled true

# 有効化後、conflictを解消すると自動記録される
git add path/to/file
git commit

# 記録された解決策の確認
git rerere diff

# 特定の記録を忘れる(解決策を間違えた場合)
git rerere forget path/to/file

# 記録の自動クリーンアップ期間(90日が既定)
git config --global gc.rerereResolved 90

pre-commit hookでマーカー検出を強制

.git/hooks/pre-commit
#!/bin/sh
# conflict marker 検出フック
if git diff --cached --check; then
  :
else
  echo "Error: conflict marker or whitespace issue detected"
  exit 1
fi

# より厳密なチェック
if git grep --cached -lE "^(&lt;&lt;&lt;&lt;&lt;&lt;&lt; |=======|&gt;&gt;&gt;&gt;&gt;&gt;&gt; )"; then
  echo "Error: unresolved conflict markers in staged files"
  exit 1
fi
exit 0

ポイント:rerereは「過去の解決法を機械的に再現」します。pre-commit hookは「commit前にマーカー残りを検出」します。両方組み合わせれば、conflict解消に関わる事故はほぼゼロに近づきます。チーム共通化するならhuskeypre-commitフレームワークで配布しましょう。

実践シナリオ

シナリオ① 解消したはずがcommit忘れ

完了させる
git status
# → all conflicts fixed: run "git commit" to conclude merge

git commit
# エディタが開くのでそのまま保存

git pull            # 以降のpullが通るようになる

シナリオ② commit済みだがconflictマーカーが1箇所だけ残った

amendで修正
# 取り残し検出
git grep -nE "^(&lt;&lt;&lt;&lt;&lt;&lt;&lt; |=======|&gt;&gt;&gt;&gt;&gt;&gt;&gt; )"
# src/auth.ts:42:<<<<<<< HEAD

vim src/auth.ts

git add src/auth.ts
git commit --amend --no-edit

# 個人ブランチなら
git push --force-with-lease

シナリオ③ rebase中のcontinue忘れ

正しく続行
git status
# → You are currently rebasing

git add .
git rebase --continue
# 次のcommitもconflictが来たら同じことの繰り返し

シナリオ④ 解消後のpushでnon-ff

リモート同期
git pull --rebase origin main
# 再度conflictが来たら解消→continue
git push origin main

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

.git/MERGE_HEAD を手で削除してごまかす

「マージ中状態が邪魔だから」とrm .git/MERGE_HEADするとGitは「マージ完了」と誤認しますが、indexの未解決エントリは残るため未整合な状態になります。正しくはgit merge --abortgit addgit commitGitに完了を教えること。

マーカー残りに気付かずビルド/デプロイ

マーカーはほとんどの言語で構文エラーになり、ステージ/テスト環境で発覚します。CIでlintやビルドが走っていれば止まりますが、手元の検証で見逃して本番に出すと障害に直結。git diff --checkをpre-commit hookに入れておきましょう。

解消後にforce pushで無理やり通す

「pushが通らないから強制」は根本原因(マーカー残り・commit忘れ・non-ff)を解決しません。共有ブランチでforce pushすると他メンバーの作業を上書きする二次災害が起きます。まずはgit statusのhintを読み、正しい手順で完了させることに集中を。

rebase中にmerge –abortを実行する

進行中のオペレーションごとに中止コマンドが異なります。rebase中はgit rebase --abort、cherry-pick中はgit cherry-pick --abort等、正しい対象を指定しましょう。git statusに書かれているhintが最も確実です。

解消のためにconflict箇所を削除してしまう

慌てて<<<<<<<>>>>>>>の範囲を丸ごと削除すると、両方の変更が失われます。意図した方の変更を残すか両方を統合するかを意識して編集しましょう。--ours--theirsで片方採用も選択肢です。

よくある質問

Qcommit したのに MERGE_HEAD が消えない
A稀にファイルシステムの問題等で残るケースがあります。git statusがクリーン状態を表示しているなら.git/MERGE_HEADを手動削除しても安全です。ただし本来はgit commitで自動削除されるので、先にstageとcommitの状態をもう一度確認してください。
Qgit pull で毎回同じconflictが出る
Arerereを有効化すると2回目以降自動解決されます。git config --global rerere.enabled trueで即オン。また「何度も同じ場所」ならそのファイルの責任範囲(複数人が同時編集する箇所)を見直して、設計上の分割を検討するのも有効です。
Qマーカーが大量にあって手作業で直せない
AVS CodeやJetBrainsの3-way merge editorを使うと、「Current」「Incoming」「Both」を選ぶだけで解消できます。git mergetoolでmeld/kdiff3等のGUIツールも起動可能。大量conflictならGUI環境で解決するのが効率的です。
Qgit add . してもcommitでエラー
Apre-commit hookが拒否しているか、まだ他ファイルにunmerged pathが残っている可能性。git statusで詳細を確認、git diff --cached --checkでスタイル違反も確認しましょう。
Q解消結果が正しいか確認したい
Agit diff --cachedでstaged内容確認、git diff --ours--theirsでマージ先/取り込み側との差を確認できます。テスト実行で挙動確認、CIで他チェックも通しておくのが確実です。
Qマージ解消後にpushがrejectedになる
Aパターン④(non-fast-forward)です。git pull --rebaseで最新を取り込んでから再push。詳細はpushしようとしたら”non-fast-forward”で拒否されたときの解決方法へ。
Q全部やり直したい
Agit merge --abort(またはrebase --abort等)で開始前に戻れます。解消中の手作業はすべて破棄されるので、必要ならまず作業ファイルをバックアップしてから実行。

関連記事

まとめ

  • 「解消したのに進まない」は5パターン:commit忘れ/マーカー残/rebase continue忘れ/non-ff/cherry-pick中
  • まずgit statusのhintを読み、.git/*_HEADで進行中オペを特定
  • マーカー残検出はgit grep "<<<<<<< "git diff --check
  • マージ完了はgit addgit commit、rebaseは--continue
  • 解消後のpush拒否は別問題(non-ff)——pull --rebaseで解決
  • 再発防止:rerere有効化pre-commit hookでマーカー検出
  • MERGE_HEADを手で消すのは禁物、正規の手順で完了させる

「解消したのに進まない」状態は、Gitがあと一歩の作業を促しているサインです。git statusの最後のヒントは実は的確で、読み落としていた「次の操作」(commit/continue/abort)を教えてくれています。本記事の5パターン早見表とpre-commit hook・rerere設定を組み合わせれば、同じトラブルを繰り返さずに安全にconflict解消のサイクルを回せるようになります。