Linux & IT ノート

Git のコンフリクトを恐れない:発生原因と解消の手順

管理人 約10分で読めます

前回 git rebase を学んだ際、「コンフリクト(マージ競合)が起きた場合は次回詳解」と書きました。 今回はそのコンフリクトを正面から扱います。

コンフリクトと聞くと「難しそう」「怖い」と感じる方もいるかもしれません。 しかし実態は、Git が「ここは自動では判断できないから、あなたが決めてください」と丁寧に知らせてくれている状態です。 手順通りに進めれば、確実に解消できます。

コンフリクトが起きる仕組み

コンフリクトは、同じファイルの同じ箇所を、異なるブランチが別々に変更したときに起きます。

main ブランチ:    auth.py の10行目を「version = 1」に変更
feature ブランチ: auth.py の10行目を「version = 2」に変更

この2つを merge または rebase で統合しようとすると、Git はどちらの変更を採用すべきかを判断できません。 そのため処理を止め、ファイルに「ここが競合している箇所ですよ」という印(コンフリクトマーカー)を書き込んで人間に判断を委ねます。

コンフリクトが発生する典型的なシーンは次の3つです。

  • git merge <ブランチ> でブランチをマージするとき
  • git rebase <ブランチ> でコミットを再適用するとき
  • git pull(内部的に fetch + merge または rebase が走る)で最新を取り込むとき

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

コンフリクトが発生すると、該当ファイルに次のようなマーカーが自動挿入されます。

def get_version():
<<<<<<< HEAD
    return "version = 1"
=======
    return "version = 2"
>>>>>>> feature/update-version

それぞれの記号の意味は次の通りです。

マーカー意味
<<<<<<< HEADここから下が「現在いるブランチ(HEAD)」の変更
=======区切り線
>>>>>>> feature/update-versionここより上が「取り込もうとしたブランチ」の変更

つまり ======= の上が「自分側の変更」、下が「相手側の変更」です。

もう少し現実的な例を見てみましょう。README.md の「はじめに」セクションをどちらのブランチも編集していた場合:

## はじめに

<<<<<<< HEAD
このツールは Python 3.10 以上で動作します。
インストール方法は docs/setup.md を参照してください。
=======
このツールは Python 3.11 以上で動作します。
クイックスタートは docs/quickstart.md にあります。
>>>>>>> feature/update-docs

片方を採用するのか、両方を組み合わせるのか、あるいは全く別の書き方にするのかは、あなた(開発者)が判断します。

解消手順:merge の場合

git merge でコンフリクトが発生した際の手順を順番に追います。

git switch main
git merge feature/update-version
# Auto-merging auth.py
# CONFLICT (content): Merge conflict in auth.py
# Automatic merge failed; fix conflicts and then commit the result.

ステップ1: コンフリクトしているファイルを確認する

git status
# On branch main
# You have unmerged paths.
#   (fix conflicts and run "git commit")
#
# Unmerged paths:
#   (use "git add <file>..." to mark resolution)
#         both modified:   auth.py

both modified と表示されているファイルがコンフリクトしているものです。

ステップ2: ファイルを編集してマーカーを取り除く

エディタでファイルを開き、<<<<<<<=======>>>>>>> のマーカーをすべて除去しながら、最終的に正しいコードだけが残る状態にします。

# 解消後(両方の意図を取り込む場合の例)
def get_version():
    return "version = 2"  # 新しいバージョンを採用

マーカーが1行でも残っているとそのままコミットしてしまい、壊れたコードが入ります。 解消後は必ずファイル全体を見直す習慣をつけましょう。

ステップ3: git add でステージングする

解消が終わったファイルを git add します。 これが Git への「このファイルは解消済みです」というサインになります。

git add auth.py

ステップ4: git commit でマージを完了させる

git commit
# マージコミットのメッセージがエディタで開く(そのまま保存でOK)

-m を省略するとデフォルトのマージコミットメッセージが自動入力されたエディタが起動します。 内容はそのまま保存して閉じれば完了です。

