【完全版】git merge で CONFLICT が発生したときの解決手順まとめc

【完全版】git merge で CONFLICT が発生したときの解決手順まとめc

チーム開発で避けて通れないのが git merge 時の CONFLICT(コンフリクト・競合)です。慣れないうちは:

  • <<<<<<< HEAD などのマーカーが何を意味するのか分からない
  • 複数ファイルが一気にコンフリクトして途方に暮れる
  • 「ファイルを削除 vs 編集」「ファイル名変更 vs 編集」など特殊なコンフリクトの対処法が分からない
  • バイナリファイル(画像・PDF)のコンフリクトはどうすれば?
  • git mergetool や VSCode でビジュアルに解決したいが設定方法が分からない
  • 間違えた、マージを途中でやめて元に戻したい
  • 同じコンフリクトが毎回発生して都度解決するのが辛い(rerereの存在を知らない)

など、初心者から中級者まで多くの人がつまづくポイントです。

本記事では、git mergeCONFLICT が発生したときの解決手順の全パターンを、リファレンスとしてまとめ直しました。コンフリクトの仕組みから、マーカーの読み方、コンフリクトの種類別(content/delete/rename/binary)の対処法、--ours/--theirsの一括採用、git mergetool・VSCode・IntelliJでのGUI解決、rerereによる自動化、rebase・cherry-pick時との違い、マージ中止・復旧方法、予防策まで完全網羅。この1本をブックマークすれば、どんなコンフリクトにも落ち着いて対処できるようになります。


目次

結論:CONFLICTが発生したら最初にすること

時間がない方向けに、基本の解決フロー全体を先に示します。

# Step 1: どのファイルがコンフリクトしているか確認
git status

# Step 2: コンフリクトしているファイルを開いてマーカーを見る
# <<<<<<< HEAD(現在ブランチの内容)
# =======(区切り線)
# >>>>>>> feature-branch(マージしようとしたブランチの内容)

# Step 3-A: 手動でファイルを編集してマーカーを消す(内容を決める)
vi conflict_file.txt

# Step 3-B: 片方をまるごと採用する(コマンドで一括)
git checkout --ours   conflict_file.txt   # 現在ブランチの内容を採用
git checkout --theirs conflict_file.txt   # マージ元ブランチの内容を採用

# Step 4: 解決済みとしてステージング
git add conflict_file.txt

# Step 5: 全ファイルが解決済みか確認
git status

# Step 6: マージコミット
git commit

# もし途中でやめて元に戻したいとき
git merge --abort

詳細は以下で順に解説します。


コンフリクトが発生する仕組み

git merge は2つのブランチの差分を自動的に統合しようとしますが、同じファイルの同じ箇所を両方のブランチが異なる内容で変更していた場合、Gitはどちらを正解とするか判断できないためコンフリクトを起こします。

共通の祖先コミット(ベース)
        │
        ├── main ブランチ: 「A」→「B」に変更
        └── feature ブランチ: 「A」→「C」に変更

git merge feature  (mainブランチ上で実行)
→ 「B」にすべきか「C」にすべきか判断不可能 → CONFLICT

Gitが自動マージできるケース(コンフリクトにならない)

共通の祖先コミット
        │
        ├── main: 1行目を変更
        └── feature: 5行目を変更

→ 変更箇所が重複していないためGitが自動統合できる(Fast-forward または Auto-merging)

異なる行の変更は自動マージされるため、コンフリクトになるのは本当に「判断が必要な箇所」だけです。慌てず落ち着いて対処しましょう。


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

コンフリクトが発生すると、Gitは対象ファイルにコンフリクトマーカーを自動挿入します。

<<<<<<< HEAD
現在のブランチ(マージ先、自分がいるブランチ)の内容
=======
マージしようとしたブランチ(マージ元)の内容
>>>>>>> feature-branch

具体例

def greet(name):
<<<<<<< HEAD
    return f"こんにちは、{name}さん"
=======
    return f"Hello, {name}!"
>>>>>>> feature/english-greeting

この場合の選択肢は4つです。

