Quartet / Quintet

2026-09-04  ·  claudecode · git · ai · 個人開発

Claude Codeを本当に並列で走らせる - git worktree と、直列にすべき工程

Claude Codeで複数の作業を同時に走らせたら、こうなった人向けです。

論理的な担当範囲を分けても、作業ディレクトリが同じなら物理的に衝突します。 git worktree で分離するのが答えなのですが、片付けの自動化でハマったので、 その中身を書きます。

1. 呼び出しを分けた時点で並列ではない

最初に踏むのがこれです。

(並列にならない)
「Issue 12 を実装して」
  → 完了を待つ
「Issue 13 を実装して」
  → 完了を待つ

Claude Codeは、1つの応答の中で複数のAgent呼び出しを発行したときにだけ、 それらを同時に走らせます。会話のターンを分けると、その時点で直列です。

(並列になる)
「Issue 12, 13, 14 を実装して。
  1メッセージ内で3つのAgent呼び出しを同時に発行すること」

「同時に発行すること」を明示しないと、1つずつ丁寧に処理されることがあります。 依頼の形が「1つずつ」だと、そちらに引きずられます。

2. 同じディレクトリで走らせると事故る

論理的な担当範囲を分けても、作業ディレクトリが同じなら物理的に衝突します。

実際に起きたこと。

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. 他の3つは別記事に分けました

worktree以外にも踏んだ落とし穴がありますが、性質が違うので分けます。

これらは「担当範囲をどう切るか」の話で、別記事で扱っています。 この記事はディレクトリの分離に絞ります。

補足: worktree を使うときの細かい注意

ブランチは1つの worktree からしか checkout できない

同じブランチを2つの worktree で開こうとすると git が止めます。これは安全機構なので、 回避しようとせず、1作業1ブランチを守ってください。

git worktree prune は自動では消さない

ディレクトリを手で 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 を使うほうが安全です。

まとめ

この運用を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 章を 足したものは製品ページにあります。

この運用そのものを配っています

4 人格版 Quartet は MIT で無料公開しています。UI 設計人格・レビュー基準・ Issue 単位の並列実行スクリプト・実践ガイド 10 章を足した Quintet は有料版です。

無料版を見る 製品ページ