0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

git rebase でコンフリクトが出ても慌てない。コマンドベースの対処フロー

0
Posted at

はじめに

feature ブランチを最新の main に追従させようと git rebase origin/main を実行したら、こんな出力が出て止まった経験はないでしょうか。

Auto-merging src/pages/Settings.tsx
CONFLICT (content): Merge conflict in src/pages/Settings.tsx
error: could not apply 1a2b3c4... feature: プロフィール編集機能を追加
hint: Resolve all conflicts manually, mark them as resolved with
hint: "git add/rm <conflicted_files>", then run "git rebase --continue".

私も先日これに遭遇し、error: could not apply という文言から「rebase が失敗した」と思い込みました。しかし実際には、これは失敗ではありません。この記事では、rebase の衝突がなぜ起きるかと、止まった状態から完了(または撤退)までのコマンドベースの手順を整理します。

結論

  • 衝突による停止は「自動マージできない箇所を人間に選ばせるための一時停止」であり、rebase の正常な動作
  • 対処は git status で確認 → マーカーを解消 → git addgit rebase --continue」の繰り返し
  • 迷ったら git rebase --abort でいつでも rebase 前の状態に無傷で戻れる

なぜ止まるのか

rebase は、feature ブランチの各コミットを main の先端の上に1つずつ適用し直す操作です。main 側の別の変更(たとえば他の人の PR)と自分のコミットが同じファイルの同じ行域を変更していると、git はどちらを採るべきか機械的に判断できず、そのコミットの適用を保留して停止します。

merge が衝突を1回でまとめて解消するのに対し、rebase はコミット単位で適用するため、衝突もコミットごとに複数回発生しうる点が特徴です。

対処フロー

Step 1: 状態を確認する

git status
interactive rebase in progress; onto 9f8e7d6
You are currently rebasing branch 'feature/xxx' on '9f8e7d6'.
  (fix conflicts and then run "git rebase --continue")

Unmerged paths:
	both modified:   src/pages/Settings.tsx

both modified が衝突しているファイルです。エラーメッセージの見た目に惑わされず、まず git status で「rebase 進行中・解消待ち」という事実を確認するのが出発点です。

どのコミット同士がぶつかったのかを知りたければ、衝突に関与した両側の変更を表示できます。

git log --merge --oneline -- src/pages/Settings.tsx

Step 2: 衝突マーカーを解消する

衝突ファイルを開くと、次のマーカーが入っています。

<<<<<<< HEAD
(main 側=載せ替え先の内容)
=======
(自分のコミットの内容)
>>>>>>> 1a2b3c4 (feature: プロフィール編集機能を追加)

両方の変更が必要なら手で統合し、マーカー行(<<<<<<< ======= >>>>>>>)を残さず削除します。

片側をまるごと採用する場合はコマンドでも解消できますが、ここに rebase 最大の罠があります。

git checkout --ours   src/pages/Settings.tsx   # main 側(載せ替え先)を採用
git checkout --theirs src/pages/Settings.tsx   # 自分のコミットの変更を採用

rebase 中は HEAD が main 側に付け替わっているため、--ours が main 側、--theirs が自分の変更を指します。merge のときと逆なので、機械的に打つと意図と反対の側を採用してしまいます。

Step 3: 解消を宣言して続行する

git add src/pages/Settings.tsx
git rebase --continue

git add が「このファイルの衝突は解消済み」という宣言になります。--continue するとコミットメッセージの確認を経て次のコミットの適用に進み、残りがすべて衝突なく適用されれば rebase 完了です。途中でまた衝突すれば、Step 1 から繰り返します。

3つの出口を使い分ける

コマンド 動作 使いどころ
git rebase --continue 解消済みとして次へ進む 基本はこれ
git rebase --skip 今のコミットを捨てて次へ進む 同じ変更が main に取り込み済みのとき
git rebase --abort rebase 前の状態へ完全に巻き戻す 解消が手に負えないとき・やり直したいとき

--abort は作業ツリーもブランチも rebase 開始前に戻すので、失うものはありません。「いつでも撤退できる」と知っておくだけで、rebase の心理的ハードルは大きく下がります。

なお --skip はコミットそのものを履歴から落とすため、「同じ内容が main に取り込み済み」と確認できたときに限って使うのが安全です。

衝突対応を楽にする設定

zdiff3: 共通祖先も表示する

git config --global merge.conflictStyle zdiff3

衝突マーカーに「分岐前はどうだったか」(共通祖先)のブロックが追加され、どちらが何を変えたのかを読み取れるようになります。

<<<<<<< HEAD
(main 側の内容)
||||||| parent of 1a2b3c4
(分岐前の元の内容)
=======
(自分のコミットの内容)
>>>>>>> 1a2b3c4

rerere: 同じ衝突の解消を再利用する

git config --global rerere.enabled true

一度解消した衝突のパターンを記録し、同じ衝突に再び出会ったとき自動で再適用します。長生きする feature ブランチを何度も rebase する運用では特に効きます。ただし過去の解消が誤っていた場合もそのまま再適用されるので、自動解消された箇所は git diff --cached で確認してから続行するのが無難です。

まとめ

  • CONFLICTerror: could not apply は失敗ではなく解消待ちの一時停止
  • 基本フローは git status → マーカー解消 → git addgit rebase --continue の繰り返し
  • rebase 中の --ours は main 側、--theirs は自分の変更(merge と逆)
  • 手に負えなければ git rebase --abort で無傷で撤退できる

rebase と merge の使い分けそのものは、前回の記事「git rebase と git merge の違いを整理する」で扱っています。

参考リンク

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?