# 選択肢A: HEADの内容を採用(mainブランチの日本語版)
def greet(name):
    return f"こんにちは、{name}さん"

# 選択肢B: feature-branchの内容を採用(英語版)
def greet(name):
    return f"Hello, {name}!"

# 選択肢C: 両方を統合(独自の編集)
def greet(name):
    return f"こんにちは / Hello, {name}!"

# 選択肢D: 全く別の内容に書き直す(両方破棄してゼロから)
def greet(name):
    return f"Bonjour, {name}!"

マーカーを含む3行(<<<<<<<=======>>>>>>>)は必ず削除し、最終的なファイルに残してはいけません。

diff3スタイルでベースも表示する

# ベースコミット(共通の祖先)の内容も表示させる設定
git config --global merge.conflictstyle diff3
<<<<<<< HEAD
現在ブランチの内容
||||||| 共通の祖先(ベース)の元の内容
=======
マージ元ブランチの内容
>>>>>>> feature-branch

3者が見えることで「元はどんな内容で、それぞれがどう変えたか」が分かり、より適切な判断がしやすくなります。コンフリクトが多い環境では、この設定を入れておくことをお勧めします。


コンフリクトの種類別対処法

コンフリクトには「内容が衝突した」だけでなく、複数の種類があります。git statusの出力でどの種類かを判断できます。

種類1:content(内容の衝突)

最も頻出するタイプ。同じ行の内容が両ブランチで異なる場合です。

# git status の表示
# both modified: src/app.py

# 手動で解決する
vi src/app.py
# ↑ マーカーを見て内容を決め、マーカーを削除して保存

git add src/app.py

種類2:delete/modify(片方が削除・片方が編集)

# git status の表示
# deleted by us: config/old-settings.yml       ← 自分が削除、相手が編集
# deleted by them: config/old-settings.yml     ← 相手が削除、自分が編集

どちらか選択して解決します。

# 削除を採用する(ファイルを削除したままにする)
git rm config/old-settings.yml

# 編集を採用する(ファイルを残す)
git add config/old-settings.yml

種類3:both added(両方が同名ファイルを新規追加)

# git status の表示
# both added: src/utils/helper.py

# 内容を確認して手動統合
git diff src/utils/helper.py
vi src/utils/helper.py
git add src/utils/helper.py

種類4:rename/rename(両方が別の名前にリネーム)

# git status の表示
# CONFLICT (rename/rename): old.txt renamed to new-main.txt in HEAD and new-feature.txt in feature-branch

# 採用するファイル名を選ぶ
git add new-main.txt       # mainブランチのリネームを採用
git rm new-feature.txt     # featureブランチのリネームを破棄

種類5:binary(バイナリファイルのコンフリクト)

画像・PDFなどのバイナリファイルはGitがマージできないため、必ず片方を選択します。

# git status の表示
# CONFLICT (content): Merge conflict in assets/logo.png

# 手動マージ不可能なので、どちらかを採用
git checkout --ours   assets/logo.png   # 現在ブランチの画像を採用
git checkout --theirs assets/logo.png   # マージ元ブランチの画像を採用

git add assets/logo.png

コマンドで一括採用する(–ours / –theirs)

内容を精査せずに「現在ブランチを優先する」「マージ元を優先する」と決められる場合は、コマンドで一括採用するのが速いです。

# 特定のファイルについて現在ブランチの内容を採用
git checkout --ours path/to/file.txt

# 特定のファイルについてマージ元ブランチの内容を採用
git checkout --theirs path/to/file.txt

# 全コンフリクトファイルに一括適用(--oursの場合)
git checkout --ours .
git add .

⚠️ --ours--theirs の「どちらがどちらか」は操作の文脈によって変わります

操作--ours--theirs
git merge feature (mainブランチ上)main(現在ブランチ)feature(マージ元)
git rebase main (featureブランチ上)main(リプレイ先)feature(リプレイ中のコミット)

rebase時は --ours--theirs の意味が直感と逆転することがあるため注意が必要です(後述)。

-X オプションで最初からマージ戦略を指定

