Dev

Codex CLI のマネージド Git worktree: 始め方は 3 通り、モデルが呼ぶツールだけはリモートのデフォルトブランチから始まります

Codex CLI 0.162.1 を使い捨てのリポジトリで実際に動かした記録です。チェックアウトがどこに作られるか、入口ごとにどのコミットから始まるか、.env と未コミットの編集がなぜ元の場所に残るか、作業をどうマージして戻すか、後片付けでディスクに何が残るかを確かめました。

10 月 8 日に公開された Codex CLI 0.162.0 のリリースノートには、worktree についての記述が 1 行だけあります。"Add tools for creating and listing managed Git worktrees from trusted local projects when the worktrees feature is enabled." (worktrees 機能が有効なとき、信頼済みのローカルプロジェクトからマネージド Git worktree を作成、一覧表示するツールを追加)。この 1 行が説明しているのは、CLI でマネージド worktree を手に入れる 3 つの方法のうちの 1 つで、しかも他の 2 つと挙動が違うものです。ツールはリモートのデフォルトブランチから始まり、フラグと TUI の選択画面は、いまいるコミットから始まります。

3 つすべてを、使い捨てのリポジトリで試しました。環境は Linux 上の codex-cli 0.162.1 と git 2.43.0 です。以下の内容は、ソースコードからと断っている箇所を除き、すべてこの実行結果です。パスは ~/lab/shop に短縮しています。

Codex CLI のマネージド Git worktree とは何ですか?

Codex が専用のディレクトリに作り、1 つの会話にひも付ける、ごく普通のリンクされた Git worktree です。Git worktree は同じリポジトリの 2 つ目のチェックアウトです。ファイル、HEAD、インデックスは別々で、オブジェクトストアとブランチは共通です。2 つのエージェントがそれぞれ自分の worktree から出なければ、編集するファイルは別々になります。

「マネージド」で加わるのは 3 点です。場所は Codex が決め、既定では ~/.codex/worktrees になります。worktree の Git メタデータの中に codex-thread.json というファイルを作り、所有する会話の ID を書き込みます。そして、リポジトリのチェックアウトを、所有する会話ごとに一覧表示できる worktree ブラウザーが付きます。

有効化の手順は不要かもしれません。手元のビルドでは最初からオンでした。

text
$ codex features list | grep worktrees
worktrees                                stable             true

オフにすると、フラグは実行を拒否し、対処法を示します。

text
$ codex exec --worktree --disable worktrees "pwd"
Error: --worktree requires the worktrees feature; enable it with --enable worktrees

Codex CLI のセッションを新しい Git worktree で始めるには?

入口は 3 つあります。コマンドラインのフラグ、TUI 内の選択画面、モデルが呼び出すツールです。

フラグ。 codex と codex exec は --worktree を受け付けます。--help での説明は "Run the session in a new managed Git worktree" です。codex fork も受け付けますが、ソースを見るかぎり、明示的なセッション ID が必要です。codex resume --help にもこのフラグは載っていますが、codex resume --worktree は拒否され、"--worktree supports new interactive sessions, codex fork, codex exec, and codex exec fork" というメッセージで終わります。0.162 のリリースノートはこのフラグに触れておらず、どのリリースで追加されたかは確認していません。

テスト用リポジトリは、プッシュしていないコミットが 1 つあるローカルブランチ上にあり、未コミットの編集も 1 つありました。.env と node_modules ディレクトリもあり、どちらも ignore 対象です。出力は要点の行だけに絞っています。

text
$ cd ~/lab/shop && codex exec --worktree -s read-only "Run: pwd ; git status --short --branch ; git log --oneline -3"
workdir: /home/me/.codex/worktrees/0867/shop
## HEAD (no branch)
42b4cf1 add discount (local branch, not pushed)
84e5a0a initial

セッションは ~/.codex/worktrees/0867/shop で動きました。4 文字のバケット、その下にリポジトリのディレクトリ名という構成です。サブディレクトリ api/ から起動した 2 回目の実行は …/551a/shop/api で開きました。相対位置は引き継がれます。

選択画面。 TUI では、/new と /fork が会話をどこで動かすかを尋ねます。下の文言は、2 つの選択肢の名前を変更したプルリクエスト #50416 のものです。

text
  Where should the new conversation run?
› 1. Use current Git worktree  Keep using the current working directory
  2. Create new Git worktree   Create a separate checkout of this repository in another directory

