- Git
- 技術 💻
Gitのコンフリクトの解消方法|マーカーの読み方から防ぎ方まで
Gitのコンフリクト(競合)とは、2つのブランチで同じファイルの同じ場所が別々に変更されていて、Gitがどちらを採用すればよいか自動で判断できない状態です。エラーのように見えますが、壊れたわけではありません。どちらの変更を残すかを人が決めて、ファイルを直し、コミットすれば解消できます。
この記事では、実際にコンフリクトを起こして、マーカーの読み方から解消、中止、よくあるつまずきまでを手順で解説します。rebaseのときに --ours と --theirs の意味が逆になる点や、ロックファイルの扱い、同じ解消を自動で再利用する rerere など、実務で役立つ内容もまとめています。
コンフリクト解消の早見表
| やりたいこと | コマンド |
|---|---|
| どのファイルがコンフリクトしているか見る | git status |
| コンフリクトしているファイルだけを一覧にする | git diff --name-only --diff-filter=U |
| 解消したことをGitに伝える | git add ファイル名 |
| マージを完了する | git commit |
| rebaseを続ける | git rebase --continue |
| やめて元に戻す | git merge --abort / git rebase --abort / git cherry-pick --abort |
| 自分の側の内容を採用する | git checkout --ours ファイル名 |
| 相手の側の内容を採用する | git checkout --theirs ファイル名 |
| 元の内容も表示する | git config --global merge.conflictStyle zdiff3 |
| 同じ解消を自動で再利用する | git config --global rerere.enabled true |
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Gitのコンフリクトの解消方法|マーカーの読み方から防ぎ方まで」
実際にコンフリクトを起こしてみる
mainブランチとfeatureブランチで、税率を別々に書き換えた状況です。
// 元のファイル(price.js)
const TAX_RATE = 0.08;
const SHIPPING = 500;
- featureブランチ:
0.08を0.10に変更 - mainブランチ:
0.08を0.1に変更
mainにfeatureをマージすると、次のように表示されます。
git merge feature
Auto-merging price.js
CONFLICT (content): Merge conflict in price.js
Automatic merge failed; fix conflicts and then commit the result.
git status --short では、コンフリクトしているファイルに UU が付きます。
UU price.js
コンフリクトマーカーの読み方
ファイルを開くと、次のように両方の変更が書き込まれています。
<<<<<<< HEAD
const TAX_RATE = 0.1;
=======
const TAX_RATE = 0.10;
>>>>>>> feature
const SHIPPING = 500;
| マーカー | 意味 |
|---|---|
<<<<<<< HEAD から ======= まで |
今いるブランチ(HEAD、ここではmain)の内容 |
======= から >>>>>>> feature まで |
取り込もうとしているブランチ(feature)の内容 |
| マーカーの外 | 自動でマージできた部分(そのままでよい) |
元の内容も表示する(zdiff3)
上の表示では、変更前が何だったのかがわかりません。次の設定をしておくと、元の内容も表示されます。
git config --global merge.conflictStyle zdiff3
<<<<<<< HEAD
const TAX_RATE = 0.1;
||||||| abdd467
const TAX_RATE = 0.08;
=======
const TAX_RATE = 0.10;
>>>>>>> feature
const SHIPPING = 500;
||||||| から ======= までが元の内容です。「元は0.08で、両方とも10%に変えようとした(書き方が違うだけ)」ことがすぐにわかります。どちらを残すべきかを判断しやすくなるため、設定しておくのがおすすめです。
解消の手順
1. どちらを残すかを決めて、ファイルを直す
マーカーを削除し、最終的にあるべき内容に書き換えます。片方を選ぶだけでなく、両方の変更を組み合わせることもあります。
const TAX_RATE = 0.1;
const SHIPPING = 500;
2. マーカーが残っていないか確認する
git diff --check
マーカーの消し忘れがあると、x.js:1: leftover conflict marker のように、ファイル名と行番号つきで表示されます。エディタで <<<<<<< を検索するのも確実です。
3. 解消したことをGitに伝えて、完了する
git add price.js
git commit # マージの場合
rebaseの場合は、コミットではなく次のコマンドで続けます。
git add price.js
git rebase --continue
4. 動作を確認する
コンフリクトを解消しただけでは、正しく動くとは限りません。ビルドやテストを実行してから、pushします。
迷ったら中止して元に戻せる
解消の途中で「よくわからなくなった」ときは、中止すれば、コンフリクトが起きる前の状態に戻せます。
git merge --abort # マージを中止
git rebase --abort # rebaseを中止
git cherry-pick --abort # cherry-pickを中止
落ち着いて、変更の内容を確認したり、変更した人に相談したりしてから、やり直せます。
--ours と --theirs:rebaseでは意味が逆になる
ファイル全体を、どちらか一方の内容にしたいときは、--ours または --theirs を使います。
git checkout --ours price.js # 「自分の側」を採用
git checkout --theirs price.js # 「相手の側」を採用
git add price.js
注意が必要なのは、mergeとrebaseで「自分」と「相手」が逆になる点です。実際に確認した結果です。
| 操作 | --ours |
--theirs |
|---|---|---|
mainにいて git merge feature |
mainの内容(0.1) | featureの内容(0.10) |
featureにいて git rebase main |
mainの内容(0.1) | featureの内容(0.10) |
rebaseは、mainの上に、featureのコミットを1つずつ積み直す操作です。積み直している最中は、mainの側が「土台(ours)」、積み直しているfeatureのコミットが「取り込む側(theirs)」になります。featureで作業している自分の変更が --theirs になるため、間違えやすいポイントです。
VS Codeで解消する
VS Codeでコンフリクトしたファイルを開くと、マーカーの上に次のボタンが表示されます。
| ボタン | 動作 |
|---|---|
| Accept Current Change | 今いるブランチ(HEAD)の内容を採用 |
| Accept Incoming Change | 取り込むブランチの内容を採用 |
| Accept Both Changes | 両方を残す(順番に並ぶ) |
| Compare Changes | 2つを並べて比較する |
また、「Resolve in Merge Editor」を選ぶと、左右に両方の変更、下に結果を表示する3画面のエディタで、1行ずつ選びながら解消できます。
「Accept Both Changes」は、両方の行が並ぶだけです。同じ変数を2回定義するなど、そのままでは動かないことがあるため、結果を必ず確認します。
ロックファイルのコンフリクト
package-lock.json、yarn.lock、uv.lock、poetry.lock などのロックファイルは、自動で生成されるファイルです。手で直すと、整合性が崩れることがあります。
# 例:package-lock.jsonがコンフリクトした場合
git checkout --theirs package-lock.json # いったんどちらかに揃える
npm install # package.jsonに合わせて作り直す
git add package-lock.json
先に package.json のコンフリクトを解消し、その内容に合わせてロックファイルを作り直すのが基本です。作り直した後は、テストを実行して確認します。
削除と変更のコンフリクト
片方のブランチでファイルを削除し、もう片方で同じファイルを変更した場合も、コンフリクトになります。
CONFLICT (modify/delete): config.js deleted in feature and modified in HEAD. Version HEAD of config.js left in tree.
| 決めたこと | コマンド |
|---|---|
| ファイルを残す | git add config.js |
| ファイルを削除する | git rm config.js |
なぜ削除されたのかを、削除した人に確認してから判断します。
rerere:同じ解消を自動で再利用する
rebaseを繰り返したり、同じブランチを何度もマージし直したりすると、同じコンフリクトを何度も解消することになります。rerere(reuse recorded resolution)を有効にすると、一度解消した内容を記録し、同じコンフリクトが起きたときに自動で適用してくれます。
git config --global rerere.enabled true
実際に、一度解消したマージを取り消して、もう一度マージした結果です。
Recorded resolution for 'price.js'. ← 1回目の解消を記録
...
CONFLICT (content): Merge conflict in price.js
Resolved 'price.js' using previous resolution. ← 2回目は自動で解消
自動で解消された内容を確認し、問題がなければ git add してコミットします。
コンフリクトを減らす運用のコツ
コンフリクトは避けられないものですが、小さく、早く統合することで、減らしたり、解消を楽にしたりできます。
| コツ | 理由 |
|---|---|
| ブランチを短命にする(数日以内に統合) | mainとの差が小さいほど、コンフリクトも小さくなる |
| mainの変更をこまめに取り込む | 大きなコンフリクトを、小さなうちに解消できる |
| 1つのプルリクエストを小さくする | 変更するファイルと行が少なくなる |
| フォーマッターをチーム全員で揃える | 空白や改行だけの違いによるコンフリクトを防ぐ |
| 同じファイルを大きく変更するときは声をかける | 並行して作業する人と、順番や分担を決められる |
| 大きなファイルを分割する | 1つのファイルに変更が集中しにくくなる |
よくある質問
Q. コンフリクトを解消してコミットした後で、間違いに気づきました。
A. pushする前なら、マージのコミットを取り消してやり直せます(git reset --hard ORIG_HEAD など)。push済みの場合は、修正のコミットを追加するのが安全です。
Q. 空白や改行コードだけのコンフリクトが大量に出ます。
A. フォーマッターの設定の違いや、改行コード(LFとCRLF)の違いが原因のことが多いです。.gitattributes やエディタの設定で改行コードを揃え、フォーマッターの設定をリポジトリで共有します。
Q. GitHubの画面でも解消できますか? A. 簡単なコンフリクトであれば、プルリクエストの画面の「Resolve conflicts」から解消できます。複雑な場合や、ロックファイルなど作り直しが必要な場合は、手元で解消してテストしてからpushする方が確実です。
まとめ
- コンフリクトは、同じ場所の変更をGitが自動で選べない状態。人が決めて直せば解消できる
<<<<<<<から=======が今いるブランチ、=======から>>>>>>>が取り込む側- zdiff3 を設定すると、元の内容も表示されて判断しやすい
- 解消したら
git add→git commit(rebaseならgit rebase --continue)。テストも実行する - 迷ったら
--abortで元に戻せる - rebaseでは
--oursと--theirsの意味が逆になる - ロックファイルは手で直さず作り直す。同じ解消の繰り返しは rerere で自動化
- 小さく、早く統合することが、最大の予防策
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Gitのコンフリクトの解消方法|マーカーの読み方から防ぎ方まで」
株式会社RJCでは、チーム開発の基本を、実際に手を動かしながら仲間と教え合っています。こうした知識を共有しながら、一緒に成長を楽しんでいます。
株式会社RJC ― SI事業・SES事業・AI駆動開発。未経験からエンジニアに育てるのが得意な会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。