解消手順:rebase の場合

git rebase でコンフリクトが発生した場合は、解消後のコマンドが異なります。

git rebase main
# CONFLICT (content): Merge conflict in auth.py
# error: could not apply a1b2c3d... Update version

ファイルを編集して git add するまでは merge と同じです。 ただし、完了のコマンドが git commit ではなく git rebase --continue になります。

git add auth.py
git rebase --continue
# 次のコミットの再適用が続く(コミットが複数ある場合は繰り返し)

rebase はコミットを一つずつ再適用するため、複数のコミットを rebase している場合はコンフリクトが何度か起きることがあります。 そのたびに同じ手順(編集 → git addgit rebase --continue)を繰り返します。

やり直し:git merge —abort / git rebase —abort

「解消途中で一旦リセットしたい」「やっぱり merge / rebase をやめたい」という場合は、中止コマンドで操作前の状態に完全に戻せます。

# merge 中止
git merge --abort

# rebase 中止
git rebase --abort

どちらも操作を始める前の状態へ巻き戻します。 「難しいコンフリクトに直面したときはいったん --abort で逃げる」という選択肢があることを覚えておくと、落ち着いて対処できます。

エディタ・ツールの活用

コンフリクトマーカーを手で解消するのは確実ですが、ファイルが多かったり変更量が大きかったりする場合は、専用ツールが助けになります。

Git 付属のマージツール

git mergetool

設定済みのマージツール(vimdiffmeldkdiff3 など)が起動し、3ペイン(自分側・ベース・相手側)で比較しながら解消できます。

# 使用するツールを設定する例
git config --global merge.tool vimdiff

エディタの組み込みサポート

VS Code は .git が存在するディレクトリを開くとコンフリクトマーカーを自動検出し、「現在の変更を適用」「受信した変更を適用」「両方を適用」のボタンをインライン表示します。 JetBrains IDE(IntelliJ、PyCharm 等)も同様の3-way マージエディタを内蔵しています。 コマンドラインに慣れるまでは、これらを使うのが解消のミスを減らすうえで有効です。

競合を小さく保つ運用のコツ

コンフリクトは完全に防げるものではありませんが、影響を最小限にする運用は工夫できます。

こまめに main を取り込む

ブランチが main から大きく乖離するほど、コンフリクトは複雑になります。 機能開発中でも定期的に git fetch + git rebase main(または git merge main)を行い、差分を小さく保つのが有効です。

# フィーチャーブランチ作業中
git fetch origin
git rebase origin/main

ファイルの責務を分ける

1つのファイルに複数の機能が詰め込まれていると、異なるタスクのブランチが同じファイルを編集してコンフリクトしやすくなります。 ファイルを適切に分割しておくと、コンフリクトの発生頻度を下げられます。

作業範囲を細かくする

長期間かかる大きなタスクより、短期間で完了できる小さなタスクに分けて進めると、mainとの乖離が生まれにくくなります。 「1つのブランチの作業期間は数日以内」を目標にすると、コンフリクトのリスクを自然に減らせます。

まとめ

  • コンフリクトは「同じ箇所への異なる変更」を自動で判断できないときに起きる Git の通常動作
  • <<<<<<<=======>>>>>>> マーカーの読み方を覚えれば、どこを直せばいいかは明快
  • merge のコンフリクト解消:ファイル編集 → git addgit commit
  • rebase のコンフリクト解消:ファイル編集 → git addgit rebase --continue
  • git status でコンフリクト中のファイルを把握できる
  • git merge --abort / git rebase --abort でいつでも操作を中断して元に戻せる
  • VS Code などのエディタの組み込みサポートを活用すると解消ミスを減らせる
  • こまめに main を取り込む運用でコンフリクトの影響を小さく保てる

次回はリモートリポジトリと PR(プルリクエスト)運用を取り上げます。 GitHub を使ったチーム開発のワークフロー、git push / git fetch / git pull の使い方を学びます。