コンフリクトの直し方:起きる理由と、解消の手順

公開 更新 書いた人 大林 諒

CONFLICT と表示されて手が止まった人に向けて書きます。ファイルに見慣れない記号が入り、何かを壊したように見えますが、壊れてはいません。この記事では、料金表の同じ行を2人が変えた場面を実際に動かし、表示の読み方、直し方、やめ方を順に追います。GitHubの画面とVS Codeでの直し方も扱います。読み終わると、次に同じ表示が出ても自分で判断できます。

この記事の内容
  1. 先に答えると
  2. コンフリクトは「機械が決められない」という知らせです
  3. 起きる条件と、起きない例
  4. 動かして再現する
  5. やめて元に戻す
  6. GitHubの画面で解消する
  7. VS Codeで解消する
  8. AIに任せるときの注意
  9. Excelや画像は混ぜられません
  10. 減らす方法
  11. できないことと、その代わり
  12. よく詰まるところ
  13. 次に読む

先に答えると

コンフリクトは、同じファイルの同じ場所を2人が別々に変えたため、どちらを残すかをGitが決められない状態です。データは失われていません。Gitが手を止めて、人に判断を求めているだけです。

  • 直し方は3段階です。ファイルの <<<<<<<、=======、>>>>>>> を消して正しい内容にする、git add、git commit
  • やめたいときは git merge --abort で合流前に戻せます
  • GitHubの画面でも「Resolve conflicts」で直せます。単純な行の競合に限ります
  • AIに任せたら、どちらを残したかを差分で必ず確かめます
  • Excelや画像は行単位で混ぜられません。どちらか一方を選ぶことになります

コンフリクトは「機械が決められない」という知らせです

Gitは2つの枝を合流させるとき、枝が分かれた時点の内容と、両方の枝の内容を比べます。片方しか変えていない場所は、変えたほうを自動で採用します。両方が同じ場所を変えていると、どちらが正しいかはGitには分かりません。

サンプル料理教室の料金表で考えます。外注エンジニアBが和食の基本を6,500円に直し、代表は同じ行を6,200円に直しました。どちらが正しいかは料金を決めた人にしか分かりません。ここで勝手に片方を選ばないのがGitの仕事です。

ブランチ(枝)は本筋から分かれて作業する線、マージは枝を合流させることです。本筋の枝を main と呼びます。コンフリクトが起きるのは主にマージのときで(git pull、枝を付け直す rebase、打ち消しの revert でも起きます)、枝の作業中や保存中には起きません。

同じ行を別の金額に変えた2本の枝は、合流点で止まる
同じ行を別の金額に変えた2本の枝は、合流点で止まる

起きる条件と、起きない例

実際に動かして確かめた結果です。

2人がしたこと結果
別のファイルを変えた起きない
同じファイルの離れた行を変えた起きない
同じファイルの同じ行を変えた起きる
同じファイルの隣り合った行を変えた起きる
片方が変更し、もう片方が同じファイルを消した起きる
Excelや画像を両方が変えた起きる

和食の料金と問い合わせの電話番号のように離れた行なら、自動で合流します。

$ git merge --no-edit fix-phone
Auto-merging menu.html
Merge made by the 'ort' strategy.
 menu.html | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

意外なのは隣り合った行です。和食と洋食は別の講座で別の行なのに、並んでいるだけで止まります。

$ git merge fix-neighbor
Auto-merging menu.html
CONFLICT (content): Merge conflict in menu.html
Automatic merge failed; fix conflicts and then commit the result.

逆に、コンフリクトが出なくても内容が正しいとは限りません。代表が menu.html の料金を変え、Bが index.html に古い料金を書いても、別のファイルなのでGitは止まりません。Gitが見ているのは文字の位置だけで、意味は見ていません。

動かして再現する

ここからは本題の場面を、そのままの順で動かします。

2人が同じ行を変える

Bは枝を作って和食の基本を6,500円に変え、pushします。pushはGitHubに送る操作です。この記事では練習用の場所に送っているので、To の行が ../website.git になっています。

$ git switch -c fix-menu-price
Switched to a new branch 'fix-menu-price'

$ git add menu.html

$ git commit -m "和食の基本を6,500円に修正"
[fix-menu-price 7a1962f] 和食の基本を6,500円に修正
 1 file changed, 1 insertion(+), 1 deletion(-)

$ git push -u origin fix-menu-price
To ../website.git
 * [new branch]      fix-menu-price -> fix-menu-price
branch 'fix-menu-price' set up to track 'origin/fix-menu-price'.

同じころ、代表は main で同じ行の名前と金額を変えました。履歴は次のように分かれています。

