マージコンフリクトに初めて遭遇したとき、自分が何かを壊してしまったのかと思った。CONFLICT (content): Merge conflict in app.pyという赤い文字が表示された瞬間、ターミナルを閉じてコーヒーを淹れに行って気持ちを落ち着かせた。
実際のところ、マージコンフリクトはまったく普通のことだ。同じファイルの同じ箇所を2人が同時に編集し、両方ともメインブランチにマージしようとすると発生する。Gitはどちらのコードを残すべきか判断できないため、手動で対処するよう「助けを求めて」くる。
マージコンフリクトはどう見える?
Gitは競合している箇所を3種類のマーカーでファイルに直接書き込む。最初は少し混乱するが、理解してしまえば数秒で読めるようになる:
<<<<<<< HEAD
def tinh_tong(a, b):
return a + b # 自分のバージョン
=======
def tinh_tong(a, b):
result = a + b
return result # 同僚のバージョン
>>>>>>> feature/refactor-math
<<<<<<< HEADと=======の間が自分のコード(現在のブランチ)、残りが相手のブランチからのコードだ。どちらを残すか、あるいは両方を組み合わせるかを決めて、マーカーをすべて削除するのが作業内容となる。
マージコンフリクトの3つの対処法
方法1:テキストエディタで手動修正
最もシンプルな方法——ファイルを開き、マーカーを削除して正しいコードだけを残し、コミットし直す。
# マージ後にコンフリクトが発生した場合
git merge feature/refactor-math
# コンフリクトしたファイルを開いて手動修正後:
git add app.py
git commit -m "fix: resolve merge conflict in tinh_tong function"
メリット:追加インストール不要で、各行のコードを完全にコントロールできる。
デメリット:マーカーを見落としやすい。特にコンフリクトが多い長いファイルでは要注意。以前、>>>>>>> feature/xyzという行を消し忘れたままプッシュしてしまい、ビルドサーバーがすぐにエラーを報告した——かなり恥ずかしかった。
方法2:マージツールを使う(VS Code、vimdiff、IntelliJ)
VS Code、IntelliJ、vimdiffはいずれも同じことをしてくれる:2つのバージョンを並べて表示し、どちらを残すか選べるようにする——散らかったマーカーを直接見る必要がなくなる。
# VS Codeをデフォルトのマージツールに設定する
git config --global merge.tool vscode
git config --global mergetool.vscode.cmd 'code --wait $MERGED'
# マージツールを起動する
git mergetool
GUIのないSSHサーバー上で作業している場合は、vimdiffを使う:
git config --global merge.tool vimdiff
git mergetool
# vimdiff内の操作:
# ]c — 次のコンフリクトにジャンプ
# :diffg LOCAL — 自分のバージョンを採用
# :diffg REMOTE — 相手のブランチのバージョンを採用
メリット:視覚的に分かりやすく、見落としが少ない。多くのツールはシンタックスハイライトもあるのでコードが読みやすい。
デメリット:初期設定が必要。vimdiffはvimに慣れていない場合は学習コストがかかる。
方法3:ours/theirs戦略
どちらかのバージョンが常に正しいと確信できる場合もある——たとえばmainのバージョンだけ残せばよい設定ファイルで、相手のブランチの変更はすべて破棄したいケースなどだ。
# 現在のブランチのコードをすべて採用する
git checkout --ours package.json
git add package.json
# 逆に:相手のブランチのコードをすべて採用する
git checkout --theirs package.json
git add package.json
メリット:素早く、判断に迷わない。
デメリット:片方のコードが完全に失われる——使い方を誤ると、復元できない重要な変更を消してしまう可能性がある。
分析:どの方法をいつ使う?
チームでGitを使って数年経験を積んだ後、こうまとめられる:
- 設定ファイル(package.json、requirements.txt、.env.example):
ours/theirsを使う——ロジックが少なく、どちらのバージョンが正しいか決めるだけでよい。 - 複雑なロジックを含むコードファイル:手動修正かマージツールが必須——どちらの変更を残すか決める前に、両方の変更を理解する必要がある。
- 1ファイルに多数のコンフリクト:VS Codeなどのマージツールを使う——視覚的に分かりやすく、マーカーの見落としが少ない。
- SSHサーバー上:nano/vimで手動修正するか、vimdiffを使う。
実践ワークフロー:コンフリクトを順を追って解決する
コンフリクトが発生したときに毎回使っているワークフロー——ステップごとに確認して見落としなし:
# ステップ1:コンフリクトしているファイルの一覧を確認
git status
# コンフリクトしたファイルは「both modified」の状態で表示される
# ステップ2:各ファイルのコンフリクト詳細を確認
git diff
# ステップ3:VS Codeで解決する(または手動修正)
code .
# ステップ4:修正完了後、解決済みとしてマークする
git add app.py
# ステップ5:まだコンフリクトが残っていないか確認
git status
# ステップ6:マージを完了させる
git commit
# またはリベース中の場合:
git rebase --continue
プッシュ前に以下のコマンドも実行して、マーカーが残っていないことを確認する——少なくとも数回は恥ずかしいビルド失敗から救ってくれた:
grep -r "<<<<<<< " --include="*.py" --include="*.js" .
# 出力がなければプッシュしても安全
コンフリクトを減らすための予防策
これは痛い目を見て学んだ教訓だ——コンフリクト解消に疲弊してようやくワークフローを変える気になった。8人のチームで以下の4つのルールを適用したところ、コンフリクトの発生頻度が1日3〜4回から週1〜2回にまで減少した:
- 作業開始前にプルする:新しいコードを書き始める前に、常に最新のコードを同期する。
- ブランチは小さく、短命に:ブランチが長く存在するほどmainから乖離し、コンフリクトが増えて解決も難しくなる。自分のルールとして、ブランチの寿命は3日以内と決めている。
- 担当範囲を明確に分ける:可能であれば、同じスプリント中に同じファイルを2人が同時に編集しないようにする。
- 定期的にmainをブランチにマージする:毎朝作業中のブランチにmainを同期することで、コンフリクトを小さく保ち、スプリント末まで溜め込まずすぐに対処できる。
# 朝のルーティン
git checkout main
git pull origin main
git checkout feature/my-task
git rebase main
# 小さなコンフリクトをその場で解決する——スプリント末まで放置するより遥かに楽
MergeかRebaseか——どちらを選ぶ?
最もよく聞かれる質問だ。簡潔にまとめると:
- Merge:マージコミットを作成し、実際の履歴をそのまま残す。フィーチャーブランチをmainに統合するときに使う。
- Rebase:履歴を書き換え、コミットを直線的にきれいに並べる。mergeする前にフィーチャーブランチをmainで更新するときに使う。
最もシンプルなルール:複数人が共有しているブランチは絶対にrebaseしない。rebaseするのは自分だけが使っているブランチに限る。
まとめ
最初にターミナルを閉じてコーヒーを淹れに行ったあの瞬間を振り返ると——実際にやるべきことはファイルを開き、数行のマーカーを削除してコミットするだけだった。それだけのことだ。この記事で紹介した3つの方法をしっかり理解すれば、赤い文字を見るたびに気持ちを落ち着かせるためにコーヒーは必要なくなるはずだ。
長期的には、チームの習慣こそがコンフリクトを本当に減らす鍵となる。小さいブランチ、早めのマージ、毎朝の同期——シンプルに聞こえるが、自分が習慣として定着させるまでに約6ヶ月かかった。まず一つだけ始めてみよう:毎朝コードを書く前にgit rebase mainを実行する。それだけでも確実に違いを感じられるはずだ。