# マージ時点から全ファイルについて現在ブランチを優先(競合箇所は自動的に--oursを適用)
git merge -X ours feature-branch

# マージ時点から全ファイルについてマージ元を優先
git merge -X theirs feature-branch

ロックファイル(package-lock.jsonyarn.lockなど)の更新を全自動で現在ブランチ優先にしたい場合などに有効ですが、意図せず変更が失われるリスクもあるため慎重に使います。


git mergetool でビジュアルに解決する

テキストエディタでのマーカー編集が面倒な場合、git mergetoolでGUIツールを起動できます。

設定方法

# 使用するツールを確認(環境によって異なる)
git mergetool --tool-help

# vimdiff(デフォルトでインストール済みのことが多い)
git config --global merge.tool vimdiff

# 使いやすいGUIツールを使う場合
git config --global merge.tool meld      # Meld(Linux向け、無料)
git config --global merge.tool opendiff  # FileMerge(Mac標準)
git config --global merge.tool bc        # Beyond Compare(有料)

実行する

# 全コンフリクトファイルを順番にツールで開いて解決
git mergetool

# 特定ファイルのみ
git mergetool path/to/conflict_file.txt

vimdiffを使う場合、画面は3ペイン(左:LOCAL=現在ブランチ、中央:BASE=共通祖先、右:REMOTE=マージ元)で表示されます。下部ペインに統合結果を編集します。

⚠️ git mergetool は解決後に *.orig という元ファイルのバックアップを残すことがあります。不要であれば削除するか、以下で無効化できます。

git config --global mergetool.keepBackup false

VSCode・IntelliJでGUI解決する

VSCodeでのコンフリクト解決

VSCodeはコンフリクトマーカーを自動検出し、インラインにボタンを表示してくれます。

<<<<<<< HEAD の箇所に「現在の変更を取り込む」「入力側の変更を取り込む」
「両方の変更を取り込む」「変更の比較」というリンクが表示される
# VSCodeをgit mergetoolとして設定する
git config --global merge.tool vscode
git config --global mergetool.vscode.cmd 'code --wait $MERGED'

# 以降、git mergetool でVSCodeが起動する
git mergetool

VSCode GitLens拡張でのサポート

GitLens拡張をインストールすると、コンフリクト解決用のUIがさらに強化されます。

IntelliJ IDEA / WebStorm / PyCharm

JetBrains IDEは標準でGitコンフリクト解決ダイアログを内蔵しています。

VCS メニュー → Git → Resolve Conflicts
→ 3ペインのビジュアルマージエディタが起動
(左: 現在ブランチ、中央: 結果、右: マージ元)

コンフリクトが多いプロジェクトではGUIツールを積極的に活用することを推奨します。


複数ファイルのコンフリクトを効率よく処理する

コンフリクトしているファイルを一覧で確認

# コンフリクトしているファイルだけを抽出
git diff --name-only --diff-filter=U

# より詳しく状態を確認
git status | grep -E "^(both|deleted|added by)"

残っているコンフリクトマーカーを検索する

「全部解決したつもり」で git commit しようとして「まだマーカーが残っている」とエラーになることがあります。

# マーカーが残っているファイルを全検索(grep方式)
grep -rn "^<<<<<<< " .

# git diff でコンフリクトマーカーが残っていないか確認
git diff --check

git diff --check はコンフリクトマーカーや末尾の余計な空白を検出してくれるため、コミット前の最終確認として習慣化するのがおすすめです。


git rerere:同じコンフリクトを二度と手動解決しない

rerere(Reuse Recorded Resolution)は、一度解決したコンフリクトの解決方法を記録しておき、次回同じコンフリクトが発生したときに自動的に適用してくれる機能です。

有効化

git config --global rerere.enabled true

使い方

# rerereを有効にしておくと、コンフリクト解決時に自動で記録が作成される
git merge feature-branch
# CONFLICT発生 → 手動で解決 → git add → git commit
# → 解決パターンが ~/.git/rr-cache/ に記録される

