Claude Media
Claude Codeで大きな差分を複数PRに分割する — 依存順に切るブランチ運用

Claude Codeで大きな差分を複数PRに分割する — 依存順に切るブランチ運用

数百ファイルの変更を、依存の浅い層から順にブランチへ切り出し、各ブランチでテストを通してからPRにする手順です。Plan modeでの分け方、git restoreでの写し方、取りこぼしの検査まで。

Claude Codeに機能開発を任せると、気づけば1本のブランチに数百ファイルの差分が溜まることがあります。そのまま出しても、レビューする側が追いきれません。この記事は、できあがった大きな差分を、依存の浅い層から順に複数のPRへ切り出す手順をまとめたものです。素のgitとClaude Codeだけで進め、各ブランチで必ずテストを通します。

分割の単位は「ファイル数」ではなく依存の層

差分を同じ件数ずつに切ると、後ろのPRが前のPRの未マージ部分を参照して、単独ではビルドが通りません。切り方の基準は件数でなく、依存の向きです。

典型的な層は、次のように下から積み上がります。

層中身の例単独で通すテスト
1. 型・スキーマ中身の例型定義、DBマイグレーション単独で通すテスト型検査、マイグレーション適用
2. コア処理中身の例ドメインのロジック、ユーティリティ単独で通すテストユニットテスト
3. 境界中身の例APIハンドラ、CLIのエントリ単独で通すテスト統合テスト
4. 画面・設定中身の例UI、設定ファイル、ドキュメント単独で通すテストE2E、ビルド

下の層は上の層を知らないので、層1から順に、依存の浅いPRを1つずつマージすれば、どの時点の main もビルドが通ります。逆向きに切ると、層1のPRが層3の型を欲しがって詰まります。

層の数は、差分の実態で決まります。2層で済む変更も、6層に割れる変更もあります。

Plan modeで切り方だけ先に決める

分割は、手を動かす前の設計が出来を決めます。Claude CodeのPlan modeは、ファイルを読んで計画を提案するだけで、承認するまで編集をしません。起動時に指定するか、セッション中に Shift+Tab を押して切り替えます。

claude --permission-mode plan

まず変更ファイルの一覧を機械的に出し、それを計画の材料にします。git diff --name-only はファイル名だけを出力し、三点ドットの形は共通祖先との差分を指します。

git diff --name-only main...feature/export-v2 > changed-files.txt
wc -l changed-files.txt

依頼の例は次のとおりです。

changed-files.txt の各ファイルについて、import の向きを調べて依存の浅い順に
層へ分けて。層ごとに、単独で通せるテストコマンドも書いて。
どの層にも入らないファイルは別枠で列挙して。編集はしないで。

返ってきた計画では、次の3点を人が確かめます。

  • 層の境界で、上の層から下の層への参照が残っていないか
  • テストファイルが、テスト対象と同じ層に入っているか
  • ロックファイルや自動生成ファイルが、どの層にも入らず宙に浮いていないか

最後の点は見落としやすい箇所です。依存を入れた層に、ロックファイルの更新もまとめて載せます。

層ごとにブランチを切って差分を写す

計画が固まったら、元のブランチの内容を層ごとに新しいブランチへ写します。使うのは git restore の --source です。指定したコミットやブランチの内容で、作業ツリーのファイルを置き換えます。

層1のブランチは main から切ります。

git switch -c split/1-types main
git restore --source=feature/export-v2 --staged --worktree -- \
  $(grep -E '^(src/types|migrations)/' changed-files.txt)
git add -A
npm test
git commit -m "add: エクスポート機能の型とマイグレーションを追加"

層2は、層1のブランチの先頭から切ります。ブランチの起点を前の層にするのが、積み上げ運用の要です。

git switch -c split/2-core split/1-types
git restore --source=feature/export-v2 --staged --worktree -- \
  $(grep -E '^src/core/' changed-files.txt)
git add -A
npm test
git commit -m "add: エクスポート処理のコアロジックを追加"

これを最上位の層まで繰り返します。ポイントは、git commit の前に必ずその層のテストを通すことです。落ちたら、写し忘れた依存ファイルが元ブランチにあります。それを前の層に戻すか、今の層に足すかを決めます。

Claude Codeに頼む場合は、ブランチ名と層の対応を渡し、ブランチを切る手順をひとまとまりで任せます。