選択肢 2 を選ぶと "Working directory changed to: /home/me/.codex/worktrees/a861/shop" と表示され、セッションはそこで続きました。同じ選択画面のコードが /worktree ("start or continue a conversation in a new worktree") の裏側にもあります。こちらは、現在の会話を新しいチェックアウトで続ける、そこで新しい会話を始める、既存の worktree を一覧表示する、の 3 つから選べます。ここでは選択画面の一部として数えます。「続ける」を選ぶと、会話は履歴ごと新しいチェックアウトにフォークされます。新しいチェックアウトを作る理由がまっさらな状態からのやり直しだった場合は、一度立ち止まって考える価値があります。フォークは事実と一緒に、捨てたはずの推測まで引き継ぐからです。

ツール。 リリースノートが告知しているのはこれで、プルリクエスト #50148 で入りました。TUI で、信頼済みのローカルプロジェクトにいるとき、モデルは create_worktree、get_worktree_creation_status、list_worktrees を使えます。作成は非同期なので、モデルは作成を開始してから状態をポーリングします (ID は短縮しています)。

text
• Called codex_worktrees.create_worktree({"allowAsync":true})
  └ {"type": "pending", "operationId": "9a68342f-…", "message": "Creation is running. Poll status; do not repeat creation. Work on independent tasks while waiting."}
• Called codex_worktrees.get_worktree_creation_status({"operationId":"9a68342f-…"})
  └ {"type": "created", "worktree": {"root": "/home/me/.codex/worktrees/9e21/shop", …}, "message": "Keep the task cwd. Use the returned workspaceRoot explicitly and request filesystem permissions if needed."}

フラグや選択画面と違い、ツールはセッションを移動させません。作成後に pwd を頼むと、元のチェックアウトのままでした。モデルが受け取るのはパスで、それを明示的に使う必要があります。ツール自身のメッセージにも "request filesystem permissions if needed" (必要ならファイルシステムの権限を要求すること) とあります。チェックアウトを作っても、そのセッションから書き込めるようにはなりません。list_worktrees も、名前から想像するより範囲が狭いツールです。返すのは現在の会話にひも付いた worktree だけなので、すでにマネージド worktree がいくつもあるリポジトリでも、新しい会話には空のリストが返りました。

ツールは TUI にしかありません。codex exec のセッションにこれらのツールを持っているか尋ねたところ、ないという答えでした。スクリプトから使える手段はフラグだけです。

Codex CLI の worktree はどのコミットから始まりますか?

入口によって違います。フラグと選択画面は、起動したチェックアウトの現在のコミットから始まります。ツールは、リポジトリのリモートのデフォルトブランチから始まります。

リポジトリは feature/discount 上にあり、origin/main より先に進んでいました。

入口worktree の HEADそのコミットは
codex exec --worktree42b4cf1現在のコミット、未プッシュ
/new の "Create new Git worktree"9a95de4その時点の現在のコミット (最初の実行から数コミット後)
create_worktree、ref なし84e5a0aorigin/main

ツール自身の説明にこう書かれています。"Defaults to the repository's remote default branch, not the current branch; specify ref if the default cannot be determined or when continuing existing branch or PR work." (既定は現在のブランチではなく、リポジトリのリモートのデフォルトブランチ。既定を決められない場合や、既存のブランチや PR の作業を続ける場合は ref を指定すること)。作業の途中で worktree を開くと決めたエージェントが ref を省くと、作業中だったブランチを含まないチェックアウトができます。プロンプトの文章で worktree の作成を頼むなら、ref を明示してください。

以下はソースからで、再現はしていません。デフォルトは、リポジトリにすでにある ref から、fetch せずに決まります。優先されるのはリモートの HEAD (origin が先) で、次に origin/main、origin/master、ローカルの main、ローカルの master です。これらの ref が 1 つもないリポジトリ、たとえばリモートがなく trunk というブランチが 1 つだけの場合は、"Could not determine the project's default branch." というエラーで終わります。origin/main が古ければ、開始地点も古くなります。同じくソースからですが、フラグ付きの codex fork は、フォーク元の会話が最後に作業していたディレクトリの現在のコミットから始まります。それはコマンドを打ったディレクトリとは限りません。

3 つとも detached HEAD になります。ブランチを作ってくれるものはありません。

Codex CLI の worktree を作ると、元のチェックアウトに何が残されますか?

Git が追跡していないものすべてと、コミットしていないものすべてです。新しいチェックアウトにあったのは追跡対象のファイルだけでした。

text
$ ls -A ~/lab/shop
.env  .git  .gitignore  api  node_modules  web
$ ls -A ~/.codex/worktrees/0867/shop
.git  .gitignore  api  web