$ git log --oneline --graph --all
* 3215a2a 和食の基本の名前と料金を修正
| * 7a1962f 和食の基本を6,500円に修正
|/  
* 4ccaca6 最初の版

合流すると CONFLICT と出る

代表が main にBの枝を合流させます。

$ git merge origin/fix-menu-price
Auto-merging menu.html
CONFLICT (content): Merge conflict in menu.html
Automatic merge failed; fix conflicts and then commit the result.

「menu.html で競合したので、直してからコミットしてください」という意味です。Gitはここで止まり、合流の記録はまだ作っていません。git status で状況を見ます。

$ git status
On branch main
Your branch is up to date with 'origin/main'.

You have unmerged paths.
  (fix conflicts and run "git commit")
  (use "git merge --abort" to abort the merge)

Unmerged paths:
  (use "git add <file>..." to mark resolution)
	both modified:   menu.html

no changes added to commit (use "git add" and/or "git commit -a")

both modified が「両方が変えた」ファイルです。ファイルが多いときも、ここに出たものだけ直せば足ります。

ファイルの中の記号を読む

menu.html を開くと、競合した場所に記号が入っています。

$ cat menu.html
<h1>料金表</h1>
<table>
<<<<<<< HEAD
<tr><td>和食の基本(全4回)</td><td>6,200円</td></tr>
=======
<tr><td>和食の基本</td><td>6,500円</td></tr>
>>>>>>> origin/fix-menu-price
<tr><td>家庭の洋食</td><td>6,500円</td></tr>
<tr><td>親子パン教室</td><td>4,800円</td></tr>
<tr><td>季節の和菓子</td><td>5,200円</td></tr>
<tr><td>魚のさばき方</td><td>6,800円</td></tr>
</table>
<p>お問い合わせ: 03-0000-0000</p>
記号意味この例
<<<<<<< HEADここから今いる枝の内容main にいる代表の版
=======2つの版の区切りここより上が代表、下がB
>>>>>>> origin/fix-menu-priceここまでが入ってくる枝の内容Bの版

競合していない行はそのままです。洋食や電話番号の行は触らなくて構いません。

コンフリクトの記号の読み方
コンフリクトの記号の読み方

直して add、commit する

直すとは、3つの記号を消して、残すべき内容だけにすることです。片方を選ぶとは限りません。この場面では、Bに確認して、名前は代表の「全4回」、金額はBの6,500円が正しいと分かったので、両方を生かした1行にします。

$ grep -n 和食 menu.html
3:<tr><td>和食の基本(全4回)</td><td>6,500円</td></tr>

保存したら git add で「直しました」とGitに伝えます。

$ git add menu.html

$ git status
On branch main
Your branch is up to date with 'origin/main'.

All conflicts fixed but you are still merging.
  (use "git commit" to conclude merge)

Changes to be committed:
	modified:   menu.html

git commit を実行すると、既定のメッセージが入った編集画面が開きます。そのまま保存して閉じれば合流の記録ができます。ここまで終えると main の履歴は合流した形になり、git push で送れます。

$ git commit
[main 8d63472] Merge remote-tracking branch 'origin/fix-menu-price'

$ git log --oneline --graph -5
*   8d63472 Merge remote-tracking branch 'origin/fix-menu-price'
|\  
| * 7a1962f 和食の基本を6,500円に修正
* | 3215a2a 和食の基本の名前と料金を修正
|/  
* 4ccaca6 最初の版

$ git push
To ../website.git
   3215a2a..8d63472  main -> main

記号の間に元の内容も出す

3つの記号だけだと、もともと何だったかが見えません。リポジトリに merge.conflictStyle の設定を入れると、||||||| の下に分かれる前の内容が出ます。入れなくても直せますが、判断に迷うことが多いなら役に立ちます。

$ git config --local merge.conflictStyle diff3

この設定で親子パン教室の行を競合させると、該当の部分は次のように出ます。

<<<<<<< HEAD
<tr><td>親子パン教室</td><td>5,000円</td></tr>
||||||| 8d63472
<tr><td>親子パン教室</td><td>4,800円</td></tr>
=======
<tr><td>親子パン教室</td><td>4,900円</td></tr>
>>>>>>> a1

やめて元に戻す

判断がつかない、Bに聞いてからにしたいというときは、やめて構いません。git merge --abort で合流を始める前に戻ります。

$ git merge --abort

$ git status
On branch main
Your branch is up to date with 'origin/main'.

nothing to commit, working tree clean

$ grep -n 和食 menu.html
3:<tr><td>和食の基本(全4回)</td><td>6,200円</td></tr>

