Communityコーディング&開発github.com

roselletabuena/git-conflict-resolution

マージ、リベース、チェリーピック、リバート、スタッシュポップ時のGitコンフリクトを正しく解決します。どちらかの変更を単に選ぶのではなく、両方の意図を理解して組み合わせ、ビルド結果を確認した上で操作を継続します。

git-conflict-resolution とは?

このスキルは、Gitのコンフリクト解決を支援するための高度なAIアシスタントです。`<<<<<<<`マーカー、”CONFLICT (content)”、”both modified”、”deleted by us/them”といったコンフリクト表示、または「コンフリクトを修正して」「リベースを完了して」「解決して続行」といったユーザーの依頼に応答して起動されます。特に、リベース中の`--ours`や`--theirs`が直感と逆の意味になる落とし穴を避けるために設計されています。このスキルはまず、現在のGit操作(マージ、リベース、チェリーピックなど)を特定し、`ours`と`theirs`が何を意味するかを正しく把握します。次に、`zdiff3`コンフリクトスタイルや`git show`コマンドを用いて、コンフリクトのベース(共通祖先)を含む全体像を可視化し、各変更の意図をコミットメッセージから読み取ります。解決の方針として、単に片方を採用するのではなく、両方の変更を意図に従って組み合わせることを推奨します。例えば、一方が`tax_rate`パラメータを追加し、他方が`discount`パラメータを追加した場合、両方のパラメータを受け取る関数を作成します。生成ファイルやバイナリファイルは適切なツールで再生成するか、意図的に片方を選択します。解決後は、`git diff --check`でコンフリクトマーカーの残存を確認し、ビルドとテストを実行して整合性を検証します。最後に、適切な`--continue`コマンドで操作を継続します。また、`rerere`機能を利用して同じコンフリクトが繰り返す場合の解決を支援したり、判断が難しい場合にユーザーに確認するよう促します。このスキルはClaude Code、Cursor、CodexなどのAIエージェント向けに設計されており、開発者が安全かつ効率的にコンフリクトを解決し、意図しない変更の破棄を防ぐのに役立ちます。

対応~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/roselletabuena/skills/tree/HEAD/plugins/git-workflows/skills/git-conflict-resolution

Installed? Explore more コーディング&開発 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

お気に入りのAIに質問する

このエージェントスキルを事前に読み込んだ状態で新しいチャットを開きます。

ドキュメント

コンフリクトの解決

コンフリクトとは、Gitが同じ箇所に対する2つの変更を検出し、推測を拒否した状態です。解決とは意図に関する判断であり、安全なデフォルトは両方の意図を保持することです。片方を削除して解決すると、マーカーは消えますが問題は悪化します。ビルドは依然として成功し、同僚の変更が失われます。

1. 現在の操作を把握する - ours と theirs は操作に依存する

git status                  # "rebase in progress" / "You have unmerged paths" / "cherry-pick in progress"
操作--ours / HEAD 側--theirs 側
merge自分がいるブランチマージされるブランチ
rebaseリベース先のブランチ (上流)リプレイ中の自分のコミット
cherry-pick、revert、stash pop自分がいるブランチ適用されるコミット

リベースの行が落とし穴です。git rebase main 中は、「ours」が main で、「theirs」がユーザー自身の作業であり、マージとは逆になります。git checkout --theirs file をリベース中に実行すると、自分の変更が保持され、上流の変更が破棄されます。いずれかのフラグを使用する前に、この表をもう一度読んでください。

2. マーカーだけでなくコンフリクト全体を確認する

git diff --name-only --diff-filter=U        # マージされていないファイル
git status                                   # タイプ: both modified / deleted by us / both added
git config merge.conflictStyle zdiff3        # 共通祖先も表示する (git 2.35+; 古いバージョンでは diff3 を使用)
git checkout --conflict=zdiff3 -- <file>     # ベースを表示して1つのファイルを再レンダリングする (そのファイルに既に行った編集は破棄される)

ベースが見えると、マーカーは3つの部分(あなたの側、オリジナル、相手の側)で構成されます。ほとんどの一見したコンフリクトは、各側が何から変更したかがわかれば明白になります。

各側がなぜ変更したかを理解するには:

git log --merge -p -- <file>                 # 両側からのファイルに触れるコミット (マージまたはリベース進行中)
git show REBASE_HEAD                          # リベース中: リプレイ中のコミット
git show :1:<file>  :2:<file>  :3:<file>     # ベース、"ours"、"theirs" をファイル全体として

コミットメッセージを読んでください。側面ごとに一文で説明できない解決策は、推測です。

