AI コーディングエージェントで Git Worktree を使う
AIコーディングエージェントでgit worktreesを使い、ブランチを分離し、ファイル衝突を防ぎ、ポートやDB、マージを安全に管理します。
1 つのリポジトリで複数の AI コーディングエージェントを動かす確実な方法は、1 タスク・1 ブランチ・1 worktree・1 エージェントです。各エージェントは git worktree add で作成した専用ディレクトリを持ち、専用ブランチ上で作業し、他のエージェントが編集中のファイルには一切触れません。
多くの開発者は、この方法に苦い経験を経てたどり着きます。1 つのチェックアウトで 2 つのエージェントを動かすと、互いの書きかけの編集を静かに上書きし合ったり、すでに別の場所でチェックアウト済みのブランチをチェックアウトしようとして片方のエージェントが動けなくなったりします。本記事では、エージェントのワークフローに特化して、このパターンが何を分離し、何を意図的に分離しないのか、そして 3 つのエージェントが同時に完了したときに何をすべきかを解説します。
要点
- worktree ごとに 1 エージェントを動かす。
git worktree add ../task-a -b agent/task-a mainで、各エージェントに独立したディレクトリとブランチを与える。 - worktree が分離するのはファイルであって、ランタイムではない。ポート、データベース、Docker ボリュームはマシンに属するため、worktree ごとに専用のポートとデータベースを割り当てる。
- 新しい worktree には追跡対象ファイルしか含まれない。エージェントを起動する前に
npm ciを実行し、.envをコピーすること。 - 1 つのブランチは 1 つの worktree でしかチェックアウトできないため、エージェントには
git checkoutやgit switchを決して実行しないよう指示する。 - 完了したブランチは 1 つずつマージし、マージ前に更新済みの
mainへそれぞれリベースする。
AI エージェントのための Git Worktree パターン
「1 worktree に 1 エージェント」のパターンは、タスクごとに 2 つのコマンドで済みます。main を基点とした新しいブランチ付きで worktree を作成し、その中でエージェントを起動します。
git worktree add ../task-a -b agent/task-a main
git worktree add ../task-b -b agent/task-b main
一方のエージェントを ../task-a に、もう一方を ../task-b に向けます。それぞれが独自の作業ディレクトリ、独自のインデックス、独自の HEAD を持ちますが、すべての worktree は同じオブジェクトデータベースと refs を共有するため、一方で作成したコミットは他方からすぐに参照できます。worktree はエージェントではなくタスクに紐付けましょう。作業開始時に作成し、ブランチがマージされたら削除します。Anthropic も Claude Code の worktrees ガイドで同じ手動手順を文書化しており、Cursor は Agents Window から分離された worktree でエージェントを実行します。Codex もディレクトリを指定すれば同様に動作します。
なぜ 1 つのディレクトリを共有すると失敗するのか
1 つのディレクトリで 2 つのエージェントを動かすと、失敗は静かに起こります。一方が他方のコミット前の編集を上書きしても、git もエージェントもエラーを出さないため、被害が表面化するのはレビュー時になってからです。git の競合検出はコミット同士を比較するものであり、同一ワーキングツリーへの同時書き込みはそこまで到達しません。
2 つ目の失敗はより目立ちますが、より厄介です。共有ディレクトリでの並行 git 操作は .git/index.lock で衝突し、ロックを保持したままエージェントがクラッシュすると、残された lock ファイルが他のすべてのエージェントの git コマンドを、誰かが手動で削除するまでブロックします。リトライするエージェントもあれば、コミットを諦めて、際限なく膨らむ未コミット状態の上にコードを生成し続けるエージェントもあります。worktree を分ければ、各 worktree が共有インデックスではなく自身のインデックスに対してステージングするため、どちらの問題も解消します。
新しい worktree に含まれないもの
新しい worktree には追跡対象ファイルしか含まれません。したがって node_modules、.env、ビルドキャッシュ、その他 gitignore されているものは、自分で用意するまで存在しません。何も用意されていない worktree で起動したエージェントは、最初のテスト実行で失敗するか、さらに悪ければ「気を利かせて」設定を書き換えて辻褄を合わせようとします。エージェントを起動する前に、すべての worktree をブートストラップしましょう。
cd ../task-a
npm ci
cp ../main-repo/.env .env
ここでは npm ci が適切なインストール方法です。クリーンな環境向けに設計されており、ロックファイルが指定する内容を正確にインストールします。エージェントが Claude Code の場合、.worktreeinclude ファイルを使えば .env のような gitignore されたファイルを Claude Code 自身が作成する worktree に持ち込めますが、git worktree add で手動作成した worktree には適用されません。そのため、手動コピーはどのツールでも通用する方法です。
worktree が分離しないもの
worktree が分離するのはファイルであって、ランタイムではありません。ポート、データベース、Docker ボリューム、共有キャッシュはマシンに属するため、2 つの worktree から起動した 2 つの開発サーバーは、依然としてポート 3000 を奪い合います。ホスト名やポートでアドレス指定されるものにとって、ディレクトリの境界には何の意味もありません。
| worktree ごとに分離されるもの | マシン全体で共有されるもの |
|---|---|
| 作業ファイル、未コミットの編集 | TCP ポート |
| インデックス(ステージングエリア) | データベース |
チェックアウト中のブランチ / HEAD | Docker ボリュームとデーモン |
| ディレクトリ内のビルド成果物 | グローバルパッケージキャッシュ |
マイグレーションを実行するエージェントを起動する前に、worktree ごとに専用のポートと専用のデータベースを与えてください。1 つの worktree から行ったスキーマ変更は、他の worktree が共有しているデータベースにも反映されてしまうからです。コピーした .env に両方を設定します。
# ../task-a/.env
PORT=3001
DATABASE_URL=postgres://localhost:5432/app_agent_task_a
データベース名をブランチ名にちなんで付けておくと、後で不要になったものを見つけて削除するのが容易になります。コンテナでもランタイム層を分離できますが、そのトレードオフは別記事に値する話題です。
エージェントに worktree 内にいることをどう伝えるか
1 つのブランチは一度に 1 つの worktree でしかチェックアウトできないため、同期しようと git checkout main を実行したエージェントはエラーになり、そのまま停止してしまうことがよくあります。git checkout のマニュアルはこの挙動を規定しており、上書きする手段として --ignore-other-worktrees を挙げています。この事実をエージェントに実行時に発見させてはいけません。エージェントが読み込む指示ファイル(AGENTS.md、CLAUDE.md など)に記載しておきましょう。
You are working inside a git worktree.
Stay on the current branch. Never run `git checkout` or `git switch`.
To sync with main, run `git fetch origin` and `git rebase origin/main`.
エージェントが特定のコミットを読み取ったりビルドしたりするだけでよい場合は、ブランチを完全に省略できます。git worktree add --detach は HEAD が detached な worktree を作成するため、排他ルールを完全に回避できます。
git worktree add --detach ../review-b1a2c3 b1a2c3
戻ってきた成果物をどうマージするか
worktree はマージ競合をなくすわけではありません。競合を、静かなランタイム上書きから、マージ時に可視化される競合へと移動させ、そこでは通常の git ツールで対処できるようにします。複数のエージェントが同時に完了したら、直列に統合しましょう。1 つのブランチをマージし、次のブランチを更新済みの main にリベースし、マージし、これを繰り返します。
cd ../main-repo
git merge agent/task-a
cd ../task-b
git rebase main
cd ../main-repo
git merge agent/task-b
各リベースは、すでにマージ済みのすべての内容に対する競合を、1 ブランチずつ表面化させます。3 つの変更が絡み合った予期せぬ事態が積み重なることはありません。この順序付けのルールは、マージよりも上流に位置します。同じファイルに触れるタスクや、一方が他方の出力を利用するタスクは、並列化せず順序付けます。もともと独立していないタスク分解を、worktree の構成で修正することはできません。
エージェントは何体まで、そしていつ片付けるか
現実的な上限は git やディスクではなく、あなたのレビュー帯域です。並列に動く各エージェントは、読んで、テストして、マージしなければならないブランチを生み出すからです。incident.io のエンジニアリングチームのようにこのワークフローについて公開しているチームは、4〜5 体のエージェントを同時に動かしていると述べていますが、ほとんどの開発者はもっと少ない数で十分だと感じるはずです。ブランチをマージしたら git worktree remove ../task-a を実行します。ブートストラップで追跡対象外のファイルを追加しているため、--force が必要になると考えてください。git worktree remove は、クリーンでない worktree に対してこのオプションを要求します。ディレクトリを rm -rf で削除してしまった場合は、git worktree prune で git に残ったままの古いメタデータを一掃できます。
まずは 2 体のエージェントから
1 タスク・1 ブランチ・1 worktree・1 エージェント。この原則により、並列エージェントは静かなデータ破壊の温床から、レビュー可能なブランチの集合へと変わります。まずは 2 体から始めましょう。worktree を作成し、それぞれを npm ci とタスクごとの .env でブートストラップし、ブランチから離れないよう指示を追加し、実際にレビューできる規模を超えて拡大する前に「リベース → マージ」の手順を練習してください。
FAQ
AI エージェントを並列で動かすには、git worktree と個別クローンのどちらを使うべきですか?
通常は worktree の方が優れています。1 つのオブジェクトデータベースを共有するため、ある worktree で作成したコミットは push や fetch なしに他の worktree からすぐ参照でき、履歴もディスク上に 1 度しか保存されません。個別クローンは履歴全体を複製し、コミットをやり取りするには push と fetch が必要です。クローンが有利なのは、サブモジュールを含むリポジトリのように完全な分離が必要な場合だけです。
git worktree はサブモジュールを使うリポジトリでも動作しますか?
部分的にしか動作しません。git 自身の worktree マニュアルは、サブモジュールのサポートを BUGS の項に記載し、複数チェックアウトを実験的機能と呼び、スーパープロジェクトを複数の場所で同時にチェックアウトしないよう勧告しています。また、サブモジュールを含む worktree は git worktree move で移動できません。サブモジュールを含むリポジトリでエージェントを並列実行する場合は、履歴が重複し、チェックアウト間でコミットを共有するには push が必要になるとしても、個別のフルクローンの方が安全な分離手段です。
すべての git worktree は同じ git 設定を共有しますか?
はい。明示的にオプトアウトしない限り、すべての worktree が同じリポジトリ設定を読み込みます。特定の worktree に独自の設定を与えるには、git config extensions.worktreeConfig true を実行し、git config --worktree で値を書き込みます。これにより、その worktree 独自の config.worktree ファイルに保存されます。トレードオフとして、この拡張を有効にすると、古い Git リリースではリポジトリを開けなくなります。
worktree を削除すると、エージェントのブランチも削除されますか?
いいえ。ブランチはリポジトリ全体で共有される ref なので、git worktree remove は作業ディレクトリとそのメタデータを削除しますが、ブランチとその上のすべてのコミットはそのまま残ります。失われるのはそのディレクトリ内の未コミットの変更だけであり、だからこそ remove は --force を渡さない限りクリーンでない worktree を拒否します。ブランチはマージ後に git branch -d で別途削除してください。