Claude Codeでcherry-pickのバックポートを進める — 衝突の直し方と戻し忘れ確認
保守ブランチごとにworktreeを切り、修正コミットをcherry-pickしてClaude Codeに衝突箇所だけ直させる流れを、gitのコマンドと依頼文つきで示します。hotfixの戻し忘れ確認も扱います。
保守ブランチのバックポートは、ブランチごとのworktreeで進める
バックポートとは、main(開発ブランチ)に入れた修正を、release/2.3 のような保守ブランチへ持ち込む作業です。やることは単純で、修正コミットを git cherry-pick で保守ブランチに適用します。手間がかかるのは、保守ブランチのコードが古く、適用時に衝突が起きる場面です。
Claude Codeでこの作業を進めるなら、保守ブランチ1本につき1つのworktreeを用意し、その中でClaudeを起動します。mainの作業ツリーを汚さず、衝突解決を保守ブランチ側だけに閉じ込められます。
流れは次の6段階です。
バックポートの全体の流れ
- 1
mainで修正し、コミットのハッシュを控える
修正はmainで先にレビューとマージまで済ませます。持ち込むコミットの範囲がここで決まります。
- 2
保守ブランチ用のworktreeをgitで作る
起点は
origin/release/2.3です。claude --worktreeではなく、gitで直接作ります。 - 3
worktree内でClaude Codeを起動する
依存関係の導入も、この作業ツリーの中で済ませます。
- 4
cherry-pickを実行し、衝突したらClaudeに直させる
衝突した箇所だけを対象に依頼します。
- 5
保守ブランチのテストを回してPRを出す
mainで通ったテストが、古いブランチで通る保証はありません。
- 6
mainと保守ブランチの差をgit cherryで確かめる
どちらかにだけ入っている修正を洗い出します。
保守ブランチのworktreeは --worktree ではなくgitで作る
Claude Codeの --worktree は便利ですが、保守ブランチの起点には向きません。新しいworktreeはリポジトリの既定ブランチ(通常は main)を起点に、worktree-<name> という新しいブランチで作られます。起点を変える worktree.baseRef も、取れる値は "fresh" と "head" の2つだけです。
既存ブランチを起点にしたいときは、gitで直接worktreeを作ります。worktree.baseRef にはブランチ名を指定できないためです。設定の詳細はworktree.baseRefとsparsePathsの記事にあります。
バックポート用には、保守ブランチから新しい作業ブランチを切る形にします。
git fetch origin
git worktree add -b backport/1234-2.3 \
../app-release-2.3 origin/release/2.3
cd ../app-release-2.3release/2.3 そのものをチェックアウトせず、backport/1234-2.3 という作業ブランチを切るのには理由があります。gitは、すでに別の場所でチェックアウトされているブランチのworktreeを作ろうとすると、--force を付けない限り拒否します。保守ブランチを手元の別worktreeで開いていても、作業ブランチ方式なら衝突しません。PRも作業ブランチから出せます。
ブランチ名には、元のIssue番号と保守ブランチのバージョンを入れておくと、git worktree list で見たときに用途が分かります。
新しいworktreeは依存関係が空の状態から始まる
worktreeは「新しいチェックアウト」なので、開発環境の初期化が必要です。依存関係の導入は、Claudeに頼んでも自分で実行してもかまいません。
保守ブランチは依存のバージョンがmainと違うことが多いので、ここで入れ直すのが肝心です。mainの依存をそのまま流用すると、古いブランチで動かないものを使ってしまう恐れがあります。
.env のようなgitignore対象のファイルを引き継ぐ .worktreeinclude は、ドキュメント上は「Claude Codeがgitで作るworktree」に適用されると書かれています。git worktree add で自分で作った場合の扱いは書かれていません。必要なファイルは手でコピーするほうが確実です。.worktreeinclude の書き方は専用の記事にまとめています。
cherry-pickで衝突したら、衝突箇所だけClaudeに直させる
worktreeの中でClaude Codeを起動したら、cherry-pickを実行します。コマンドは自分で打っても、Claudeに頼んでもかまいません。
git cherry-pick -x 3f9a1c2cherry-pickの途中は、gitが状態を覚えている
git cherry-pick は作業ツリーがきれいなこと(HEADからの変更がないこと)を要求します。適用が難しいときの挙動は、次のとおりです。
- 現在のブランチとHEADは、最後に成功したコミットのまま止まる
- 問題のコミットを指す
CHERRY_PICK_HEADが設定される(--no-commitを付けたときを除く) - 衝突しなかったパスは、インデックスと作業ツリーの両方に反映される
- 衝突したパスには、
<<<<<<<と>>>>>>>で挟まれた衝突マーカーが入る
つまり、衝突の最中は「適用済みの部分」と「手直し待ちの部分」が混在しています。Claudeに任せるときは、この境界を依頼文に書いておくと範囲がぶれません。
依頼文は「衝突ファイルだけ」「古いブランチの流儀で」と書く
例えば次のような依頼文です。
cherry-pickが衝突しました。git statusで衝突しているファイルを確認し、
そのファイルの衝突マーカーだけを解消してください。
- このブランチ(release/2.3)に存在するAPIだけを使う
- mainで入ったリファクタリングは持ち込まない
- 解消後に、元コミットの意図と差がないかgit diff --cachedで説明する
- git cherry-pick --continueはまだ実行しない最後の1行が要です。--continue の前に、人間が差分を見る時間を作ります。
保守ブランチは、mainの新しい関数やクラスを持っていないことがあります。依頼文に「このブランチに存在するAPIだけ」と書くのは、そのためです。同じ内容をCLAUDE.mdに書いておく方法もあります。ただし、保守ブランチが古くてCLAUDE.mdを持たない場合は、worktreeの中にファイルがありません。その場合は依頼文で毎回伝えます。
## バックポート作業のルール(release/* ブランチ)
- 衝突解消はマーカーのあるファイルに限る。周辺コードの整形や改名をしない
- そのブランチに無い関数・型は使わず、修正の意図を既存のAPIで書き直す
- 解消後はテストを実行し、結果を貼ってから報告する衝突の中身で、次の一手が変わる
衝突を直したあとの切り分け
- Qgit diff --cachedの内容は、元コミットの意図と同じか?答えがいいえのとき答えがはい
Claudeに差分を説明させ、意図が抜けている箇所を指摘して直させます。納得できなければ
git cherry-pick --abortで、cherry-pickを始める前の状態に戻せます。 - Q保守ブランチのテストは通るか?答えがいいえのとき答えがはい
失敗したテストの出力を渡して、原因がバックポートの手直しか、保守ブランチ側の元からの失敗かを切り分けます。後者ならバックポートとは別に扱います。
git cherry-pick --continueで確定する
コミットが作られたら、作業ブランチをpushしてPRを出します。
衝突が解けたら git cherry-pick --continue で続行します。複数コミットをまとめて指定していた場合も、同じコマンドで残りの適用に進みます。取りやめるなら --abort です。
cherry-pickのオプションで、バックポート特有の場面をさばく
保守ブランチへの適用では、通常のcherry-pickにはない場面が出ます。オプションを知っておくと、Claudeへの依頼も具体的になります。
| 場面 | 使うもの | 挙動 |
|---|---|---|
| 由来を記録したい | 使うもの-x | 挙動公開された2ブランチ間(保守ブランチへの修正持ち込みが例)では有用 |
| mergeコミットを持ち込む | 使うもの-m 1 | 挙動メインラインとみなす親の番号(1始まり)を指定する |
| すでに入っている修正に当たる | 使うもの--empty=drop | 挙動重複して空になるコミットを捨てる |
| 複数の修正を1コミットにまとめる | 使うもの-n | 挙動コミットせず、変更をインデックスと作業ツリーに載せる |
-x には落とし穴があります。(cherry picked from commit …) という行が付くのは、衝突なしで適用できた場合だけです。衝突を手で解消したコミットには付かないので、必要なら自分でコミットメッセージに足します。元コミットの追跡が目的なら、見落としやすい点です。
--empty の既定は stop です。前のコミットの結果として空になったコミットに当たると、そこで止まって確認を求めます。バックポートでは、すでに保守ブランチに入っていた修正を二重に適用しようとして起きます。drop を付けると自動で捨てられますが、何が捨てられたかは後で確認します。
-n は、複数の修正を束ねて1本のPRにしたいときに使います。このオプションでは CHERRY_PICK_HEAD が記録されないため、git commit で確定すると、あなたが作者として記録されます。元コミットの再現ではなく、自分の変更として扱う設計です。
hotfixをmainへ戻し忘れていないか、git cherryで確かめる
保守ブランチの修正で怖いのは、mainに入れ忘れたhotfixです。次のリリースで、直したはずの不具合が復活します。逆方向の取りこぼし、つまりmainの修正を保守ブランチへ入れ忘れる事態もあります。
git cherry は、この確認に使えるコマンドです。<head> 側のコミットのうち <upstream> に同等の変更があるかを調べます。同等かどうかは、空白と行番号を除いたdiffで判定されます。出力はコミットごとに1行で、同等の変更があれば -、なければ + が付きます。git cherry-pick で複製したコミットも、この仕組みで検出できます。
git fetch origin
# mainの修正のうち、release/2.3に未適用のもの(+が未適用)
git cherry -v origin/release/2.3 origin/main <基準コミット>
# release/2.3のhotfixのうち、mainに未適用のもの
git cherry -v origin/main origin/release/2.3 <分岐点><基準コミット> と <分岐点> は、調べる範囲の下限です。git cherry の第3引数にあたり、そのコミットより後のものだけが対象になります。リリースを切った時点のコミットやタグを指定します。
確認の方向は、修正をどこで先に作ったかで決まります。
| 修正を先に入れた場所 | 確認したいこと | 実行するもの |
|---|---|---|
| main(通常のバックポート) | 確認したいこと対象の保守ブランチすべてに届いたか | 実行するものgit cherry -v origin/release/x.y origin/main <基準> を保守ブランチごとに |
| 保守ブランチ(hotfix) | 確認したいことmainに戻したか | 実行するものgit cherry -v origin/main origin/release/x.y <分岐点> |
結果を読むときは、+ の行を見ます。+ が残っていても、必ず取りこぼしとは限りません。衝突を手で解消したバックポートは、元コミットとdiffが変わる場合があります。判定がdiffの比較に基づくためです。+ の行は、コミットの件名で対応を目視で確認します。
この確認はClaudeに頼めます。出力を渡して、件名の対応づけを任せる使い方です。
次の git cherry の出力で + が付いたコミットについて、
件名や変更内容から、もう一方のブランチに実質同じ修正があるか調べてください。
見つからないものだけを、未適用の候補として一覧にしてください。出力を見るだけの確認なら、読み取りのコマンドしか走りません。ブランチやコミットを書き換える操作は、確認の依頼とは分けます。
作業が終わったら、worktreeを片付ける
バックポートのPRがマージされたら、worktreeは不要です。git worktree list で一覧を確認し、git worktree remove で消します。
git worktree list
git worktree remove ../app-release-2.3
git branch -d backport/1234-2.3claude --worktree で作ったworktreeは、終了時にClaudeが変更の有無を調べ、削除を確認します。gitで自分で作ったworktreeには、その確認が働きません。ドキュメントがこのクリーンアップを説明しているのは、Claudeが作ったworktreeです。片付けは自分の責任になります。
保守ブランチが増えると、worktreeも増えます。.claude/worktrees 配下に溜まったものの掃除はagent viewの記事が詳しいです。
落とし穴
作業ブランチを消したつもりが、worktreeが残る
git branch -d は、そのブランチがworktreeでチェックアウト中だと失敗します。先に git worktree remove を実行してから、ブランチを削除します。
バックポートでつまずきやすい点
worktreeの中のgitは、共有の .git に書き込む
worktree内のgitコマンドはメインリポジトリの共有 .git ディレクトリに書き込みます。sandboxingもこの書き込みを許可するため、sandbox有効時でも git commit は動きます。作業ブランチへのコミットが、そのままリポジトリ全体のブランチとして見えるわけです。別のworktreeで git branch を実行すると、backport/1234-2.3 が見えます。
一方で、worktreeからmain側のチェックアウトを読むgitコマンドが拒否される報告もあります。経緯はworktreeからmain側のgitが拒否される件にあります。バックポートでmainのコミットを参照するときは、ハッシュを直接渡して git cherry-pick を使えば、main側の作業ツリーを開く必要はありません。
Claudeがworktreeへの入室を拒否することがある
Refusing to use <path> as an isolation worktree というエラーが出ることがあります。メッセージの後半によって原因が変わります。WorktreeCreate hookが別のリポジトリの内側にディレクトリを作ると断られる例があります。原因の見分け方はisolation checksの記事にまとめています。この記事の手順は、リポジトリの外に兄弟ディレクトリ(../app-release-2.3)を作る形です。
テストが通ることと、修正が効いていることは別
保守ブランチで既存のテストが通っても、バックポートした修正が効いているとは限りません。元のIssueで追加した再現テストも一緒に持ち込み、修正前に落ち、修正後に通ることを確かめます。Claudeには次のように頼みます。
バックポートした修正が効いていることを確認してください。
1. 再現テストだけを、修正コミットを外した状態(git revert --no-commit)で実行して失敗を確認する
2. revertを取り消し、同じテストが通ることを確認する
3. 保守ブランチの全テストを実行し、結果を報告するまとめ
保守ブランチごとにworktreeを持ち、その中でcherry-pickの衝突をClaudeに直させれば、mainの作業を止めずに複数のバージョンを保守できます。分かれ目は2点です。保守ブランチの起点はgitで指定する点と、--continue の前に人間が差分を見る点です。取りこぼしの確認は git cherry で行い、+ が残る行だけを件名で見比べます。
参照した一次ソース
この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。