2026-09-04 · claudecode · git · ai · 個人開発
Claude Codeを本当に並列で走らせる - git worktree と、直列にすべき工程
Claude Codeで複数の作業を同時に走らせたら、こうなった人向けです。
- 片方が
npm installしている最中に、もう片方がpackage.jsonを読んで壊れた dist/のビルド成果物が混ざり、どちらの出力か分からなくなった- 片方の
git checkoutが、もう片方の未コミットの変更を巻き込んだ
論理的な担当範囲を分けても、作業ディレクトリが同じなら物理的に衝突します。
git worktree で分離するのが答えなのですが、片付けの自動化でハマったので、
その中身を書きます。
1. 呼び出しを分けた時点で並列ではない
最初に踏むのがこれです。
(並列にならない)
「Issue 12 を実装して」
→ 完了を待つ
「Issue 13 を実装して」
→ 完了を待つ
Claude Codeは、1つの応答の中で複数のAgent呼び出しを発行したときにだけ、 それらを同時に走らせます。会話のターンを分けると、その時点で直列です。
(並列になる)
「Issue 12, 13, 14 を実装して。
1メッセージ内で3つのAgent呼び出しを同時に発行すること」
「同時に発行すること」を明示しないと、1つずつ丁寧に処理されることがあります。 依頼の形が「1つずつ」だと、そちらに引きずられます。
2. 同じディレクトリで走らせると事故る
冒頭に挙げた3つは、どれも同じ原因から出ています。担当範囲を論理的に分けても、
作業ディレクトリが1つなら物理的に衝突する。 git worktree で分離します。
git worktree add -b issue/12-upload ../worktrees/issue-12 main
git worktree add -b issue/13-profile ../worktrees/issue-13 main
git worktree add -b issue/14-export ../worktrees/issue-14 main
これで3つの独立した作業ディレクトリができます。各担当には、 自分のディレクトリで作業させます。
ディスクの話を正確に書いておきます。共有されるのは .git のオブジェクトだけで、
作業ツリーは複製されます。 手元で測ると、ソース 5,600KB のリポジトリに
作業ツリーを1つ足して増えたのは 5,608KB、.git 側の増分は 44KB でした。
node_modules も共有されません(後述)。3本並列なら、ソースと依存関係が3つぶん要ります。
片付けで気をつけること
終わったら消すのですが、ここに落とし穴があります。
# 危ない書き方
git worktree list | ... | while read branch; do
git merge-base --is-ancestor "$branch" main && git worktree remove ...
done
これは作業がまだ1つも入っていない worktree を消します。 作りたてのブランチは main と同じコミットを指しているので、 「mainの祖先=マージ済み」と判定されてしまうからです。
正しくは、分岐点から見て自前のコミットがあるかを先に見ます。
fork="$(git merge-base main "$branch")"
own="$(git rev-list --count "${fork}..${branch}")"
[ "$own" -eq 0 ] && continue # 作業ゼロ。消さない
さらに注意点があります。マージ後は merge-base がブランチ先端と一致します。
そのため「マージ済み」と「作業ゼロ」が区別できなくなります。
対策は、ブランチを作った時点で分岐点を記録しておくことです。
git worktree add -b "$branch" "$dir" main
git config "branch.${branch}.myBase" "$(git rev-parse main)"
PRベースで運用しているなら、GitHubのPRのマージ状態を正とするのが確実です。
state="$(gh pr list --head "$branch" --state all --limit 1 --json state -q '.[0].state')"
case "$state" in
MERGED) echo "消してよい" ;;
OPEN|CLOSED) echo "残す" ;;
esac
squash merge は、git だけでは判定できません。
git cherry main "$branch" の全行が - かで判断する方法をよく見かけますが、
これは誤りです。git cherry は patch-id の一致で見るので、複数のコミットを
1つに潰した squash では原理的に一致しません。手元で確かめると、こうなります。
| ブランチ | squash 後の git cherry |
|---|---|
| コミット1個 | -(一致する) |
| コミット2個 | + +(一致しない) |
1コミットのときだけ偶然動くので、試して動いたと思い込みやすい落とし穴です。
当てにならない推測でブランチを消すより、gh が使えるなら PR の状態を見るほうが確実です。
使えないなら、squash の判定はあきらめて手で消します。
3. 他の落とし穴は別記事に分けました
worktree以外にも踏んだ落とし穴がありますが、性質が違うので分けます。
- マージは直列にしないと、壊れたときの切り分けができない
- 並列数の上限は3〜4本。制約はAI側ではなくレビュー側にある
これらは「担当範囲をどう切るか」の話で、別記事で扱っています。 この記事はディレクトリの分離に絞ります。
補足: worktree を使うときの細かい注意
ブランチは1つの worktree からしか checkout できない
同じブランチを2つの worktree で開こうとすると git が止めます。これは安全機構なので、 回避しようとせず、1作業1ブランチを守ってください。
手で rm -rf すると管理情報が残る
ディレクトリを手で rm -rf した場合、gitの管理情報は残ります。
rm -rf ../worktrees/issue-12 # ディレクトリだけ消えた状態
git worktree prune # 管理情報を整理
git worktree remove を使えばこの手間は要りません。
依存関係のインストールは worktree ごとに必要
.git は共有されますが、node_modules は共有されません。worktreeを作るたびに
インストールが走ります。これは分離の代償なので、受け入れるか、
pnpm のようにグローバルストアを持つツールを使うかの判断になります。
CI の設定によっては worktree を認識しない
git rev-parse --git-dir が worktree では .git ではなく
/path/to/main/.git/worktrees/<name> を返します。このパスを前提にした
スクリプトがあると壊れます。git rev-parse --show-toplevel を使うほうが安全です。
まとめ
- 担当範囲を分けても、作業ディレクトリが同じなら物理的に衝突する
git worktreeで分ける。共有されるのは.gitのオブジェクトだけで、 作業ツリーとnode_modulesは本数ぶん増える- 片付けの自動化が危ない。 「mainの祖先か」だけで判定すると、 作業がまだ入っていないブランチを消す
- 「分岐点から見て自前のコミットがあるか」を先に見る
- マージ後は
merge-baseがブランチ先端と一致するので、作成時に分岐点を記録しておく - PR運用なら、PRのマージ状態を正とするのが確実。squashにも対応できる
この運用をClaude Codeのサブエージェント構成として定義し、 worktreeの作成・片付けスクリプトと合わせて配布しています。 無料版はMITで公開しています。
Claude Code に設計・実装・レビューを別々の人格として分担させ、GitHub Issue と
ブランチを軸に並列開発を回すための設定一式を MIT で公開しています。
コピーして ./setup.sh を叩けば動きます。技術スタックには依存しません。
https://github.com/quintetkit/quartet
このワークフローだけで実際にツールを 1 つ作りました。Issue の分割から PR、 レビュー、マージまで記録が全部残っています。うまくいかなかった箇所も消していません。
https://github.com/quintetkit/mdlinkcheck
UI 設計人格・レビュー基準・Issue 単位の並列実行スクリプト・実践ガイド 10 章を 足したものは製品ページにあります。