記号が消え、代表の版に戻りました。Bの枝もそのまま残っているので、いつでもやり直せます。合流が始まっていないときに実行すると fatal: There is no merge to abort (MERGE_HEAD missing). と出ますが、これは「やめるものがない」という知らせで、害はありません。

注意が1つあります。コミットしていない変更がある状態で合流を始めると、--abort で元に戻せないことがあると公式に書かれています。git merge の前にコミットを済ませておきます。

GitHubの画面で解消する

プルリクエスト(Pull Request、PR)で競合していると、「Merge pull request」のボタンが押せなくなります。プルリクエストは、枝を本筋に入れていいかを確認する依頼のことです。単純な行の競合なら、次の手順で画面の中で直せます。

  1. プルリクエストの下のほうにある「Resolve conflicts」を押す
  2. 編集画面で内容を直し、<<<<<<<、=======、>>>>>>> を消す
  3. 「Mark as resolved」を押す
  4. 他に競合したファイルがあれば左の一覧から選び、同じように直す
  5. 「Commit merge」を押す
  6. 確認が出たら「Create branch and update my pull request」か「I understand, continue updating」で始まるボタンを選ぶ
  7. プルリクエストの画面に戻り、「Merge pull request」を押す

ここで起きていることは手元の操作と少し違います。Commit merge で変わるのは main ではなくBの枝です。main の内容全部をBの枝に合流させて、そこで競合を片付けています。main が変わるのは7の時点です。

「Resolve conflicts」が押せないときは、ファイルの削除と変更がぶつかった場合など、画面で扱えない競合です。手元のコマンドで直します。保護された枝がからむ場合は、新しい枝を作るよう求められます。

GitHubの Resolve conflicts の画面
GitHubの Resolve conflicts の画面

VS Codeで解消する

VS Codeはコードを編集するためのエディタで、Gitの競合を画面で直せます。競合したファイルを開くと、該当箇所の上に次の4つの文字が出ます。

表示していることこの例で押すと
Accept Current Change今いる枝の版を残す代表の6,200円になる
Accept Incoming Change入ってくる枝の版を残すBの6,500円になる
Accept Both Changes両方残す和食の基本が2行並ぶ
Compare Changes2つを並べて見る内容は変わらない

料金表の行は、Accept Both Changes だと講座が2行に増えて間違いになります。今回のように片方の一部ずつを生かすなら、どちらかを押したあと手で直すか、「Resolve in Merge Editor」で3画面の編集に切り替えます。Incoming、Current、Result の3つの欄が出て、Result が最終形です。

  1. 競合を直して保存する
  2. 3画面の編集なら「Complete Merge」を押す。そうでなければ Source Control の画面で「Stage Changes」の + を押す
  3. Source Control の画面からコミットする

Complete Merge はそのファイルを git add した状態にするだけで、合流はまだ終わっていません。3までやって完了です。

VS Codeでコンフリクトを解消するときの操作
VS Codeでコンフリクトを解消するときの操作

AIに任せるときの注意

Claude Code や GitHub Copilot に頼めば、記号を消す作業は済みます。問題は「どちらを残すか」で、これは料金を決めた人にしか分かりません。AIに任せると、記号はきれいに消えて、片方の変更だけが静かに消えていることがあります。VS Codeの「Resolve Merge Conflict with AI」のような機能も、公式に提案を見直してからステージするよう書かれています。2026年11月時点では実験的な機能で、GitHub Copilotが使える環境でしか動きません。

確かめ方は簡単です。合流の記録を作ったあと、push する前に次の2つを見ます。HEAD^1 は合流前の main、HEAD^2 は入ってきた枝です。

$ git diff HEAD^1 HEAD
diff --git a/menu.html b/menu.html
index 4878dd7..721c5c9 100644
--- a/menu.html
+++ b/menu.html
@@ -1,6 +1,6 @@
 <h1>料金表</h1>
 <table>
-<tr><td>和食の基本(全4回)</td><td>6,200円</td></tr>
+<tr><td>和食の基本(全4回)</td><td>6,500円</td></tr>
 <tr><td>家庭の洋食</td><td>6,500円</td></tr>
 <tr><td>親子パン教室</td><td>4,800円</td></tr>
 <tr><td>季節の和菓子</td><td>5,200円</td></tr>

$ git diff HEAD^2 HEAD
diff --git a/menu.html b/menu.html
index 5938e85..721c5c9 100644
--- a/menu.html
+++ b/menu.html
@@ -1,6 +1,6 @@
 <h1>料金表</h1>
 <table>
-<tr><td>和食の基本</td><td>6,500円</td></tr>
+<tr><td>和食の基本(全4回)</td><td>6,500円</td></tr>
 <tr><td>家庭の洋食</td><td>6,500円</td></tr>
 <tr><td>親子パン教室</td><td>4,800円</td></tr>
 <tr><td>季節の和菓子</td><td>5,200円</td></tr>