3. 組み合わせて解決する

  • 両側が同じ行を異なる理由で編集した場合 - 両方を行うバージョンを書きます。例えば、一方が tax_rate パラメータを追加し、他方が discount パラメータを追加した場合、解決後の関数は両方を受け取り、両方を適用します。
  • 一方が移動または名前変更し、他方が編集した場合 - 新しい場所に編集を適用します。古いファイルと新しいファイルの両方を維持しないでください。
  • 一方が削除し、他方が変更した場合 - これは機械的な判断ではなく、真の決断です。なぜ削除されたのかを調べてください。変更がまだ重要であれば、それを引き継ぐ必要があります。そうでなければ、削除が優先されます。不明な場合はユーザーに確認してください。
  • 生成ファイルとロックファイル (package-lock.json、poetry.lock、go.sum、スナップショット、コンパイル出力) - 手動でマージしないでください。いずれかの側を採用した後、ツールで再生成します (npm install、poetry lock、go mod tidy、スナップショットコマンド)。
  • バイナリファイル - マージは不可能です。意図的に一方を選択し(上の表に注意)、その旨を伝えてください。

テキストはきれいにマージできても、間違っていることがあります。一方が関数名を変更し、他方が古い名前への呼び出しを追加した場合、Gitはコンフリクトを検出しません。だからこそ、ステップ4でビルドを行うのです。

一括フラグは判断を隠します。 git checkout --ours .、-X ours / -X theirs (コンフリクトのあるハンクをすべて一方に解決する)、および -s ours (もう一方のブランチを完全に破棄する) は、それぞれ内容を確認せずに作業を捨てます。ユーザーがまさにそれを求め、何が破棄されるかを理解している場合にのみ使用してください。

4. 検証してから続行する

git diff --check                              # 残存するコンフリクトマーカーと空白エラーがあると失敗する
grep -rnE '^(<<<<<<<|=======|>>>>>>>)' .      # 念のため、git diff-check がチェックしないファイルも対象
# ビルドして最速の関連テストを実行
git add <resolved-files>

該当するコマンドで続行します。エディタが開く場合は GIT_EDITOR=true を追加します。非対話型シェルではエディタがハングするため:

git merge --continue            # または: git commit --no-edit
GIT_EDITOR=true git rebase --continue
GIT_EDITOR=true git cherry-pick --continue

リベースはコミットを1つずつリプレイするため、次のコミットで再び停止する可能性があります。このループを繰り返します。コンフリクトが現れた最初のコミットで解決してください。後のコミットは同じコンフリクトの繰り返しである可能性があります。

解決後にテストが失敗した場合、その失敗は解決に関する情報です。テストを編集して「修正」したり、そのまま続行したりせず、どの側の動作がテストで保護されているかを把握するまでは行わないでください。

5. いつ止めて質問するか

以下の場合は、推測するのではなく止めてください:

  • 両側が互換性のない方法でビジネスロジックを変更した場合(異なる税金ルール、異なるリトライポリシーなど);
  • 各側がなぜ変更を行ったかを一文で説明できない場合;
  • 解決によってテストが失敗し、原因が不明な場合。

両方の意図を述べ、何を選びその理由を説明し、ユーザーに判断を委ねてください。--continue より前のすべての操作は元に戻せます: git merge --abort、git rebase --abort、git cherry-pick --abort はツリーを開始時の状態に戻します。

繰り返し発生するコンフリクト

長いリベースで同じコンフリクトが繰り返す場合、またはブランチが繰り返しマージされて放棄される場合:

git config rerere.enabled true      # 各解決を記録し、次回に再適用する
git rerere diff                      # 記録された内容
git rerere forget-path <file>        # 誤った記録された解決を破棄する

rerere は解決を確認なしで再適用するため、誤った解決が伝播します。rerere で解決されたコンフリクトの後も、結果を確認してください。

ブランチが長期間存続し、大きく遅れている場合、トランクを1回マージすると通常1回のコンフリクトラウンドで済みますが、リベースはコミットごとにコンフリクトを再生します。その選択はユーザーに委ねられます。

ガードレール

  • コンフリクトをなくすために、片方を全面的に採用して解決しないでください。 反対側は誰かの作業です。
  • ユーザーが完了を依頼したリベースを、面倒だからといって中止しないでください。 問題が発生している場合にのみ中止し、その旨を伝えてください。
  • 依頼がない限り、解決後にプッシュしないでください。 リベースには強制プッシュが必要です。git-safe-history-rewrite に従ってください。
  • コンフリクトした各ファイルについて報告してください: 両側が何をしたか、何を保持したか、検証できなかったこと。

Individual skills in this repo

This repo contains 2 individual skills — each has its own dedicated page.

関連スキル