「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-ff・stash apply途中など原因はさまざま。状態を正しく読めば、慌てずに解消→完了→同期の流れに戻せます。
この記事で学べること
- 「解消したのに進まない」5つの典型パターン
git status+git 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の出力から、どのパターンに該当するか特定しましょう。
ポイント:大半は「続きの操作を忘れている」だけです。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 addとgit 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"
# マーカーを手動またはツールで削除 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 "<<<<<<< "を実行させるとマーカー残りを自動検出できます。husky+lint-staged等で簡単に導入可。
パターン③:rebase中にcontinueを忘れている
pull –rebaseやrebase -iでconflict解消した後、git rebase --continueを実行せずにpullしようとすると「進行中」として拒否されます。
# 状態確認 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解消の罠とは別問題ですが、混同されやすいので押さえておきましょう。
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 apply・cherry-pick・revert中にも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)を有効にすると、一度解決した結果を記録し、次回同じ衝突に出会ったら自動で同じ方法で解決します。
# 全リポジトリで有効化 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でマーカー検出を強制
#!/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 "^(<<<<<<< |=======|>>>>>>> )"; then echo "Error: unresolved conflict markers in staged files" exit 1 fi exit 0
ポイント:rerereは「過去の解決法を機械的に再現」します。pre-commit hookは「commit前にマーカー残りを検出」します。両方組み合わせれば、conflict解消に関わる事故はほぼゼロに近づきます。チーム共通化するならhuskey/pre-commitフレームワークで配布しましょう。
実践シナリオ
シナリオ① 解消したはずがcommit忘れ
git status # → all conflicts fixed: run "git commit" to conclude merge git commit # エディタが開くのでそのまま保存 git pull # 以降のpullが通るようになる
シナリオ② commit済みだがconflictマーカーが1箇所だけ残った
# 取り残し検出 git grep -nE "^(<<<<<<< |=======|>>>>>>> )" # 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 --abort/git add+git commitでGitに完了を教えること。
マーカー残りに気付かずビルド/デプロイ
マーカーはほとんどの言語で構文エラーになり、ステージ/テスト環境で発覚します。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で片方採用も選択肢です。
よくある質問
git statusがクリーン状態を表示しているなら.git/MERGE_HEADを手動削除しても安全です。ただし本来はgit commitで自動削除されるので、先にstageとcommitの状態をもう一度確認してください。git config --global rerere.enabled trueで即オン。また「何度も同じ場所」ならそのファイルの責任範囲(複数人が同時編集する箇所)を見直して、設計上の分割を検討するのも有効です。git mergetoolでmeld/kdiff3等のGUIツールも起動可能。大量conflictならGUI環境で解決するのが効率的です。pre-commit hookが拒否しているか、まだ他ファイルにunmerged pathが残っている可能性。git statusで詳細を確認、git diff --cached --checkでスタイル違反も確認しましょう。git diff --cachedでstaged内容確認、git diff --ours/--theirsでマージ先/取り込み側との差を確認できます。テスト実行で挙動確認、CIで他チェックも通しておくのが確実です。git pull --rebaseで最新を取り込んでから再push。詳細はpushしようとしたら”non-fast-forward”で拒否されたときの解決方法へ。git merge --abort(またはrebase --abort等)で開始前に戻れます。解消中の手作業はすべて破棄されるので、必要ならまず作業ファイルをバックアップしてから実行。関連記事
- 【Git】「Pulling is not possible because you have unmerged files」の解決方法 — 進行中マージの脱出
- 【Git】コンフリクトしてpullできないときの対処法完全ガイド — 未コミット変更でのpullエラー
- 【Git】error: you need to resolve your current index first — index未解決エラー
- 【Git】pushしようとしたら”non-fast-forward”で拒否されたときの解決方法 — 解消後のpush拒否
- 【Git】リベース途中でエラーになったときの復旧方法 — rebase中のトラブル
- 【Git】マージの取り消し方法 — 解消をやり直したい場合
- 【Git】リモートとローカルの履歴が食い違ったときの同期方法 — pull戦略の選び方
- 【Git】よく使うgitコマンドまとめ — 日常コマンドの早見表
まとめ
- 「解消したのに進まない」は5パターン:commit忘れ/マーカー残/rebase continue忘れ/non-ff/cherry-pick中
- まず
git statusのhintを読み、.git/*_HEADで進行中オペを特定 - マーカー残検出は
git grep "<<<<<<< "やgit diff --check - マージ完了は
git add+git 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解消のサイクルを回せるようになります。

