gitマージコンフリクトの解消手順
git pull や git merge の直後にCONFLICT (content): Merge conflict in src/app.js と表示されて作業が止まる—— チーム開発で最も身近なトラブルです。コンフリクトは「壊れた」状態ではなく、gitが判断を保留して人間に委ねている状態です。本記事ではマーカーの読み方、 merge と rebase で ours/theirs の意味が逆転する罠、実践的な解消フローを整理します。
A merge conflict is not a broken state — Git is simply asking you to decide. This guide covers how to read conflict markers, the ours/theirs reversal trap during rebase, one-side resolution with checkout --ours/--theirs, and how to safely abort and retry.
TL;DR
<<<<<<< HEAD〜=======が現在のブランチ、=======〜>>>>>>>が取り込む側- 解消 = 望む最終形に編集し、マーカー3行を全て削除 →
git add→git commit - 迷ったら
git merge --abort/git rebase --abortでいつでも仕切り直せる - rebase 中は ours/theirs の意味が逆転(ours = 適用先、theirs = 自分のコミット)
- ファイル単位の一括採用は
git checkout --ours path/--theirs path - 解消後は必ずビルド・テストを実行してからコミット(両方の変更が論理的に矛盾する場合がある)
1. コンフリクトマーカーの読み方 / Reading conflict markers
コンフリクトが起きたファイルには、gitが両方の版を並べて書き込みます。
function greet() {
<<<<<<< HEAD
return "こんにちは"; // ← 現在のブランチ(自分)の内容
=======
return "Hello, world"; // ← 取り込む側(相手)の内容
>>>>>>> feature/english
}解消とは「このブロックを最終的に残したい形に書き換え、マーカー3行 (<<<<<<< / ======= / >>>>>>>)を削除する」ことです。 どちらか一方を選ぶ必要はなく、両方を組み合わせた第三の形にしても構いません。
現在の状況確認には git status が最も確実です。both modified: と表示されているファイルが未解消のコンフリクトです。
$ git status
Unmerged paths:
(use "git add <file>..." to mark resolution)
both modified: src/app.jsEdit the block to the final content you want, delete all three marker lines, thengit add the file. git statuslists unresolved files as "both modified".
2. merge と rebase で ours/theirs が逆転する罠 / The ours-theirs reversal
ファイル単位で「全部こっち側を採用」したいときは git checkout --ours /--theirs が便利ですが、merge と rebase で意味が逆転することを知らないと、自分の変更を全部捨てる事故につながります。
| 操作 | ours | theirs |
|---|---|---|
git merge feature(main 上で実行) | main(自分の現在地) | feature(取り込む側) |
git rebase main(feature 上で実行) | main(適用先) | feature(自分のコミット) |
rebase は「main の上に自分のコミットを1つずつ再適用する」操作なので、再適用中の HEAD は main 側にあります。そのため --ours = main、--theirs = 自分、と直感の逆になります。rebase 中に「自分の変更を残したい」なら --theirs です。
# ファイル単位で一括採用(merge 中)
git checkout --ours package-lock.json # 自分の版を採用
git checkout --theirs package-lock.json # 相手の版を採用
git add package-lock.json
# rebase 中は意味が逆! 自分の変更を残す場合:
git checkout --theirs src/app.js
git add src/app.js
git rebase --continueDuring rebase, HEAD sits on the branch you are rebasing onto, so ours = the base branch and theirs = your own commits. To keep your changes while rebasing, use --theirs.
3. 実践フロー: 発生から解消まで / Step-by-step resolution flow
# 1. 状況確認 — どのファイルが衝突したか
git status
# 2. 各ファイルを開き、マーカーを探して編集
# (エディタのコンフリクト表示や git grep でも探せる)
git grep -nE '^(<{7}|={7}|>{7})'
# 3. 解消できたファイルをステージ
git add src/app.js
# 4. 全部解消したらコミット(merge ならメッセージは自動生成)
git commit
# rebase の場合は commit ではなく:
git rebase --continue解消後のコミット前に必ずビルドとテストを実行してください。 マーカーを消しても「自分は関数名を変更、相手はその関数の呼び出しを追加」のような論理的な衝突はgitには検出できません(テキストとしては衝突しないことすらあります)。
やり直したいとき
git merge --abort # merge を取り消して開始前に戻る
git rebase --abort # rebase を取り消して開始前に戻る
git checkout --merge src/app.js # 1ファイルだけマーカー付きの状態に戻すResolve, git add each file, then git commit (or git rebase --continue). Always build and test before committing — Git cannot detect logical conflicts. Use --abort to start over safely.
4. コンフリクトを減らす運用 / Preventing conflicts
- ブランチを短命に保つ — 差分が小さいほど衝突面積も小さい。1PR=1関心事
- main を頻繁に取り込む — 長期ブランチでは定期的に
git pull --rebase origin main - 生成物をコミットしない — lockファイルの衝突は
--ours/--theirsで片側を採用し、npm installで再生成するのが定石 - フォーマッタを統一 — インデントや改行コードの差は無意味な衝突を量産する(git push rejected の対処も参照)
まとめ
コンフリクトは「gitが人間に判断を委ねている」だけの正常な状態です。 マーカーの構造(HEAD側 / 取り込み側)を理解し、rebase 時の ours/theirs 逆転にだけ注意すれば、 恐れる必要はありません。迷ったら --abort でやり直せることを覚えておくと、 落ち着いて対処できます。
Conflicts are Git deferring a decision to you, not a failure. Understand the marker layout, watch for the ours/theirs flip in rebase, verify with build and tests, and remember you can always --abort and retry.