Git入門:バージョン管理のきほん
コンフリクトを解決する
このレッスンで分かること
- コンフリクトがなぜ起きるのか、Git が何を判断できずに止まっているのか
<<<<<<< HEAD/=======/>>>>>>>の読み方と、どこを消してどこを残すか- 直して
git addしてgit commitで完了させる流れと、git merge --abortでの撤退
コンフリクトは事故ではなく正常な動作
マージのとき、Git は変更をファイル単位ではなく行単位で見比べます。別々の行が変わっているだけなら自動でまとめてくれます。片方が 3 行目、もう片方が 40 行目を直したなら、何も聞かずに両方を取り込みます。
Git が止まるのは、両方のブランチが同じファイルの同じあたりの行を、違う内容に書き換えたときだけです。どちらが正しいかは書いた人にしか分からないので、Git は判断を放棄して人間に渡します。これがコンフリクトです。壊れたわけでも、誰かがミスしたわけでもありません。
ターミナル
$ git switch main
Switched to branch 'main'
$ git merge feature
Auto-merging index.html
CONFLICT (content): Merge conflict in index.html
Automatic merge failed; fix conflicts and then commit the result.止まった直後は、必ず git status を見ます。
ターミナル
$ git status
On branch 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: index.html
no changes added to commit (use "git add" or "git commit" -a to commit)Unmerged paths に並んでいるのが、直さなければいけないファイルです。both modified は「両方のブランチが変えた」という意味です。ここに出ていないファイルは自動でまとまっているので、触る必要はありません。
この時点でマージは完了していません。中断して、人の判断を待っている状態です。だから git status は「fix conflicts and run git commit」と「use git merge --abort to abort」の 2 つの道を案内しています。進むか戻るかのどちらかを選ぶまで、リポジトリは中途半端な状態のままです。
コンフリクトしているファイルの一覧だけが欲しいときは、こう打つと簡潔です。
ターミナル
$ git diff --name-only --diff-filter=U
index.html
style.cssboth modified 以外の表示もあります。片方がファイルを消して、もう片方が編集した場合は deleted by them や deleted by us になります。この場合はマーカーが入らず、ファイルを残すか消すかの判断になります。残すなら git add ファイル名、消すなら git rm ファイル名 で意思表示します。
マーカーの読み方
コンフリクトしたファイルを開くと、Git が書き込んだ記号が入っています。
プレーンテキスト
<h1>Welcome</h1>
<<<<<<< HEAD
<p>お問い合わせは support@example.com まで。</p>
=======
<p>ご相談は各店舗の窓口までお願いします。</p>
>>>>>>> feature
<footer>© 2026 Example</footer>3 種類の記号がそれぞれ境目を表しています。
| 記号 | 意味 |
|---|---|
<<<<<<< HEAD | ここから下が、いま自分が立っているブランチの内容 |
======= | 上下を分ける仕切り |
>>>>>>> feature | ここまでが、取り込もうとしている側の内容 |
HEAD は現在地を指す印なので、main に立ってマージしたなら HEAD 側が main の内容です。>>>>>>> の後ろには、取り込む相手のブランチ名が書かれます。
マーカーの外側は無関係です。上の例の <h1> と <footer> の行は自動でまとまっているので、触ってはいけません。直すのはマーカーで挟まれた範囲だけです。
1 つのファイルに何か所もコンフリクトが起きることもあります。その場合はこの 3 点セットが、箇所の数だけ繰り返し現れます。ファイルの先頭から順に、1 か所ずつ片付けてください。
どちらが自分でどちらが相手か混乱したときは、HEAD 側が必ず「いま立っているブランチ」だと思い出してください。main でマージすれば HEAD は main、作業ブランチで git merge main を打てば HEAD は作業ブランチ側です。マージの方向を変えると、上下が入れ替わります。
分岐点の内容も一緒に見たい場合は、設定を変えると 3 つ並びで表示されます。
ターミナル
$ git config --global merge.conflictStyle diff3この設定を入れると ||||||| で区切られた 3 つ目の枠が増え、「分岐したときは何だったのか」が見えます。どちらが何を足したのかが分かるので、判断は格段に楽になります。
どこを消してどこを残すか
やることは 2 つだけです。
- どの内容を残すか決めて、いらない側を消す
<<<<<<<=======>>>>>>>の 3 行をすべて消す
残し方に決まりはありません。片方だけを採る、両方を並べる、まったく新しい文章に書き直す、どれも正解です。上の例で、両方の情報を活かすことにしたとします。編集後はこうなります。
プレーンテキスト
<h1>Welcome</h1>
<p>ご相談は各店舗の窓口、またはメール support@example.com までお願いします。</p>
<footer>© 2026 Example</footer>記号が 1 つも残っていないことを確かめてください。マーカーの消し忘れはコンフリクト解決でいちばん多い失敗です。HTML や CSS では見た目が崩れるだけで済むこともありますが、JavaScript や Python なら構文エラーで動かなくなります。
残っていないかはコマンドで確認できます。
ターミナル
$ git diff --check何も出なければ、マーカーは残っていません。残っている場合は leftover conflict marker として行番号付きで報告されます。ファイル全体を検索してもよいです。
ターミナル
$ grep -rn "<<<<<<<" .VS Code でコンフリクトしたファイルを開くと、マーカーの上に「現在の変更を取り込む」「入力側の変更を取り込む」「両方の変更を取り込む」というボタンが出ます。押すとマーカーごと自動で片付きます。便利ですが、押す前に必ず両側を読んでください。片方を機械的に採ると、相手の作業をまるごと消してしまいます。
直したら add して commit する
ファイルを直しただけでは、まだマージは終わっていません。git add が「このファイルは解決した」という宣言になります。
ターミナル
$ git add index.html
$ git status
On branch main
All conflicts fixed but you are still merging.
(use "git commit" to conclude merge)
Changes to be committed:
modified: index.htmlAll conflicts fixed but you are still merging に変わりました。あとはコミットするだけです。
ターミナル
$ git commit
[main 3d9c05a] Merge branch 'feature'git commit を引数なしで打つと、Git が用意したメッセージ (Merge branch 'feature') を入れたエディタが開きます。そのまま保存して閉じれば完了です。Vim が開いた場合は Esc を押してから :wq と入力して Enter、VS Code なら編集タブを閉じます。
複数ファイルがコンフリクトしているときは、1 つ直すたびに git add していきます。git status の Unmerged paths が空になったらコミットできる合図です。全部直し終えてから git add . でまとめても構いませんが、直し忘れに気づきにくいので、慣れるまでは 1 ファイルずつ進めてください。
git add する前に、直した結果が意図通りかを見ておくと安心です。
ターミナル
$ git diff index.htmlまだマーカーが残っていれば、この差分に <<<<<<< の行がそのまま出てきます。add してしまう前に気づけます。
コミットが終わったら、必ずアプリを動かして確認してください。コンフリクトの解決は「文字として辻褄が合う」だけで完了できてしまいます。両方の変更を機械的に並べた結果、変数が二重に定義されていたり、条件分岐が噛み合っていなかったりすることは普通に起きます。テストがあるなら、この時点で走らせるのが最短の確認です。
ターミナル
$ git commit
$ npm testコンフリクト解決のコミットは、それだけで 1 つのコミットとして完結させてください。ついでに別の修正を混ぜると、後から「この行は誰の変更だったのか」が追えなくなります。
分からなくなったら merge --abort で撤退する
コンフリクトの数が多い、あるいはどう直すのが正しいか分からない。そんなときは無理に進めず、マージを始める前の状態に丸ごと戻します。
ターミナル
$ git merge --abort
$ git status
On branch main
nothing to commit, working tree cleanこれでマージはなかったことになります。編集しかけた内容は破棄されますが、マージ前のコミットは 1 つも失われません。「何が起きているか分からない」と感じた時点で abort するのが、いちばん安全な選択です。落ち着いてから、相手のブランチの変更内容を先に読んでやり直します。
ターミナル
$ git log --oneline main..feature
8f3a91c お問い合わせ導線を店舗窓口に変更
1a4f7b2 フッターの表記を統一コミットメッセージを読めば、相手が何を意図して書き換えたのかが分かります。「導線を店舗窓口に変更」したかったのだと分かれば、どちらを残すべきかの判断もつきます。コンフリクトの解決は、Git の操作ではなく仕様の判断です。迷ったら書いた本人に聞くのがいちばん速く、いちばん正確です。
一方だけを丸ごと採ると決めたなら、コマンドで一発で選べます。
ターミナル
$ git checkout --ours index.html
$ git checkout --theirs index.html--ours が自分側 (HEAD 側)、--theirs が相手側です。設定ファイルやビルド生成物のように、片方を採れば済むと分かっているファイルには便利です。ソースコードには使わないでください。相手の変更を無言で捨てることになります。
やりがちな失敗をもう 1 つ挙げます。コンフリクトの途中で git switch して別のブランチへ逃げようとするパターンです。
ターミナル
$ git switch feature
error: you need to resolve your current index first
index.html: needs merge解決を終えるか abort するまで移動はできません。中途半端な状態のまま放置すると、翌日の自分が状況を思い出せずに事故ります。その場で解決するか、その場で abort するか、どちらかを必ず選んでください。
コンフリクトを減らす習慣
起きること自体は避けられませんが、規模は小さくできます。
- 作業ブランチを長生きさせない。1 つの機能を終えたら早めにマージする
- 作業中も定期的に
mainの最新を取り込む。差が小さいうちなら衝突も小さい - 同じファイルを 2 人が同時に大改造しない。始める前に一声かける
- 自動整形ツールの設定をチームで揃える。インデントの違いだけで全行が衝突する
作業ブランチに main を取り込む方向のマージは、こう打ちます。
ターミナル
$ git switch feature
Switched to branch 'feature'
$ git merge mainこちらで解決しても、作業ブランチの履歴が汚れるだけで main は無傷です。失敗しても影響範囲が自分のブランチに閉じるので、心理的にもずっと楽に手を動かせます。feature の側で先にぶつかりを解消しておけば、main へマージする本番は静かに終わります。履歴を一直線に保ちたい場合は、代わりに rebase で履歴を整える の方法を使います。
- コンフリクトは同じ行を両方が変えたときに起きる正常な動作。壊れてはいない
<<<<<<< HEADから=======までが自分側、>>>>>>>までが相手側。3 つの記号は必ず消す- 直したら
git addで解決を宣言し、git commitでマージを完了する - 分からなくなったら
git merge --abortで開始前に戻す。放置と逃走がいちばん危ない