# 次回同じパターンのコンフリクトが発生したとき
git merge feature-branch-v2
# → 記録済みの解決パターンが自動適用される
# Resolved 'src/app.py' using previous resolution.

記録済みの解決パターンを確認・削除

# 記録一覧を確認
git rerere status

# 特定の記録を削除
git rerere forget path/to/file.txt

長期間運用しているプロジェクトやリベースを多用する環境では、rerereを有効にするだけで解決工数が大幅に削減できます。


マージを中止して元に戻す

コンフリクトの対処が難しい、または間違ったブランチをマージしてしまった場合は、マージ中断で完全に元の状態に戻せます。

# マージを完全に中止して元の状態に戻す(コンフリクト解決前・解決途中どちらでも可)
git merge --abort

⚠️ --abort は**マージコミットが完了する前(コミット前)**のみ有効です。一度 git commit してしまった後は使えません。

コミット後にマージを取り消す

# マージコミットのハッシュを確認
git log --oneline -5

# マージコミットを取り消す(revertでマージ前の状態に相当するコミットを作成)
git revert -m 1 <merge-commit-hash>
# -m 1 はマージコミットの「1番目の親(マージ先のブランチ)」を残す指定

# または、reset で履歴ごと削除(共有ブランチでの使用は禁止)
git reset --hard ORIG_HEAD   # マージ直前のHEADに戻る

git reset --hard ORIG_HEAD は強力ですが、共有(push済み)ブランチでの使用はチームに影響するため禁止です。git revert によるコミットが安全な取り消し方法です。


rebase・cherry-pick 時のコンフリクト

git rebasegit cherry-pick でもコンフリクトが発生しますが、merge とは挙動が異なります。

rebase 時の違い

git rebase main
# コンフリクト発生!

# 解決後は git commit ではなく git rebase --continue
git add resolved_file.txt
git rebase --continue

# 中止する場合
git rebase --abort

rebase は複数コミットを1つずつ適用し直すため、コミットごとにコンフリクトが発生することがある点が merge と異なります。10コミットをリベースすると最大10回コンフリクト解決が必要になる可能性があります。

rebase 時の --ours / --theirs の逆転に注意

git rebase main  (feature ブランチ上で実行)

# この状態では:
# --ours   = main(リプレイ先、受け取る側)
# --theirs = feature(リプレイ中のコミット、自分のコミット)

# 直感と逆なので注意!
# 「自分のコミットを採用したい」→ --theirs を使う
git checkout --theirs path/to/file.txt

merge では --ours = 自分のブランチ ですが、rebase では --ours = リプレイ先(相手ブランチ)になります。この逆転が混乱を生む頻出ポイントです。

cherry-pick 時の違い

git cherry-pick abc1234
# コンフリクト発生!

# 解決後
git add resolved_file.txt
git cherry-pick --continue

# 中止する場合
git cherry-pick --abort

コンフリクトの予防策

コンフリクトの発生自体を減らす工夫は、チーム開発の生産性に大きく影響します。

1. こまめに pull/rebase してブランチの差分を小さく保つ

# フィーチャーブランチ開発中は定期的に main の最新を取り込む
git fetch origin
git rebase origin/main   # または git merge origin/main

長期間ブランチを切り離したまま開発すると、差分が大きくなりコンフリクトのリスクが増大します。毎日作業開始時に origin/main の最新を取り込む習慣を持つとコンフリクトが激減します。

2. ファイルの責任範囲を分割する

同じファイルを複数人が同時に編集することがコンフリクトの根本原因です。

モジュール分割・ファイル分割でファイルの責任範囲を明確化
  ├── user-service.py    → Aさん担当
  ├── order-service.py   → Bさん担当
  └── payment-service.py → Cさん担当

3. ロックファイルの扱いを決める

package-lock.jsonyarn.lockGemfile.lock などのロックファイルは最もコンフリクトが起きやすいファイルです。

# ロックファイルのコンフリクトは基本的にマージ元を採用して再生成
git checkout --theirs package-lock.json
npm install   # 再生成することで正しいロックファイルになる
git add package-lock.json