1つ目は代表の版から見て変わった点で、金額がBの6,500円になりました。2つ目はBの版から見て変わった点で、名前が「全4回」になりました。片方の差分が空なら、その版をそのまま採用して、もう片方の変更を捨てた印です。空でなくても、残った行が意図どおりかは読んで確かめます。AIには「どちらをどう残したか、1件ずつ書いてください」と頼むと、この確認が早く済みます。

Excelや画像は混ぜられません

テキストのファイルは行で比べられますが、Excelや画像のようなバイナリ(文字ではないデータ)は比べられません。記号も入らず、次の表示が出ます。

$ git merge excel-b
warning: Cannot merge binary files: price.xlsx (HEAD vs. excel-b)
Auto-merging price.xlsx
CONFLICT (content): Merge conflict in price.xlsx
Automatic merge failed; fix conflicts and then commit the result.

直し方は、どちらか一方を丸ごと選ぶだけです。--theirs は入ってくる枝の版、--ours は今いる枝の版です。

$ git restore --theirs price.xlsx

$ git add price.xlsx

$ git commit
[main d909903] Merge branch 'excel-b'

選ばなかった側の変更は、選んだファイルに手で入れ直します。みんなで書き換える表はGitに入れず、Googleスプレッドシートなど同時編集できる場所に置くほうが向いています。images/ の写真も同じで、同じファイル名を2人が差し替えると一方しか残せません。

減らす方法

コンフリクトをなくすことはできませんが、数は減らせます。

  • 枝を短くする。何週間も放置した枝ほど、main との差が開きます。実際に運用していると、1つの修正に1つの枝で、その日か翌日に合流する形だとほとんど起きません
  • 始める前に pull する。main を最新にしてから枝を作ります
  • 同じファイルを同時に触らない。料金表を直す間はBに一言伝えるだけで避けられます
  • 整形だけの変更を混ぜない。全行の字下げを変えると、全行が競合の対象になります

できないことと、その代わり

できないことその代わり
Gitに正しいほうを決めてもらう内容を決めた人に聞く。-X ours という指定は競合した箇所だけ自分側を自動で採用し、相手の変更が静かに消える。AIに任せたときと同じ危険があるので、普段は使わなくていい
Excelや画像を混ぜる一方を選び、もう一方は手で入れ直す。表はスプレッドシートに置く
複雑な競合をGitHubの画面で直す手元のコマンドかVS Codeで直す
競合がないことで内容の正しさを保証する合流後に画面と差分を見る

よく詰まるところ

症状原因直し方
Committing is not possible because you have unmerged files.直したファイルを add していない直して git add してからコミットする
Pulling is not possible because you have unmerged files.合流の途中で pull した先に直して終わらせるか、git merge --abort する
公開したページに <<<<<<< が出た記号を消さずに add した。Gitは止めてくれない下の検査で探し、直してコミットする
編集画面が開いて操作できないgit commit がメッセージの編集画面を開いたVimなら :wq と入力してEnter。先に git commit --no-edit を使えば開かない
Your local changes to the following files would be overwritten by mergeコミットしていない変更と pull がぶつかった先にコミットしてから pull する
fatal: Need to specify how to reconcile divergent branches.手元とGitHubの両方に別のコミットがあるgit pull --no-rebase で合流させる。競合が出たらこの記事の手順で直す
記号の上下が逆に見えるgit pull --rebase の途中では、HEAD が相手の版で下が自分の版になる落ち着いて中身で判断する。やめるなら git rebase --abort
CONFLICT (modify/delete)片方が変え、片方が消した残すなら git add、消すなら git rm してコミットする

記号が残ったままでも git add も git commit も通ってしまいます。次の検査で見つけられます。

$ git diff --cached --check
menu.html:5: leftover conflict marker
menu.html:7: leftover conflict marker
menu.html:9: leftover conflict marker

$ git grep -n "<<<<<<<"
menu.html:5:<<<<<<< HEAD

1つ目は add した直後、2つ目はコミットしたあとでも使えます。

次に読む

枝と合流の基本は「git branchとmergeの使い方:枝を分けて合流する、GitHub Flowまで」にあります。GitHubの画面での流れ全体は「プルリクエストとは:作り方・レビュー・マージの流れ」で確かめられます。合流したあとで間違いに気づいたら「Gitで元に戻す:revert・reset・restoreの違いと使い分け」を開きます。pull の使い分けは「git clone・pull・fetchの違い:受け取る操作の使い分け」に続きます。

あわせて読む