計画の層1から順に、split/<番号>-<名前> ブランチを前の層の先頭から切って。
各層で git restore --source=feature/export-v2 で該当ファイルを写し、
テストが通ってから1コミットにして。テストが落ちたら止まって、
どのファイルが足りないかを報告して。push は実行しないで。

git restore のパス指定は、ファイルが元ブランチに存在する前提で動きます。元ブランチで削除や名前変更をしたファイルは、この方法では写り切らないことがあります。その取りこぼしは、次の検査で拾います。

全層を積んだ結果が元の差分と一致するか確かめる

分割で最も怖いのは、どの層にも入らなかったファイルが黙って消えることです。最上位のブランチと元のブランチを比べれば、取りこぼしはすぐ分かります。

git diff --stat split/4-ui feature/export-v2

出力が空なら、分割後の最終状態は元のブランチと同じです。ファイルが出力に残っていれば、それが写し漏れです。削除や名前変更は、ここで見つかります。見つけたファイルは、依存の向きに合う層へ移してコミットし直します。

この検査は、Claude Codeに任せず自分の目で見る価値があります。出力が空であることだけを確かめる、短い作業だからです。

各ブランチのテストを並列に回す

層が4つあれば、テストも4回です。重いテストなら、層ごとにworktreeを分けて並列に回せます。worktreeは、同じリポジトリの別ブランチを別ディレクトリにチェックアウトする仕組みです。既存のブランチを使う場合は、gitで直接作ります。

git worktree add ../split-2 split/2-core
git worktree add ../split-3 split/3-api
git worktree list

それぞれのディレクトリで claude を起動し、テストを走らせます。.env のようなgit管理外のファイルは、新しいworktreeには存在しません。コピーが必要なら.worktreeincludeの書き方を使います。

役目を終えたworktreeは、git worktree remove ../split-2 で消します。

1つのファイルに複数の層の変更が混ざっているとき

ファイル単位の git restore では切れないケースがあります。たとえば src/api/index.ts に、層3の新しいハンドラと、層2で先に入れたい共通処理の修正が同居している場合です。

git restore には -p(--patch)があり、復元元と現在の差分をハンクごとに選んで取り込めます。対話で y と n を答えていく操作なので、Claude Codeに任せるより、人が差分を見ながら進める場面です。

git restore --source=feature/export-v2 --staged --worktree -p -- src/api/index.ts

層2のブランチでは、共通処理のハンクだけを y、新しいハンドラのハンクを n にします。ハンドラは層3のブランチで改めて取り込みます。切れないほど絡んだファイルが多いなら、層の定義そのものが粗すぎる合図です。計画を見直し、共通処理を先に独立した層へ抜き出します。

分割しないほうがよい差分

すべての大きな差分を割る必要はありません。次の2つは、分けないほうが読みやすくなります。

  • フォーマッタの一括適用や名前の一括置換のように、中身が機械的な変更。差分は大きくても、レビューは「同じ規則で変換された」ことの確認で済みます。ほかの変更と混ぜず、単独のPRにします
  • 自動生成ファイルの更新。生成元を含む層に同梱し、生成物の中身は読まずに済ませます

逆に、機械的な変更を機能の変更と同じブランチに混ぜてあるなら、分割の第一歩はその切り離しです。機械的な変更を先に main へ入れておくと、残りの差分が小さくなり、層の数も減ります。

分割用のworktreeをどの地点から作るか

Claude Codeの --worktree は、既定ではリポジトリの既定ブランチを起点にworktreeを作ります。分割の途中経過を持ったまま別セッションを開きたいときは、worktree.baseRef を "head" にすると、手元の HEAD から作れます。

{
  "worktree": {
    "baseRef": "head"
  }
}

ただし、この設定にブランチ名は指定できません。層2のブランチなど、特定のブランチからworktreeを作るときは、前の節のように git worktree add で直接作ります。

PRを積み上げて出し、下の層のマージ後に付け替える

ブランチを押し出す前に、PRの向き先を決めます。層1のPRは main を向け、層2のPRは層1のブランチを向けます。こうすると、層2のPRの差分には層2の変更だけが出ます。

