Gitのコンフリクト(競合)とは、2つのブランチで同じファイルの同じ場所が別々に変更されていて、Gitがどちらを採用すればよいか自動で判断できない状態です。エラーのように見えますが、壊れたわけではありません。どちらの変更を残すかを人が決めて、ファイルを直し、コミットすれば解消できます。

この記事では、実際にコンフリクトを起こして、マーカーの読み方から解消、中止、よくあるつまずきまでを手順で解説します。rebaseのときに --ours と --theirs の意味が逆になる点や、ロックファイルの扱い、同じ解消を自動で再利用する rerere など、実務で役立つ内容もまとめています。

コンフリクト解消の早見表

やりたいこと コマンド
どのファイルがコンフリクトしているか見る git status
コンフリクトしているファイルだけを一覧にする git diff --name-only --diff-filter=U
解消したことをGitに伝える git add ファイル名
マージを完了する git commit
rebaseを続ける git rebase --continue
やめて元に戻す git merge --abort / git rebase --abort / git cherry-pick --abort
自分の側の内容を採用する git checkout --ours ファイル名
相手の側の内容を採用する git checkout --theirs ファイル名
元の内容も表示する git config --global merge.conflictStyle zdiff3
同じ解消を自動で再利用する git config --global rerere.enabled true

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「Gitのコンフリクトの解消方法|マーカーの読み方から防ぎ方まで」

実際にコンフリクトを起こしてみる

mainブランチとfeatureブランチで、税率を別々に書き換えた状況です。

// 元のファイル(price.js)
const TAX_RATE = 0.08;
const SHIPPING = 500;
  • featureブランチ:0.08 を 0.10 に変更
  • mainブランチ:0.08 を 0.1 に変更

mainにfeatureをマージすると、次のように表示されます。

git merge feature
Auto-merging price.js
CONFLICT (content): Merge conflict in price.js
Automatic merge failed; fix conflicts and then commit the result.

git status --short では、コンフリクトしているファイルに UU が付きます。

UU price.js

コンフリクトマーカーの読み方

ファイルを開くと、次のように両方の変更が書き込まれています。

<<<<<<< HEAD
const TAX_RATE = 0.1;
=======
const TAX_RATE = 0.10;
>>>>>>> feature
const SHIPPING = 500;
マーカー 意味
<<<<<<< HEAD から ======= まで 今いるブランチ(HEAD、ここではmain)の内容
======= から >>>>>>> feature まで 取り込もうとしているブランチ(feature)の内容
マーカーの外 自動でマージできた部分(そのままでよい)

元の内容も表示する(zdiff3)

上の表示では、変更前が何だったのかがわかりません。次の設定をしておくと、元の内容も表示されます。

git config --global merge.conflictStyle zdiff3
<<<<<<< HEAD
const TAX_RATE = 0.1;
||||||| abdd467
const TAX_RATE = 0.08;
=======
const TAX_RATE = 0.10;
>>>>>>> feature
const SHIPPING = 500;

||||||| から ======= までが元の内容です。「元は0.08で、両方とも10%に変えようとした(書き方が違うだけ)」ことがすぐにわかります。どちらを残すべきかを判断しやすくなるため、設定しておくのがおすすめです。

解消の手順

1. どちらを残すかを決めて、ファイルを直す

マーカーを削除し、最終的にあるべき内容に書き換えます。片方を選ぶだけでなく、両方の変更を組み合わせることもあります。

const TAX_RATE = 0.1;
const SHIPPING = 500;

2. マーカーが残っていないか確認する

git diff --check

マーカーの消し忘れがあると、x.js:1: leftover conflict marker のように、ファイル名と行番号つきで表示されます。エディタで <<<<<<< を検索するのも確実です。

3. 解消したことをGitに伝えて、完了する

git add price.js
git commit          # マージの場合

rebaseの場合は、コミットではなく次のコマンドで続けます。

git add price.js
git rebase --continue

4. 動作を確認する

コンフリクトを解消しただけでは、正しく動くとは限りません。ビルドやテストを実行してから、pushします。

迷ったら中止して元に戻せる

解消の途中で「よくわからなくなった」ときは、中止すれば、コンフリクトが起きる前の状態に戻せます。

git merge --abort        # マージを中止
git rebase --abort       # rebaseを中止
git cherry-pick --abort  # cherry-pickを中止

落ち着いて、変更の内容を確認したり、変更した人に相談したりしてから、やり直せます。

--ours と --theirs:rebaseでは意味が逆になる

ファイル全体を、どちらか一方の内容にしたいときは、--ours または --theirs を使います。

git checkout --ours price.js     # 「自分の側」を採用
git checkout --theirs price.js   # 「相手の側」を採用
git add price.js

注意が必要なのは、mergeとrebaseで「自分」と「相手」が逆になる点です。実際に確認した結果です。

操作 --ours --theirs
mainにいて git merge feature mainの内容(0.1) featureの内容(0.10)
featureにいて git rebase main mainの内容(0.1) featureの内容(0.10)

