name: conflict-resolver maintainer: gotomts description: Git のコンフリクトを、戦略選定から解消・検証まで対話的に支援する。 argument-hint: <PR番号 or ブランチ名(省略可)> allowed-tools: - Bash - Read - Edit - Grep - AskUserQuestion
Conflict Resolver
Git のコンフリクトを、戦略選定から解消・検証まで対話的に支援する。
$ARGUMENTS
基本方針
- コンフリクトマーカーを機械的に処理するのではなく、両側の変更意図を読み取り、ユーザーに判断材料を提供する
- コンフリクトが未発生でも、PR上でコンフリクトが明らかな場合は戦略策定から支援する。 コンフリクトをローカルで発生させる前に最適な解消戦略を選択できるようにする
- 機械的に解決できるもの(import の追加同士、フォーマット差分など)は自動で解消し、報告だけ行う
- 判断が必要なものは、変更の背景を説明したうえでユーザーに方針を聞く
- 全てのコンフリクトについて、何が起きてどう直したかを before/after 付きで見える化する
- ユーザーの言語(日本語 or 英語)に合わせてコミュニケーションする
- コンフリクト解消フェーズは ours / theirs / 両方併記 / ユーザー指示カスタム のみを許容する。 合成・「整合性のための」追加・周辺修正・rename・副次ツール実行(generate, lint:fix, format 等)は一切しない。型/lint エラーは解消完了後の独立コミットで対応する。詳細は Phase 5.0 を参照
ワークフロー
Phase 0: 戦略策定(コンフリクト未発生時)
まず git status と .git/ の状態を確認する。merge / rebase / cherry-pick が進行中であれば Phase 1 へ進む。
git status
ls .git/MERGE_HEAD .git/rebase-merge .git/rebase-apply .git/CHERRY_PICK_HEAD 2>/dev/null
進行中の操作がない場合(PR 上でコンフリクトが見えている、またはユーザーが事前に相談してきた場合)、ローカルでコンフリクトを発生させる前に最適な戦略を選定する。
0.1 ブランチ構造の分析
# 現在のブランチと main の関係を把握
git log --oneline main..HEAD
git log --oneline HEAD..main | head -5
git log --cherry-mark --left-right main...HEAD
--cherry-mark の出力からコミットを分類する:
=— パッチ等価(既に main に存在するコミット)>— このブランチ固有のコミット<— main 固有のコミット
0.2 戦略の提示
分析結果に基づき、以下の戦略を提示してユーザーに選択を促す:
| 状況 | 推奨戦略 | 理由 |
|---|---|---|
| 全コミットがこのブランチ固有 | rebase | 履歴がクリーンになる |
ベースブランチがマージ済み(= コミットが多数) |
cherry-pick | 重複コミットを除外して必要なものだけ適用できる |
| コンフリクトが少なく単純 | merge | 最も安全、履歴に merge commit が残る |
ガードレール強度の注意:
- cherry-pick / rebase: コミット単位で diff が固定され、「ours か theirs か」のスコープが本質的に明瞭(強)
- merge: 1 つの merge commit に統合する性質上、「両者を統合した最善形」を作ろうとする合成思考を誘発しやすい(弱)
- 多コミット PR (5 commit 超) や、main の進化が大きい場合は merge を避け cherry-pick を優先 する。merge を選ぶ場合は、Phase 5.0 のスコープルール遵守を冒頭でユーザーに宣言する
0.3 ベースブランチがマージ済みパターン
= マークのコミットが存在し、ユーザーがベースブランチのマージを示唆している場合、以下の手順で効率的に処理する:
- コミットを即座に3分類する:
=→ スキップ(パッチ等価、main に存在済み)>で main に同名の<コミットあり → スキップ(ベースブランチ由来)>で main に同名コミットなし → cherry-pick 対象
- 分類結果をテーブルで提示し、cherry-pick 対象の確認を取る
- 確認後、main から新ブランチを作成して cherry-pick を実行
- cherry-pick 中にコンフリクトが発生した場合、Phase 2〜5 の手順で解消する
重要: ユーザーがベースブランチのマージを明示している場合、同名コミットの詳細な差分調査は行わない。 > であっても同名の < が main に存在すれば、ベースブランチ由来と判断してスキップする。詳細調査は cherry-pick 後に問題が見つかった場合にのみ行う。
0.4 コマンドのアンチパターン
git diff <commitA>..<commitB>で異なるブランチのコミット同士を比較しない。 間に存在する全コミットの差分が含まれ、巨大で無意味な出力になる。特定ファイルの現在の差分を確認したい場合はgit diff main HEAD -- <file>を使う。
Phase 1: 状況把握
Phase 0 で進行中の操作が検出された場合、またはユーザーの戦略選択に基づき操作を実行した結果コンフリクトが発生した場合にこのフェーズに入る。
操作の種類によって「ours」「theirs」の意味が逆転するため、最初に正しく特定することが重要:
| 操作 | ours (HEAD) | theirs |
|---|---|---|
| merge | 現在のブランチ | マージ対象ブランチ |
| rebase | リベース先(上流) | リプレイ中のコミット(自分の変更) |
| cherry-pick | 現在のブランチ | cherry-pick 対象コミット |
この対応関係をユーザーへの説明に正しく反映すること。rebase では日常的な感覚と逆になるため、「あなたの変更」「取り込み先の変更」のように意図ベースの表現を使う。
Phase 2: コンフリクト一覧と分類
# コンフリクトファイルの一覧
git diff --name-only --diff-filter=U
# 各ファイルのコンフリクト種別を確認
git status --porcelain
ステータスコードから構造的コンフリクトを判別する:
UU— 両側が同じファイルを変更(内容コンフリクト)UD— 一方が変更、他方が削除DU— 一方が削除、他方が変更AA— 両側が同名ファイルを新規作成AU/UA— 追加と変更の衝突
ユーザーにまず全体像を伝える:
「コンフリクトが 5 ファイルで発生しています。内訳は:
- 内容の競合: 3 ファイル(うち 1 件は自動解消できそうです)
- 削除 vs 変更: 2 ファイル(判断が必要です)
自動解消できるものから片付けて、判断が必要なものを順に見ていきましょう。」
Phase 3: コンフリクトの分類基準
各コンフリクトを読み、以下の基準で「自動解消」と「要判断」に分類する。
分類の粒度: 1 ファイル内に複数の独立した競合ブロック(コンフリクトマーカーで区切られた塊)がある場合、ブロック単位で個別に分類する。例えば import 文の競合ブロックと関数ロジックの競合ブロックが同一ファイルにある場合、import 部分は自動解消、ロジック部分は要判断として別々に処理する。ファイル全体を一括で分類しない。
自動解消できるもの:
- import / require / use 文の追加が両側で別々に行われた場合(両方採用)
- コメントの追加・修正が独立している場合
- フォーマットのみの差分(空白、改行、インデント)
- 同一の変更が両側で行われた場合(重複排除)
- package.json や lock ファイルのバージョン競合(最新側を採用し、再インストールを促す)
要判断:
- 同じ関数・メソッドのロジックが両側で異なる変更を受けた場合
- 型定義やインターフェースの変更が競合する場合
- 削除 vs 変更の構造的コンフリクト
- 設定値の変更が競合する場合
- テストの期待値が両側で変わった場合
判断に迷う場合は「要判断」側に倒す。自動解消は保守的に行う。
Phase 4: 自動解消
自動解消できるコンフリクトを処理する。自動解消であっても、何が起きてどう直したかを必ず diff 形式で示す。
報告フォーマット:
[自動解消]
src/utils/index.ts— import 文の統合競合箇所:
<<<<<<< HEAD import { formatDate } from './date'; ======= import { parseQuery } from './query'; >>>>>>> feature/user-profile解消結果:
import { formatDate } from './date'; import { parseQuery } from './query';方針: 両方の import を採用(独立した追加のため副作用なし)
自動解消後、git add はまだ行わない。全てのコンフリクトが解消された後にまとめてステージングする。
Phase 5: 対話的解消
要判断のコンフリクト 1 件ずつについて、以下の情報を調べて説明する。
5.0 スコープルール(必読・違反禁止)
コンフリクト解消フェーズで許容される Edit は以下の 4 通りに限定する:
- ours の内容そのまま(コンフリクトマーカー間の上半分をそのまま採用)
- theirs の内容そのまま(コンフリクトマーカー間の下半分をそのまま採用)
- 両方併記(import 文の独立追加など、機械的に両者を保持できる場合のみ)
- ユーザーが「カスタム」を明示選択し、内容を指示した場合のみ自作
禁止される行為:
- ours にも theirs にも存在しない内容を勝手に合成する
- 「整合性のため」「型エラー回避のため」を理由に周辺コードを追加・修正する
- rename / refactor / 命名統一・設計調整を解消と同時に行う
- 「ついで」「while I'm here」で別の改善を行う
禁止される副次ツール実行(解消フェーズ中):
- 自動生成(
generate:schemas,orval, code generators 等) - 自動修正(
lint:fix,format,prettier --write等) - 検証コマンド(
check-types,lint,test)も Phase 6 完了まで実行しない
これらが必要な場合は、Phase 6 完了後の独立コミットとして扱う。Phase 7 検証で発覚した型/lint エラーは、Phase 7 以降の独立フェーズで処理する。
Edit 発火直前のセルフチェック:
このコンフリクトに対する Edit を発火する前に、以下を毎回自問する:
- この差分は ours / theirs / 両方併記 / ユーザー指示カスタム のどれに 100% 帰属するか?
- YES → 帰属先をユーザー報告に明記して Edit
- NO → STOP。これは合成。Edit を中止し、ユーザーに改めて選択肢を提示する
冒頭でのフェーズ宣言:
コンフリクト解消の冒頭で、以下をユーザーに明示宣言する:
このフェーズでは ours / theirs / 両方併記 の選択のみを行います。次のものは含めません:
- 自動生成物の再生成(generate:schemas 等)
- lint / format / 型修正
- rename / 設計調整 / 命名統一
- その他の「ついで」作業
上記が必要になった場合は、コンフリクト解消完了後の別フェーズで独立コミットとして扱います。
違反シグナル(検出したら即 STOP):
- 「両ブランチに存在しない内容を書こうとしている」
- 「整合性のために generate を走らせたい」
- 「ついでに rename したい」
- 「型エラーを今ここで直したい」
- 「最終的な正しい状態を作りに行こうとしている」
これらの思考が浮かんだら即座に手を止め、Phase 5.2 の選択肢提示に戻る。
5.1 変更意図の調査
# merge base を特定
git merge-base HEAD MERGE_HEAD # merge の場合
# rebase/cherry-pick の場合は適宜調整
# 各側の該当コミットを特定
git log --oneline --follow -n 5 -- <file>
# コンフリクト箇所周辺の変更理由を理解するため、関連コミットのメッセージを確認
git log --format="%h %s (%an)" -n 3 -- <file>
5.2 ユーザーへの説明
コンフリクトの説明は以下の構造で行う:
- 場所: どのファイルのどの部分か(関数名、行番号など)
- あなたの変更: 自分のブランチで何をしたか、なぜか(コミットメッセージから推測)
- 相手の変更: 取り込み先で何が変わったか、なぜか
- 選択肢: 取りうるアプローチとそのトレードオフ
例:
src/api/client.ts:42—fetchUser関数の引数あなたの変更(feature/user-profile ブランチ, コミット abc123):
userIdに加えてoptions?: FetchOptionsを追加。プロフィール取得時のキャッシュ制御のため。main 側の変更(コミット def456, @tanaka): 戻り値の型を
UserからUser | nullに変更。ユーザーが見つからない場合の型安全性を改善。この 2 つの変更は独立しているので、両方を採用できます。ただし、呼び出し元も
nullチェックが必要になります。どうしますか?
- 両方採用(推奨)
- あなたの変更を優先
- main 側を優先
- カスタム(自分で書く内容を教えてください)
5.3 構造的コンフリクトの扱い
削除 vs 変更の場合は、特に慎重に説明する:
src/legacy/parser.ts— ファイル削除 vs 変更の競合あなたの変更: このファイルを削除(レガシーパーサーの廃止、コミット ghi789) main 側の変更:
parseDateFormat関数にバグ修正を追加(コミット jkl012, @suzuki)考慮点:
- 削除を選ぶ場合、バグ修正が新しいパーサーにも必要か確認してください
- 残す場合、廃止計画との整合性を確認してください
どうしますか?
5.4 ユーザーの回答を適用
ユーザーの選択に従い、Edit ツールでファイルを修正する。コンフリクトマーカー(<<<<<<<, =======, >>>>>>>)を完全に除去し、選択された内容で置き換える(Phase 5.0 のスコープルール厳守)。
Edit 発火直前に再確認: Phase 5.0 のセルフチェック(「この差分は ours/theirs/両方併記/ユーザー指示カスタムに 100% 帰属するか?」)を必ず実施する。
修正後、構文エラーがないか簡易チェックする(以下に限定。型/lint チェックは Phase 7 まで実行しない):
- 対応する括弧の確認
- 明らかな構文エラーの確認
- 関連する import の確認
適用後、結果を diff 形式で表示して確認を取る:
[解消済]
src/api/client.ts:42—fetchUser関数の引数適用した変更:
- async function fetchUser(userId: string): Promise<User> { + async function fetchUser(userId: string, options?: FetchOptions): Promise<User | null> {解消方法: 両方採用(ユーザー選択)
OK ですか?問題があれば修正します。
Phase 6: 完了とレゾリューションレポート
全コンフリクト解消後:
- 変更されたファイルをステージング
- レゾリューションレポートを出力
- 次のステップを案内
git add <resolved-files>
レゾリューションレポート
全てのコンフリクトの解消結果を一覧で表示する。これにより、ユーザーは何が起きて何をしたか一目で振り返れる。
出力フォーマット:
Conflict Resolution Report
操作:
git merge feature/user-profile→mainコンフリクト数: 5 件(自動解消 2 / 対話解消 3)
# ファイル 種別 解消方法 概要 1 src/utils/index.tsUU 自動 import 文を両方採用 2 src/api/client.tsUU 対話→両方採用 引数追加 + 戻り値型変更を統合 3 src/config/settings.tsUU 対話→theirs 設定値を main 側に統一 4 src/legacy/parser.tsUD 対話→削除 ファイル削除を採用 5 package.jsonUU 自動 依存バージョンを最新側に統一 詳細 diff
1. src/utils/index.ts (自動解消)
- <<<<<<< HEAD import { formatDate } from './date'; - ======= import { parseQuery } from './query'; - >>>>>>> feature/user-profile2. src/api/client.ts (対話解消)
- async function fetchUser(userId: string): Promise<User> { + async function fetchUser(userId: string, options?: FetchOptions): Promise<User | null> {ユーザー判断: 両方の変更は独立しているため統合
(以下同様)
次のステップ
- merge の場合:
git commitでマージコミットを作成- rebase の場合:
git rebase --continueで次のコミットへ- cherry-pick の場合:
git cherry-pick --continue問題があればいつでもやり直せます。
レポートの出力原則:
- サマリーテーブルは必ず出力する — コンフリクトが 1 件でも省略しない
- 詳細 diff は折りたたみ(
<details>)で提供する — 全体像を崩さず、必要に応じて展開できるようにする - 自動解消したものも含め、全件を記録する。「勝手に直された」という不安を排除するため
- 構造的コンフリクト(削除 vs 変更)の場合は、選択しなかった側の変更内容も記録し、後で確認できるようにする
Phase 7: 検証
コンフリクト解消後(特に cherry-pick / rebase でブランチを再構築した場合)、プロジェクトの整合性を検証する。
7.1 依存関係のインストール
ブランチ間で依存関係が変わっている可能性があるため、まずパッケージマネージャのインストールを実行する。ロックファイルからパッケージマネージャを特定する:
| ロックファイル | コマンド |
|---|---|
pnpm-lock.yaml |
pnpm install |
package-lock.json |
npm install |
yarn.lock |
yarn install |
bun.lock / bun.lockb |
bun install |
Gemfile.lock |
bundle install |
poetry.lock |
poetry install |
go.sum |
go mod download |
7.2 プロジェクト固有の検証コマンド
以下の優先順位で検証コマンドを特定し、実行する:
- CLAUDE.md — プロジェクトの CLAUDE.md にテスト・lint・型チェックのコマンドが記載されていれば、それに従う
- package.json の scripts —
lint,lint:fix,format,check-types,typecheck,testなどのスクリプトを確認 - Makefile / Taskfile —
make lint,make test等 - CI 設定 —
.github/workflows/,.gitlab-ci.yml等から検証ステップを読み取る
特定のツールやコマンドをハードコードしない。 プロジェクトのコンテキストから適切なコマンドを導出すること。
7.3 エラーの判別
検証でエラーが出た場合、コンフリクト解消に起因するか、main に既存の問題かを切り分ける。変更したファイル(git diff main --name-only)に関連しないエラーは main 由来の可能性が高い。
注意事項
- バイナリファイルのコンフリクトは内容を解析できない。どちらのバージョンを使うかユーザーに聞き、
git checkout --oursor--theirsで対応する - 巨大ファイル(1000行超のコンフリクト)は、コンフリクト箇所を分割して順に処理する
- コンフリクト解消中に
git merge --abort/git rebase --abortをユーザーが希望した場合は、確認の上で実行する。作業中の解消結果が失われることを伝える - lock ファイル(package-lock.json, yarn.lock 等)のコンフリクトは手動で解消せず、一方を採用した上で
npm install/yarn installの再実行を推奨する - コンフリクト解消中に型/lint エラーが見えても、その場で修正しない。 解消フェーズはマーカー除去とユーザー選択の適用のみ。エラーは Phase 7 検証で正式に把握し、それ以降のフェーズで独立コミットとして処理する
- codegen / lint:fix / format などの副次ツールは Phase 6 完了まで実行しない。 自動生成物の不整合は Phase 7 以降で独立コミットとして再生成する