Git入門:バージョン管理のきほん

コンフリクトを解決する

生田 陸人
LuaGate エンジニア / 現役エンジニア
編集 LuaGate編集部
マージとコンフリクト

このレッスンで分かること

  • コンフリクトがなぜ起きるのか、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.css

both modified 以外の表示もあります。片方がファイルを消して、もう片方が編集した場合は deleted by themdeleted 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 でマージすれば HEADmain、作業ブランチで git merge main を打てば HEAD は作業ブランチ側です。マージの方向を変えると、上下が入れ替わります

分岐点の内容も一緒に見たい場合は、設定を変えると 3 つ並びで表示されます。

ターミナル

$ git config --global merge.conflictStyle diff3

この設定を入れると ||||||| で区切られた 3 つ目の枠が増え、「分岐したときは何だったのか」が見えます。どちらが何を足したのかが分かるので、判断は格段に楽になります。

どこを消してどこを残すか

やることは 2 つだけです。

  1. どの内容を残すか決めて、いらない側を消す
  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.html

All 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 statusUnmerged 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 で開始前に戻す。放置と逃走がいちばん危ない