rebaseは、mainの上に、featureのコミットを1つずつ積み直す操作です。積み直している最中は、mainの側が「土台(ours)」、積み直しているfeatureのコミットが「取り込む側(theirs)」になります。featureで作業している自分の変更が --theirs になるため、間違えやすいポイントです。

VS Codeで解消する

VS Codeでコンフリクトしたファイルを開くと、マーカーの上に次のボタンが表示されます。

ボタン 動作
Accept Current Change 今いるブランチ(HEAD)の内容を採用
Accept Incoming Change 取り込むブランチの内容を採用
Accept Both Changes 両方を残す(順番に並ぶ)
Compare Changes 2つを並べて比較する

また、「Resolve in Merge Editor」を選ぶと、左右に両方の変更、下に結果を表示する3画面のエディタで、1行ずつ選びながら解消できます。

「Accept Both Changes」は、両方の行が並ぶだけです。同じ変数を2回定義するなど、そのままでは動かないことがあるため、結果を必ず確認します。

ロックファイルのコンフリクト

package-lock.json、yarn.lock、uv.lock、poetry.lock などのロックファイルは、自動で生成されるファイルです。手で直すと、整合性が崩れることがあります。

# 例:package-lock.jsonがコンフリクトした場合
git checkout --theirs package-lock.json   # いったんどちらかに揃える
npm install                                # package.jsonに合わせて作り直す
git add package-lock.json

先に package.json のコンフリクトを解消し、その内容に合わせてロックファイルを作り直すのが基本です。作り直した後は、テストを実行して確認します。

削除と変更のコンフリクト

片方のブランチでファイルを削除し、もう片方で同じファイルを変更した場合も、コンフリクトになります。

CONFLICT (modify/delete): config.js deleted in feature and modified in HEAD.  Version HEAD of config.js left in tree.
決めたこと コマンド
ファイルを残す git add config.js
ファイルを削除する git rm config.js

なぜ削除されたのかを、削除した人に確認してから判断します。

rerere:同じ解消を自動で再利用する

rebaseを繰り返したり、同じブランチを何度もマージし直したりすると、同じコンフリクトを何度も解消することになります。rerere(reuse recorded resolution)を有効にすると、一度解消した内容を記録し、同じコンフリクトが起きたときに自動で適用してくれます。

git config --global rerere.enabled true

実際に、一度解消したマージを取り消して、もう一度マージした結果です。

Recorded resolution for 'price.js'.          ← 1回目の解消を記録
...
CONFLICT (content): Merge conflict in price.js
Resolved 'price.js' using previous resolution. ← 2回目は自動で解消

自動で解消された内容を確認し、問題がなければ git add してコミットします。

コンフリクトを減らす運用のコツ

コンフリクトは避けられないものですが、小さく、早く統合することで、減らしたり、解消を楽にしたりできます。

コツ 理由
ブランチを短命にする(数日以内に統合) mainとの差が小さいほど、コンフリクトも小さくなる
mainの変更をこまめに取り込む 大きなコンフリクトを、小さなうちに解消できる
1つのプルリクエストを小さくする 変更するファイルと行が少なくなる
フォーマッターをチーム全員で揃える 空白や改行だけの違いによるコンフリクトを防ぐ
同じファイルを大きく変更するときは声をかける 並行して作業する人と、順番や分担を決められる
大きなファイルを分割する 1つのファイルに変更が集中しにくくなる

よくある質問

Q. コンフリクトを解消してコミットした後で、間違いに気づきました。 A. pushする前なら、マージのコミットを取り消してやり直せます(git reset --hard ORIG_HEAD など)。push済みの場合は、修正のコミットを追加するのが安全です。

Q. 空白や改行コードだけのコンフリクトが大量に出ます。 A. フォーマッターの設定の違いや、改行コード(LFとCRLF)の違いが原因のことが多いです。.gitattributes やエディタの設定で改行コードを揃え、フォーマッターの設定をリポジトリで共有します。

Q. GitHubの画面でも解消できますか? A. 簡単なコンフリクトであれば、プルリクエストの画面の「Resolve conflicts」から解消できます。複雑な場合や、ロックファイルなど作り直しが必要な場合は、手元で解消してテストしてからpushする方が確実です。

まとめ

  • コンフリクトは、同じ場所の変更をGitが自動で選べない状態。人が決めて直せば解消できる
  • <<<<<<< から ======= が今いるブランチ、======= から >>>>>>> が取り込む側
  • zdiff3 を設定すると、元の内容も表示されて判断しやすい
  • 解消したら git add → git commit(rebaseなら git rebase --continue)。テストも実行する
  • 迷ったら --abort で元に戻せる
  • rebaseでは --ours と --theirs の意味が逆になる
  • ロックファイルは手で直さず作り直す。同じ解消の繰り返しは rerere で自動化
  • 小さく、早く統合することが、最大の予防策

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「Gitのコンフリクトの解消方法|マーカーの読み方から防ぎ方まで」

株式会社RJCでは、チーム開発の基本を、実際に手を動かしながら仲間と教え合っています。こうした知識を共有しながら、一緒に成長を楽しんでいます。

株式会社RJC ― SI事業・SES事業・AI駆動開発。未経験からエンジニアに育てるのが得意な会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?