チームで「ロックファイルのコンフリクトはマージ元を採用して再生成する」というルールを決めておくと混乱が減ります。

4. .gitattributes でマージ戦略をファイル種別ごとに設定

# .gitattributes
# バイナリファイルはコンフリクト時に現在ブランチを自動採用
*.png  merge=ours
*.jpg  merge=ours
*.pdf  merge=ours

# 行末を統一(Windows/Mac/Linux混在環境でのコンフリクト予防)
* text=auto

5. PRのサイズを小さく保つ

1つのPRで変更するファイル数・行数が多いほどコンフリクトリスクが高まります。機能を細かく分割し、小さなPRをこまめにマージするブランチ戦略(GitHub Flow / trunk-based development)が有効です。


トラブルシューティング

「All conflicts fixed but you are still merging」と表示される

コンフリクトは解決したが git add を忘れている状態です。

# コンフリクト解決後は必ず git add でステージングしてから git commit
git add path/to/resolved_file.txt
git commit

git commit したらコンフリクトマーカーが残っていたと言われる

# コミット前にマーカー残りを確認
git diff --check

# 残っているファイルを確認して再編集
grep -rn "^<<<<<<< " .

マージコミットのメッセージを変えたい

# git commit でエディタが開くので、そこでメッセージを編集する
git commit
# デフォルトで「Merge branch 'feature' into main」が入力されている

意図せず git merge を実行してしまった(まだコミットしていない)

git merge --abort
# 完全に元に戻る

コンフリクトしたファイルを一時的にバックアップしたい

cp path/to/conflict_file.txt path/to/conflict_file.txt.bak

大規模なコンフリクト解決の途中でさらに作業が入る可能性がある場合は、git stash よりも git worktree で別ディレクトリに作業場所を作る方法も有効です。

MERGE_HEAD が残って git が変な状態になっている

稀に .git/MERGE_HEAD ファイルが残りgitが混乱した状態になることがあります。

# マージを完全にリセットする(変更は全て失われるので注意)
git reset --hard HEAD

実用パターン集

feature ブランチを main にマージする標準フロー

git checkout main
git pull origin main
git merge feature/my-feature

# コンフリクト発生時
git status                         # 対象ファイル確認
git diff --name-only --diff-filter=U  # コンフリクトファイル一覧

# 解決
vi src/app.py                      # 内容を確認して編集
git add src/app.py
git commit

# または中止
git merge --abort

PR前にローカルで先行してコンフリクトを解決する

# featureブランチ上でmainの最新を先取りしてコンフリクトを解決してからPR
git checkout feature/my-feature
git fetch origin
git merge origin/main              # または git rebase origin/main
# コンフリクト解決 → commit → push
git push origin feature/my-feature

ロックファイル(package-lock.json等)のコンフリクト一括処理

# Node.jsの場合
git checkout --theirs package-lock.json
npm install
git add package-lock.json

# Rubyの場合
git checkout --theirs Gemfile.lock
bundle install
git add Gemfile.lock

バイナリファイルのコンフリクトを全て現在ブランチで採用

# 画像ファイルのコンフリクトを全て現在ブランチで採用
git diff --name-only --diff-filter=U | grep -E "\.(png|jpg|gif|pdf)$" | \
    xargs git checkout --ours
git add .

コンフリクトが多すぎる場合の分割戦略

# 一旦マージを中止
git merge --abort

# 変更ファイルを確認してどこが衝突しそうか把握
git diff main...feature-branch --name-only

# featureブランチをファイル単位の小さな変更に分割してから順次マージ

よくある質問(FAQ)

Q1. コンフリクトマーカーを消すだけでいいですか?

はい、最終的にファイルに残したい内容だけ残してマーカー3行(<<<<<<<=======>>>>>>>)を全て削除すれば解決です。その後 git addgit commit で完了です。

Q2. --ours / --theirs どっちが自分のブランチですか?

git merge の場合、--ours = 現在いるブランチ(マージ先)--theirs = マージ元ブランチ です。ただしrebase時は逆転するため注意してください。

Q3. コンフリクトを解決したあと git commit -m でメッセージを指定すべきですか?

