こんにちは、かつコーチです。
Pull Requestを出したら「This branch has conflicts that must be resolved」という赤い表示が出て、固まってしまった経験はありませんか。
チーム開発を始めたばかりの頃は、このコンフリクトという言葉だけで身構えてしまう人も多いです。
でも仕組みさえ理解すれば、コンフリクト解消は決して怖い作業ではありません。
この記事では、コンフリクトが起きる理由から、実際に手を動かして解消するまでの流れを解説します。
コンフリクトとは?
コンフリクト=2人の編集がぶつかった編集会議
コンフリクトとは、複数の人が同じファイルの同じ箇所を別々に書き換えたとき、GitHubがどちらを採用すればいいか判断できなくなる状態のことです。
たとえるなら、1つの原稿を2人が同時に別々の場所で赤入れして、それを1つにまとめる編集会議のようなものだとイメージしてください。
Aさんが3行目を「こう直そう」と書き込み、Bさんも同じ3行目を別の内容に書き込んでいたら、編集長はどちらを採用するか自分では決められません。
Gitも同じで、同じ行に対する2つの変更履歴が食い違うと、機械的にはどちらが正しいか判断できず、人間に「あなたが決めてください」と差し戻してくるのです。
なぜコンフリクトが起きるのか
コンフリクトが起きる典型的なパターンは、複数人が同じファイルの近い行を並行して編集しているケースです。
たとえば、あなたがfeatureブランチで作業している間に、他のメンバーがmainブランチに別の修正をマージしていたとします。
その状態でPull Requestをmainに取り込もうとすると、GitHubは両方の変更履歴を突き合わせ、同じ箇所に矛盾があればコンフリクトとして知らせてきます。
これは決してエラーではなく、「編集会議で誰かの判断が必要ですよ」という正常な通知だと捉えると気が楽になります。
基本の書き方:コンフリクト解消の手順
手順1:ローカルでmainを取り込む
まず自分のfeatureブランチに最新のmainを取り込み、ローカル環境でコンフリクトを再現させます。
$ git checkout feature/user-profile
$ git fetch origin
$ git merge origin/main
Auto-merging src/components/Profile.tsx
CONFLICT (content): Merge conflict in src/components/Profile.tsx
Automatic merge failed; fix conflicts and then commit the result.
この時点で、コンフリクトが起きたファイルがターミナルに表示されます。
手順2:コンフリクトマーカーを読み解く
対象ファイルを開くと、以下のような記号で囲まれた箇所が追加されています。
<<<<<<< HEAD
const title = "ユーザー情報";
=======
const title = "プロフィール編集";
>>>>>>> origin/main
<<<<<<< HEADから=======までが自分の変更、=======から>>>>>>> origin/mainまでが取り込んだ側(mainの他メンバーの変更)です。
編集会議でいえば、上段が自分の赤入れ、下段が相手の赤入れが並んで見えている状態だとイメージしてください。
手順3:内容を1つにまとめて保存する
どちらを採用するか、あるいは両方を活かして書き直すかを決め、マーカーごと編集して1つの正しいコードに書き換えます。
const title = "プロフィール編集";
書き換えたら、忘れずにマーカー記号(<<<<<<<、=======、>>>>>>>)自体も削除してください。
つまずきやすい設定・注意点
修正が終わったら、通常のファイル変更と同じ手順でステージングとコミットを行います。
$ git add src/components/Profile.tsx
$ git commit -m "resolve conflict in Profile.tsx"
$ git push origin feature/user-profile
ここで重要なのは、git merge --abortという逃げ道があることです。
コンフリクトの内容が複雑で判断に迷うときは、無理に解消せず一度中断し、担当者に確認してから改めて取り組むという選択も十分にありです。
$ git merge --abort
よくあるつまずきポイント・エラー対処
マーカーを消し忘れたままコミットしてしまった話
私が実際にやってしまった失敗が、コンフリクトマーカーの記号を消し忘れたままコミットしてしまったことです。
❌ Before:マーカー記号を残したままコミットする
<<<<<<< HEAD
const title = "ユーザー情報";
=======
const title = "プロフィール編集";
>>>>>>> origin/main
このままコミットしてしまうと、<<<<<<<などの記号が構文エラーとしてビルドを落とし、CI上で以下のようなエラーが表示されました。
SyntaxError: Unexpected token '<<<<<<<'
編集会議でいえば、2人分の赤入れメモをそのまま原稿に貼り付けて印刷してしまったようなものです。
✅ After:マーカーを完全に削除してから最終確認する
const title = "プロフィール編集";
以降は、コミット前に必ずgit diffで差分を目視確認し、マーカー記号が残っていないかをチェックする習慣をつけました。
$ git diff --cached | grep -E "<<<<<<<|=======|>>>>>>>"
このコマンドで何も表示されなければ、マーカーの消し忘れはありません。
応用・一歩先の使い方
GitHub上のWeb UIでも簡単なコンフリクトは解消できる
変更が数行程度の軽微なコンフリクトであれば、ローカル環境を使わずGitHub上の「Resolve conflicts」ボタンからブラウザ上で解消することも可能です。
ただし、対象ファイルが多い、あるいは複雑なロジックの衝突がある場合は、動作確認をしながら進められるローカル環境での解消をおすすめします。
編集会議も、修正が1文字だけならその場でメモ書きできますが、内容が込み入るなら一度持ち帰って検討したほうが確実なのと同じ考え方です。
まとめ
この記事のポイント
- コンフリクトとは、2人の編集がぶつかった編集会議のような状態で、人間の判断が必要という正常な通知
<<<<<<<から>>>>>>>までのマーカーを読み解き、内容を1つにまとめてから記号ごと削除する- 判断に迷ったら
git merge --abortで一度中断する選択肢もある - コミット前に
git diffでマーカーの消し忘れがないか確認する習慣をつける
次に読むべき記事
コンフリクトが起きにくいチーム運用を目指すなら、「Git Flow・GitHub Flowとは?ブランチ運用ルールの考え方」でブランチ戦略の基本を押さえておくと、日々の衝突そのものを減らせます。
さらに事故を未然に防ぎたい方は、「Protected Branchとレビュー必須設定で事故を防ぐ」もあわせてご覧ください。