PRの作成はClaude Codeに任せられます。create a pr のように頼めば、変更の要約からPR作成まで進めます。Claude Codeは、gh pr create で作ったPRにセッションを紐づけるので、あとから claude --from-pr 1234 で当時のセッションを選べます。向き先の指定は、依頼にはっきり書いておきます。

split/2-core の PR を作って。ベースは main ではなく split/1-types にして。
説明の冒頭に「層1のPRに依存。層1のマージ後に main へ付け替える」と書いて。

層1のPRがマージされたら、層2のブランチを main に付け替えます。git rebase --onto の出番です。「あるブランチの上に積んだ作業を、その機能がマージされた先へ移す」という使い方が、gitのドキュメントにそのまま載っています。

git fetch origin
git rebase --onto origin/main split/1-types split/2-core

この形は、split/1-types から split/2-core までのコミットだけを、origin/main の上に置き直します。層1のコミットを巻き込みません。付け替えたあとでテストをもう一度回し、PRのベースを main に変更してからpushします。

層が3つ以上あるなら、層3のブランチも同じ手順で順に付け替えます。

Claude Codeの権限を、分割作業の形に合わせる

分割作業は、ブランチを切って写してコミットするだけなら、確認の連続になります。一方で、pushだけは慎重に扱いたい作業です。次の設定なら、コミットは確認なしで進み、git push で始まるコマンドは拒否されます。

{
  "permissions": {
    "allow": [
      "Bash(git switch *)",
      "Bash(git restore *)",
      "Bash(git add *)",
      "Bash(git commit *)",
      "Bash(npm test *)"
    ],
    "deny": [
      "Bash(git push *)"
    ]
  }
}

ルールは、* の前に書いた語までを文字どおり照合します。git -C . push のように書き換えた押し出しはこのルールに一致しないので、PRの向き先を確かめるまでpushは人の手で行う、と決めておくと確実です。

手順をCLAUDE.mdに書いておけば、毎回依頼文で説明する手間が減ります。

# 大きな差分を分割するとき
- 分割元ブランチを source、層ごとのブランチを split/<番号>-<名前> と呼ぶ。
- ブランチは前の層の先頭から切る。最初の層だけ main から切る。
- 写すときは git restore --source=<source> を使う。cherry-pick は使わない。
- コミットの前に、その層のテストを必ず実行する。
- 最後に git diff --stat <最上位の層> <source> が空であることを確認する。
- git push は実行しない。

つまずきやすい点

落とし穴

テストが後ろの層のファイルに依存して落ちる

層2のテストが、層3にある設定ファイルを読んでいると、層2のブランチで落ちます。落ちた原因が写し漏れなのか、層の切り方の誤りなのかを分けて考えます。単にファイルが足りないだけなら、そのファイルを今の層に足します。依存の向きが逆なら、層の境界をやり直します。

もう一つは、コミットの粒度です。1層を1コミットにすると、PR内の差分は層のまとまりとして読めます。1層に数十コミットを残すと、元ブランチの試行錯誤がそのまま出て、レビューの負担が戻ります。

Mergifyのようなスタックを管理する道具を使うと、付け替えやPRの更新を自動化できます。道具の入れ方と権限はMergify CLIの記事にあります。ここで見た素のgitの手順は、その道具が裏でやっていることの理解にも役立ちます。

分割後のレビューと、似た作業との切り分け

出したPRは、層ごとに目的が絞られています。観点別の指摘を得たいなら、pr-review-toolkitのように専門のエージェントでPRを読む方法があります。

コミットからPR作成までを1コマンドで済ませたい場合は、commit-commandsプラグインが使えます。層ごとのブランチに対して、同じコマンドを順に当てられます。

差分の分割ではなく、モノリスの切り出しそのものを計画する作業は別です。切り出す順番をPlan modeで決める手順は、モノリスを分割する順番にまとめています。

分割は、最初の層だけが難しい作業です。層1の境界が決まれば、残りは同じ手順の繰り返しになります。大きな差分が溜まる前に、機能の途中で層ごとにコミットしておく運用なら、そもそも分割の手間が小さくなります。

参照した一次ソース

この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。

  1. code.claude.com/docs/en/common-workflows
  2. code.claude.com/docs/en/worktrees
  3. code.claude.com/docs/en/permissions
  4. git-scm.com/docs/git-restore
  5. git-scm.com/docs/git-rebase
この記事を共有:XはてブLinkedIn