Grandream

Grandream

公開: 8 min read

AIエージェントを並行で動かすなら jujutsu|git worktree と jj workspace の違い

AIエージェントを並行で動かすなら jujutsu|git worktree と jj workspace の違い
AIエージェントの開発をご検討中ですか? AIエージェント開発サービスを見る →

Claude Code や Codex のような AI コーディングエージェントに、ログイン画面の修正と検索機能の追加を同時に任せたい。そう考えたとき、最初にぶつかるのが「2体のエージェントにどこで作業させるか」という問題です。

この記事では、Git の worktree と jujutsu(jj)の workspace という2つの方法を、初心者にも分かるように比べます。結論を先に書くと、作業場所を分けること自体はどちらでもできます。差が出るのは、分けた後にエージェントの作業を見守り、まとめ、片づける段階です。

jj の基本(作業コピーがそのままコミットになる仕組みなど)は、前回の記事「jujutsu(jj)入門」で説明しています。コマンドの出力は jj 0.44.0 で確認しました。


同じフォルダで2体動かすと何が起きるか

まず、作業場所を分けずに、同じフォルダの同じブランチで2体のエージェントを起動した場合を考えます。

エージェントは作業の前後に git status や git diff で変更内容を確かめます。このとき、もう一方のエージェントが書き換えた差分まで目に入ります。エージェントは見えた差分を自分の作業の前提として読み込むので、その分だけ読む量(コンテキスト)が増えます。

さらに「この変更は自分が書いたものか」「残すべきか、戻すべきか」という、本来の作業と関係のない判断まで抱えることになります。判断が重くなるだけでなく、相手の書きかけを自分の誤りだと考えて消してしまうこともあります。

解決策は「作業机を分ける」こと

これを防ぐには、エージェントごとに作業場所を分けます。1つのリポジトリから作業用のフォルダを複数出して、エージェントを1体ずつ別のフォルダで動かすイメージです。各エージェントには自分の差分だけが見えるようになります。

Git では git worktree、jj では jj workspace という機能でこれができます。どちらも、リポジトリの中身(履歴)は1つのまま、作業用のフォルダだけを増やします。

違いを作業机にたとえると、次のようになります。

  • Git の worktree は、机を1つ増やすたびに名札(ブランチ)を用意する必要があります。しかも同じ名札は1つの机にしか置けません。
  • jj の workspace は、机を出せばすぐに使えます。名札は、作業を提出する(push する)ときに付ければ十分です。

この「名札がいるかどうか」が、ここから説明する違いの出発点になります。

jj の workspace が有利になる5つの点

1. エージェントごとにブランチを作らなくていい

Git の worktree は、ブランチをチェックアウトして作ります。同じブランチを2つの worktree で開こうとすると、次のように拒否されます。

$ git worktree add ../wtB main
fatal: 'main' is already used by worktree at '/path/to/wtA'

そのため、エージェントを1体起動するたびに新しいブランチを作り、名前を考える必要があります。

jj の workspace は、ブランチを必要としません。同じ起点から、いくつでも作れます。

$ jj workspace add --name agent1 -r main ../myapp-agent1
$ jj workspace add --name agent2 -r main ../myapp-agent2

これで main を起点にした作業フォルダが2つできます。それぞれのフォルダでエージェントを起動すれば準備は終わりです。

2. 全エージェントの途中経過を1画面で見られる

人間の役割は、エージェントが今何をしているかを見守ることです。

Git の worktree では、エージェントの書きかけはそれぞれの worktree の中にしかありません。様子を見るには、フォルダを1つずつ回って git diff を打つことになります。

jj では、各 workspace の書きかけも自動でコミットとして記録され、1つのリポジトリに入ります。元のフォルダで jj log を打つだけで、全エージェントの作業が1つの図に並びます。

$ jj log
@  qxzstuyt ... default@ 1e135030
│  (empty) (no description set)
│ ○  xpskwzov ... agent2@ 3a75be35
├─╯  agent2: search
│ ○  yympklro ... agent1@ 0fa51fb3
├─╯  agent1: login
○  zrsqrxxx ... main c0a135c0
│  init

agent1@ や agent2@ の印が、どのエージェントの作業かを示しています。エージェント同士は互いの差分を見ずに済み、人間だけが全体を見渡せるという分担になります。

なお、記録されるのは、その workspace で jj のコマンドが実行された時点の状態です。エージェントがファイルを書き換えただけで jj のコマンドをまだ打っていなければ、図には反映されません。

3. エージェントの書きかけが消えにくい

エージェントの事故で一番困るのは、書きかけのコードが消えることです。Git では、コミットする前の変更は Git の記録に入っていません。エージェントがコミットし忘れたまま git checkout . や git reset --hard を実行すると、その変更は取り戻せません。

jj では、jj のコマンドが実行されるたびに書きかけの状態が記録され、その履歴を jj evolog で見られます。

$ jj evolog
@  yympklro ... agent1@ 0fa51fb3
│  agent1: login
○  yympklro/1 ... c4c9da9a (hidden)
│  -- operation 94ba4f09899c snapshot working copy
○  yympklro/2 ... 9747c7db (hidden)
│  -- operation 4c18800610bf snapshot working copy

snapshot working copy の行が、自動で記録された途中の版です。「さっきの版のほうがよかった」というときも、この中から選んで戻せます。また、jj に対する操作はすべて jj op log に残り、jj undo で取り消せます。どの workspace で行った操作かも記録されるので、どのエージェントが何をしたかを後から追えます。