.env も node_modules もなく、web/index.html への未コミットの編集もありませんでした。編集は元のチェックアウトにそのまま残っています。この worktree で「テストを実行して」と頼まれたエージェントは、まず依存関係がないことに気づくところから始めます。ソースを読むと、試していない欠落がさらに 3 つあります。チェックアウトは Git のフックとフィルターを無効にした状態で展開され、Git LFS の smudge は省かれ、サブモジュールは再帰的に取得されません。

ここが CLI とデスクトップアプリの違いです。OpenAI の worktree ドキュメントが説明しているのはアプリだけです。そこには、アプリは未コミットの変更を新しい worktree に適用する、そして "Codex only copies ignored files that match .worktreeinclude" (Codex は .worktreeinclude に一致する ignore 対象ファイルだけをコピーする) と書かれています。.env を列挙した .worktreeinclude をコミットして、フラグをもう一度実行しました。worktree には .worktreeinclude ファイルが入り、.env は入りませんでした。0.162.1 の CLI の経路は、このファイルを読みません。

アプリの後片付けも CLI には当てはまりません。同じページによると、アプリは既定で "your most recent 15 Codex-managed worktrees" (直近 15 個の Codex 管理 worktree) を残し、古いものを削除します。CLI はアプリと同じディレクトリを使いますが、設定に付いたソースコメントは "CLI allocation uses the same root without enabling automatic cleanup." (CLI の割り当ては同じルートを使うが、自動クリーンアップは有効にしない) です。作ったものが勝手に削除されることは一度もありませんでした。

2 つの Codex CLI エージェントは、Git worktree を使って 1 つのリポジトリで並列に作業できますか?

ファイルについては、できます。それ以外は、これまでどおり自分で段取りする必要があります。codex exec --worktree -s workspace-write のセッションを 2 つ同時に起動し、一方に api/cart.py を、もう一方に web/index.html を編集させました。それぞれに専用のチェックアウトが作られ、どちらにも相手の編集は見えませんでした。

ただし、どちらもコミットできませんでした。

text
fatal: Unable to create '/home/me/lab/shop/.git/worktrees/shop6/index.lock': Read-only file system

リンクされた worktree は、インデックスを元のリポジトリの .git の中、つまり worktree の外に置きます。最初はそれが原因のすべてだと考えました。ところが、同じサンドボックスで普通のクローンを使った対照実験でも、コミットはクローン自身の .git/index.lock で同じように失敗しました。つまり worktree が理由ではありません。OpenAI のサンドボックスのドキュメントには、書き込み可能なワークスペースの .git は "is protected as read-only whether it appears as a directory or file" (ディレクトリでもファイルでも読み取り専用として保護される) とあり、リンクされた worktree のようにそれが gitdir: ポインターの場合は "the resolved Git directory path is also protected" (解決先の Git ディレクトリのパスも保護される) とあります。今回の実行のように承認ポリシーが never だと、エージェントは例外を申請できません。--add-dir で元の .git を追加しても変わりませんでした。したがってこの構成では、エージェントが編集し、コミットは人間が行います。別の承認ポリシーならエージェントが許可を申請できますが、そちらは試していません。

worktree が共有するのはリポジトリそのものです。ブランチはすべてのチェックアウトで共通で、Git は 1 つのブランチを同時に 1 つのチェックアウトでしか使わせません。ある worktree にブランチ名を付けたあと (付け方は次の節で示します)、元のチェックアウトはそのブランチに切り替えられなくなりました。

text
$ git switch agent/tax
fatal: 'agent/tax' is already used by worktree at '/home/me/.codex/worktrees/2f46/shop'

ポート、ローカルのデータベース、ツリーの外にあるキャッシュなど、マシンに結び付いたものは、worktree を使っても何も変わりません。worktree が分離するのはファイルです。調整はしませんし、どのエージェントがどのタスクを担当しているかも知りません。以前の記事で、マルチエージェントシステムは分散システムであり、作業単位の所有権はエージェントの外に置く必要があると書きました。チェックアウトを 1 つ増やしても、それは変わりません。なくなる障害は 1 つだけ、同じディレクトリに書き手が 2 人いるという状況で、残りは元のままです。

2 つ目のエージェントが別のツールであっても、事情は同じです。1 つのリポジトリで Claude Code と Codex を使う場合も、やり取りするのは diff や計画ファイルといった成果物で、進行中の会話を渡すことはありません。

Codex CLI の worktree から作業を戻し、worktree を削除するには?

何よりも先に detached HEAD にブランチ名を付け、それから元のチェックアウト側でマージします。CLI には、アプリの Hand off ボタンに相当するものがありません。

