コンフリクトを恐れない — 発生原因と解決手順
コンフリクト(conflict、競合)は、Git初心者がもっとも怖がる瞬間です。エディタに <<<<<<< なんて記号が並び、「何か壊した気がする」と焦ってしまう。
実際には、コンフリクトは壊れているわけではなく、Gitが「ここは自動判断できないから、人間が決めて」と頼んでいるだけです。落ち着いて読めば必ず解決できます。
コンフリクトが起きる仕組み
コンフリクトは、同じファイルの同じ行を、2つのブランチで違う内容に変更したときに発生します。
たとえば以下のような状況です。
main ブランチ:
title = "こんにちは";
feature ブランチ:
title = "Hello";
両方のブランチが、元は title = "Hi"; だった1行をそれぞれ違う内容に変更しました。Gitはこれをmergeするとき、「どちらが正解か判断できない」のでコンフリクトにします。
コンフリクトが起きない変更もある
同じファイルでも、違う行を編集していればコンフリクトしません。Gitは行単位で差分を管理しているので、以下のような場合は自動マージされます。
mainが1行目を変更、featureが10行目を変更 → コンフリクトしないmainが新しい関数を追加、featureが別の関数を修正 → コンフリクトしない
なので「このファイルを両方で触ったからコンフリクトする」とは限りません。実際にやってみるまで分からないのが普通です。
コンフリクトマーカーの読み方
コンフリクトが起きると、Gitはファイルに特殊な記号を書き込んで「ここが衝突しているよ」と教えてくれます。
<<<<<<< HEAD
const title = "こんにちは";
=======
const title = "Hello";
>>>>>>> feature各記号の意味は以下の通りです。
| 記号 | 意味 |
|---|---|
<<<<<<< HEAD | ここから現在のブランチの内容 |
======= | 区切り線 |
>>>>>>> feature | ここまでが取り込もうとしたブランチ(feature)の内容 |
つまり、======= の上がHEAD側、下が取り込み側です。
解決の手順
コンフリクトを解決する流れは以下の4ステップです。
ステップ1: どのファイルがコンフリクトしているか確認
git statusOn branch main
You have unmerged paths.
(fix conflicts and run "git commit")
Unmerged paths:
(use "git add <file>..." to mark resolution)
both modified: src/app.js
both modified と表示されたファイルがコンフリクトしています。
ステップ2: ファイルを開いて手動で編集
対象のファイルを開き、コンフリクトマーカーを見ながら正しい内容に直します。マーカーも一緒に削除するのを忘れないでください。
// Before(コンフリクト状態)
<<<<<<< HEAD
const title = "こんにちは";
=======
const title = "Hello";
>>>>>>> feature
// After(解決後)
const title = "Hello";「両方採用したい」ケースもあります。その場合は両方残してマーカーだけ消します。
const titleJa = "こんにちは";
const titleEn = "Hello";ステップ3: 解決を宣言する
編集が終わったら、そのファイルを git add します。これが「解決しました」の合図です。
git add src/app.js複数ファイルがコンフリクトしている場合は、全部のファイルを編集して git add します。
ステップ4: コミットしてマージを完了
すべて解決したらコミットします。
# mergeの場合
git commit
# rebaseの場合
git rebase --continuegit commit を引数なしで実行すると、Gitが自動で Merge branch 'feature' のようなメッセージを提案してくれます。そのまま保存すればOKです。
解決を間違えたらやり直せる
「編集ミスった、最初からやり直したい」というときは、マージ/rebaseを中止できます。
# mergeを中止
git merge --abort
# rebaseを中止
git rebase --abortこれでコンフリクト発生前の状態に戻ります。安心してやり直しましょう。
VS Codeでコンフリクトを解決する
エディタ上でコンフリクトマーカーをいちいち削除するのは面倒です。VS Codeはコンフリクトを自動で検出して、ボタンで選択できるUIを提供してくれます。
コンフリクトしたファイルを開くと、以下のような選択肢が表示されます。
- Accept Current Change — HEAD側を採用
- Accept Incoming Change — 取り込み側を採用
- Accept Both Changes — 両方採用
- Compare Changes — 差分をサイドバイサイドで比較
ボタンを押すだけでマーカーごと自動で書き換えてくれるので、慣れないうちはこれを使うのが楽です。
どちらを採用すべきか迷ったら
実務では「どっちを採用するのが正しいか」の判断に悩むことが多いです。以下のような手順で考えます。
git logで両側のコミットの意図を確認するgit log --oneline HEAD..feature # featureのみにあるコミット git log --oneline feature..HEAD # HEADのみにあるコミット- 両方の変更を理解してから、マージ後に期待される挙動を考える
- 判断できなければ、変更した本人に聞く
「コンフリクトしたから片方消しちゃえ」は一番危険です。相手の変更を意図せず消してしまうと、バグを埋め込む原因になります。
コンフリクトを減らすコツ
コンフリクトは完全には避けられませんが、頻度と規模を減らすことはできます。
- こまめにmainを取り込む — 長期間feature branchを放置するとコンフリクトが巨大化する
- 小さなPRを心がける — 1つのPRで多くのファイルを触らない
- チーム内で作業領域を分ける — 同じファイルを同時に触らないよう調整
- リファクタリングと機能追加を同じPRに混ぜない — 両方やると広範囲でコンフリクトしやすい
よくあるハマりどころ
1. コンフリクトマーカーを消し忘れてコミットしてしまった
<<<<<<< がコードに残ったままコミットすると、構文エラーや動作不良の原因になります。コミット前に必ず git diff --cached で確認する癖を付けましょう。
エディタやエディタ拡張で、保存時にマーカーを検出する仕組みを入れておくのも有効です。
2. 大量のコンフリクトに圧倒される
数十ファイルが一気にコンフリクトすると心が折れます。そういうときは:
- 1ファイルずつ解決する
- 中止して、もっと小さい単位でmergeし直す(ブランチをこまめにリベースする運用に切り替える)
- 最悪、ファイルごとに「片方を完全に採用」する
# featureの内容を丸ごと採用
git checkout --theirs path/to/file
# HEADの内容を丸ごと採用
git checkout --ours path/to/file3. コンフリクトしたファイルを誤って削除してしまった
解決中のファイルを誤削除した場合、git checkout -m path/to/file でコンフリクト状態に戻せます。
ちゃんと使うためのポイント
- コンフリクトは壊れたわけではない、Gitが人間の判断を求めているだけ
<<<<<<<=======>>>>>>>の意味を読める- 解決は「編集 →
git add→git commit」の流れ - 迷ったら
git merge --abort/git rebase --abortでやり直せる - こまめに取り込むことでコンフリクトの規模を小さく保つ
次の章では、コミットしたあとに「取り消したい」「修正したい」となったときの方法、reset・revert・restore の使い分けを学びます。