4. main が進んでも作業が止まらない

エージェントが作業している間に、main に別の修正が入ることはよくあります。エージェントの作業を新しい main の上に載せ直す(リベースする)とき、同じ箇所を書き換えていればコンフリクトが起きます。

Git では、コンフリクトするとリベースが途中で止まります。解消して git rebase --continue を打つまで、その worktree は中途半端な状態のままです。エージェントはこの状態の扱いが苦手で、立ち往生したり、誤った解消をしたりしがちです。

jj では、リベースは最後まで終わり、コンフリクトしたコミットに印が付くだけです。

$ jj rebase -s agent1@ -d main
Rebased 1 commits to destination.
New conflicts appeared in 1 commits:
  yympklro 451227cd (conflict) agent1: login

他のエージェントの作業はそのまま進められます。コンフリクトの解消は、人間かエージェントが後で落ち着いて行えばよく、作業全体が止まりません。

5. 後片づけが1回で済む

作業が終わったら、作業フォルダを片づけます。Git では worktree の削除とブランチの削除を別々に行う必要があり、ブランチの消し忘れが溜まりがちです。

jj では、workspace の登録を外すだけで済みます。ブランチは最初から作っていないので、残りません。

$ jj workspace forget agent2

フォルダ自体は残るので、不要なら削除します。

チーム全体で導入しなくていい

jj は、保存先として Git のリポジトリをそのまま使います。jj で作ったコミットは Git のコミットで、GitHub への push も普通の Git のブランチとして届きます。

そのため、jj はチーム全体で一斉に導入する必要がありません。エージェントを並行で動かしたい人だけが、手元のリポジトリで jj git init --colocate を実行すれば使い始められます。他のメンバーは今までどおり Git を使い続けられ、誰が jj を使っているかも区別されません。新しいツールを試すときに、チームの合意を取らなくてよいのは大きな利点です。

その代わり、エージェントに「どちらを使うか」を教える

注意が必要なのは、jj で追加した workspace には .git フォルダが無いことです。その中で git コマンドを実行すると失敗します。

$ git status
fatal: not a git repository (or any of the parent directories): .git

Claude Code や Codex は、何も指示しなければ git コマンドで状態を確かめようとします。一方で、チームには Git だけを使う人もいるので、共有の設定ファイルに「jj を使う」と書くわけにもいきません。

そこで、次の2段構えにします。

  1. 自分だけの設定ファイルに「jj を使う」と記録する。 Claude Code なら、Git の管理から外した CLAUDE.local.md が使えます。
  2. 共有の CLAUDE.md(Codex なら AGENTS.md)に、どちらを使うか判断するルールを書く。

自分だけの設定ファイルには、次のように書きます。

# CLAUDE.local.md(Git の管理外)
- このマシンでは バージョン管理に jj を使う

共有の CLAUDE.md には、判断のルールを書きます。

## バージョン管理コマンドの選び方
- `jj root` が成功し、かつ CLAUDE.local.md に「jj を使う」とある場合は jj を使う
  - 状態の確認は `jj st`、差分は `jj diff`、履歴は `jj log`
  - 作業の区切りでは `jj describe -m` と `jj new` を使う
- それ以外は git を使う

こうしておけば、jj を使う人の手元ではエージェントが jj で作業し、Git だけを使う人の手元では今までどおり Git で作業します。共有の設定ファイルは1つのままで、使う人ごとに動きが切り替わります。

弱点: Git のフックは動かない

jj には、はっきりした弱点が1つあります。Git のフック(pre-commit や pre-push などの、コミットや push の前に自動で走る検査)が、jj のコマンドでは実行されないことです。

チームで「push の前に型チェックとテストが自動で走る」ようにフックを設定していても、jj git push で push するとその検査は走りません。エージェントが検査を通っていないコードを push してしまうおそれがあります。

対策としては、push の前に検査用のコマンドを実行するよう、先ほどの CLAUDE.md のルールに書き足しておくのが手軽です。

  - push の前に必ず `pnpm test` と `pnpm typecheck` を実行し、通ったことを確かめる

フックは検査を強制できますが、この方法はエージェントが指示に従うことを前提にしています。検査を確実に通したい場合は、GitHub 側の CI で同じ検査を回すなど、別の手段も併用してください。

まとめ

AI エージェントを並行で動かすとき、作業場所を分ける必要があるのは Git でも jj でも同じです。違いは分けた後に表れます。

  • ブランチを作らずに作業フォルダを出せる
  • 全エージェントの途中経過を jj log で1画面に見られる
  • 書きかけが自動で記録され、消えにくい
  • main が進んでコンフリクトしても、作業が止まらない
  • 片づけが1回で済む

jj は Git のリポジトリをそのまま使うので、自分の手元だけで試せます。導入するときは、自分だけの設定ファイルと共有の CLAUDE.md の2段構えで、エージェントに jj と Git を使い分けさせてください。Git のフックが動かない点だけは、検査の実行を明示的に指示して補います。

エージェント1体で作業するなら、Git のままでも困ることは少ないでしょう。2体、3体と並べて見守るようになったら、jj の workspace を試してみる価値があります。

関連記事

Grandream

Grandream

株式会社グランドリーム

AI・システム開発のプロフェッショナルチームです。AIエージェント・業務自動化・Webシステム開発などを手がけています。

AIエージェント開発のご相談はお気軽に

PoC段階から本番運用まで一貫対応します。

AI開発について相談する

AIエージェント開発サービスの詳細を見る →

どのサービスが合うか30秒で診断する →