bash
git -C ~/.codex/worktrees/2f46/shop switch -c agent/tax
git -C ~/.codex/worktrees/2f46/shop add -A
git -C ~/.codex/worktrees/2f46/shop commit -m "add tax"
# your own edits committed or stashed first
git -C ~/lab/shop merge agent/tax
git -C ~/lab/shop worktree remove ~/.codex/worktrees/2f46/shop
git -C ~/lab/shop branch -d agent/tax

何が起きるか見るために、一度ブランチ名を付けずにやってみました。ある worktree の detached HEAD 上でコミットし、その worktree を削除しました。ツリーがクリーンだったので Git は何も言わずに削除し、そのコミットを含むブランチはどこにも残りませんでした。git fsck --lost-found にはまだ dangling として出るので、ガベージコレクションまでは復元できます。ただ、それを思い出させてくれるものは何もありません。

変更済みのファイルや未追跡のファイルがある worktree のほうが安全です。git worktree remove は "contains modified or untracked files, use --force to delete it" と言って拒否します。ignore 対象のファイルにはこの保護がありません。それ以外はクリーンな worktree に .env を置いて削除したところ、Git は .env ごとディレクトリを黙って消しました。エージェントが ignore 対象のパスに生成したものは、すべて同じ道をたどります。

知っておきたい残り物が 2 つあります。git worktree remove はチェックアウトを削除しますが、4 文字のバケットディレクトリは空のまま残します。worktree を 10 個作ったあと、~/.codex/worktrees の下には空のディレクトリが 10 個ありました。また、最初のターンの前に強制終了した 2 回の実行はチェックアウトを残し、worktree ブラウザーには "No attached thread" と表示されます。中身は普通の worktree で、元のリポジトリで git worktree list を実行すればすべて出てきます。

手作りの Git worktree のほうが Codex CLI の worktree より向いているのはどんなときですか?

ブランチがもう決まっているときです。git worktree add -b fix/login ../shop-login origin/main なら、名前の付いたブランチ、自分で選んだ開始コミット、自分で選んだディレクトリが、コマンド 1 つで手に入ります。detached HEAD も、どのコミットから始まったのかという疑問も、バケットディレクトリも出てきません。そのディレクトリで codex を起動すれば、選択画面の最初の選択肢 "Use current Git worktree" でそのまま使えます。

マネージドのほうが役に立つのは、チェックアウトが使い捨てで、あとで見つけ直したいのが会話のほうである場合です。/worktree の下のブラウザーは、各チェックアウトを所有する会話のタイトルで一覧表示し、そこからその会話を再開できます。手で作ったディレクトリには、そういうつながりがありません。

同じリポジトリで 2 つ目のエージェントを始める前に確認しておきたいことが 3 点あります。まず、コミットか stash をすることです。新しいチェックアウトはコミットから始まるので、作業ツリーの状態は見えません。モデルに worktree を作らせるときは、欲しい ref を必ず伝えることです。そして、何かを削除する前に、成果にブランチ名を付けることです。

ディスカッション

コメント欄はありません。議論は X で行っています。

Max Nardit

Max Nardit

@mnardit

ほかの記事

サプライチェーンとしての Claude Code スキル: 220 万件の採用、レジストリなし、スキャナーがソースを読む一方で Python はキャッシュを実行する

スキルは実行する前に読む、というのが定番の助言ですが、この助言には穴が 2 つあります。コピーしたスキルには更新の経路がないため、読んだコピーは知らないうちに元のスキルとずれていくことがあります。そして Python を同梱するスキルでは、読んだファイルとインタープリターが読み込むファイルが同じとは限りません。

Claude Code vs Codex: どちらかを選ばず、開発者が 2 つに仕事を振り分ける 7 つの方法

受け渡しの手段は、いまではベンダー自身が文書化しています。レビューコマンド、セッションの転送、双方向のインポーターです。一方、どの仕事をどちらに任せるべきかは、どこにも書かれていません。7 つの分担を出どころまでたどり、「文書化済み」か「報告」かを付けて紹介します。あわせて、既定値が異なるサンドボックスと、今年削除された連携も取り上げます。

コンテキストエンジニアリングは棚卸しから始まる: 一覧にできないコンテキストウィンドウは予算化できない

コンテキストエンジニアリングのガイドが勧める実践は、どれも「選ぶ」動詞です。削る、後回しにする、取りに行く、圧縮する。選ぶには一覧が要ります。Claude Code に付いてくる一覧は、設定を読み込み元とサイズつきで項目化する一方で、会話全体はひとつの数字として報告し、何がいつまで残るのかはどこにも書いていません。