マージコミットのメッセージは自動で生成されるため、基本は git commit のみ(メッセージなし)でエディタが開き、そのまま保存するのが慣習です。カスタムメッセージが必要なら git commit -m "Merge feature-X, resolve conflict in app.py" のように指定してもかまいません。

Q4. コンフリクトを全部まとめて「現在のブランチ優先」で解決したい

git merge -X ours feature-branch

ただし、この方法はマージ元の変更が全て上書きされるリスクがあります。精査なく一括適用する前に git diff main...feature-branch で差分の規模を確認してください。

Q5. コンフリクト解決中に別の作業が入りました

# 進行中の作業を退避
git stash

# 別の作業を行う

# 作業後に退避した状態を復元
git stash pop
# ただし、コンフリクト解決中の状態に戻るため、引き続き解決作業を続ける

Q6. git rebase 中のコンフリクトが多すぎて手に負えません

git rebase --abort  # リベースを中止して元に戻す

# 代わりにマージで取り込む(コンフリクト解決は1回で済む)
git merge origin/main

履歴のきれいさを重視しない場合、rebaseをあきらめてmergeに切り替えるのも実用的な判断です。

Q7. コンフリクトした内容を、マージ前の状態で見たい

# diff3スタイルを有効にするとベース(共通祖先)の内容も確認できる
git config merge.conflictstyle diff3

# または、各バージョンのファイルを個別に確認
git show :1:path/to/file.txt   # 共通祖先(ベース)
git show :2:path/to/file.txt   # HEAD(現在ブランチ)
git show :3:path/to/file.txt   # マージ元ブランチ

Q8. コンフリクトを解決したあとのコミットグラフはどうなりますか?

git log --graph --oneline で確認
*   a1b2c3d (HEAD -> main) Merge branch 'feature'
|\
| * d4e5f6g feature: add new function
* | h7i8j9k main: fix bug
|/
* k1l2m3n Common ancestor

マージコミット(a1b2c3d)は2つの親を持つコミットとして記録されます。

Q9. コンフリクト解決の記録を再利用(rerere)するには?

git config --global rerere.enabled true

これだけで次回以降、同じパターンのコンフリクトが自動解決されます。

Q10. コンフリクトが怖くて merge を避けてしまいます

コンフリクトはGitのエラーではなく「人間が判断を下すべき箇所」という通知です。適切な解決フローを身につければ、発生しても落ち着いて対処できます。git merge --abort でいつでも元に戻せることも覚えておくと安心です。


参考リンク・関連資料

公式ドキュメント

関連記事(本サイト)


まとめ

git mergeCONFLICT は慌てなければ必ず解決できます。要点を再整理します。

  • まず git status: どのファイルがコンフリクトしているか確認するのが起点
  • マーカーの意味を把握: <<<<<<< HEADが現在ブランチ、>>>>>>>がマージ元、全マーカー行は最後に必ず削除
  • diff3スタイルを活用: merge.conflictstyle diff3 でベースも見えると判断しやすい
  • 種類別の対処: content/delete/rename/binary それぞれ解決方法が異なる
  • 一括採用は --ours/--theirs: ファイル単位での一括採用、mergeとrebaseで意味が逆転する点に注意
  • GUIツールを積極活用: VSCode・IntelliJ・mergetoolで視覚的に解決するのが現代的
  • git add を忘れずに: 解決後のステージングが必須、git commit で完了
  • やり直しは git merge --abort: コミット前なら完全にマージ前の状態に戻せる
  • rerereで繰り返しを防ぐ: 一度解決したパターンを自動再利用できる
  • rebase時は --ours/--theirs が逆転: 混乱したら git rebase --abort で仕切り直す
  • 予防が最重要: こまめなpull/rebase、ファイル責任の分割、小さなPRでコンフリクトを最小化する

本記事は2026年6月時点の情報をもとに、Git 2.40以降での動作確認に基づき作成しています。一部のオプションや挙動はバージョンによって異なる場合があるため、最新の情報は公式ドキュメントもあわせてご確認ください。