Dev

Codex CLI managed Git worktrees: three ways to start one, and the model's tool starts from the remote default branch

A hands-on run of Codex CLI 0.162.1 on a throwaway repository: where the checkouts land, which commit each entry point begins at, why your .env and uncommitted edits stay behind, how to merge the work back, and what cleanup leaves on disk.

Codex CLI 0.162.0, released on October 8, has one line about worktrees in its release notes: "Add tools for creating and listing managed Git worktrees from trusted local projects when the worktrees feature is enabled." That line describes one of three ways to get a managed worktree in the CLI, and it is the odd one out: the tool starts from the remote default branch, while the flag and the TUI picker start from the commit you are on.

I ran all three on a throwaway repository with codex-cli 0.162.1 and git 2.43.0 on Linux. Everything below is from that run unless I say it comes from the source code. Paths are shortened to ~/lab/shop.

What is a managed Git worktree in Codex CLI?

It is an ordinary linked Git worktree that Codex creates for you in its own directory and ties to one conversation. A Git worktree is a second checkout of the same repository: its own files, its own HEAD and index, the same object store and the same branches. Two agents that each stay in their own worktree edit separate files.

"Managed" adds three things. Codex picks the location, by default ~/.codex/worktrees. It writes the owning conversation's id into the worktree's Git metadata, in a file called codex-thread.json. And it gives you a browser that lists the checkouts of a repository by the conversation that owns each one.

There may be no enable step. On my build the feature is already on:

text
$ codex features list | grep worktrees
worktrees                                stable             true

With it off, the flag refuses to run and tells you the fix:

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

How do you start a Codex CLI session in a new Git worktree?

There are three entry points: a command-line flag, a picker inside the TUI, and a tool the model calls.

The flag. codex and codex exec accept --worktree, described in --help as "Run the session in a new managed Git worktree". So does codex fork, though the source shows it wants an explicit session id. codex resume --help lists the flag too, but codex resume --worktree refuses and ends with "--worktree supports new interactive sessions, codex fork, codex exec, and codex exec fork". The release notes for 0.162 do not mention the flag, and I have not checked which release added it.

My test repository was on a local branch with one unpushed commit and an uncommitted edit. It also had a .env and a node_modules directory, both ignored. The output here is trimmed to the lines that matter:

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

The session ran in ~/.codex/worktrees/0867/shop: a four-character bucket, then the repository's directory name. A second run, launched from the api/ subdirectory, opened in …/551a/shop/api: the relative position carries over.

The picker. In the TUI, /new and /fork ask where the conversation should run. The wording below is from pull request #50416, which relabelled the two options:

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

Option 2 printed "Working directory changed to: /home/me/.codex/worktrees/a861/shop" and the session continued there. The same picker code sits behind /worktree, "start or continue a conversation in a new worktree", which offers to continue the current conversation in a new checkout, start a fresh one there, or browse existing worktrees. I count it with the picker. Continuing forks the conversation into the new checkout with its history, which is worth a second thought if the reason for the new checkout was a clean start: a fork inherits the abandoned guesses along with the facts.

The tools. These are what the release note announces, from pull request #50148. In the TUI, in a trusted local project, the model gets create_worktree, get_worktree_creation_status and list_worktrees. Creation is asynchronous, so the model starts it and polls (ids shortened):

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."}

Unlike the flag and the picker, the tool does not move the session. I asked for pwd afterwards and it was still the original checkout. The model gets a path and has to use it explicitly, and the tool's own message says to "request filesystem permissions if needed": creating the checkout does not make it writable for the session. list_worktrees is also narrower than it sounds. It returns the worktrees attached to the current conversation, so in a repository that already had several managed worktrees, a new conversation got an empty list.

The tools exist only in the TUI. I asked a codex exec session whether it had them, and it reported none. From a script, the flag is the only route.

Which commit does a Codex CLI worktree start from?

It depends on the entry point. The flag and the picker start from the current commit of the checkout you launch them in. The tool starts from the repository's remote default branch.

My repository was on feature/discount, ahead of origin/main:

Entry pointWorktree HEADThat commit is
codex exec --worktree42b4cf1my current commit, unpushed
/new, "Create new Git worktree"9a95de4my current commit at that point, a few commits later
create_worktree, no ref84e5a0aorigin/main

The tool's own description says so: "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." An agent that decides mid-task to open a worktree and omits ref gets a checkout without the branch you were working on. If you ask for one in prose, name the ref.

From the source, not reproduced: the default is resolved from refs already in the repository, without fetching. It prefers a remote's HEAD, origin first, then origin/main, origin/master, local main, local master. A repository with none of those refs, say no remote and a single branch called trunk, ends at the error "Could not determine the project's default branch." A stale origin/main means a stale starting point. Also from the source: codex fork with the flag starts from the current commit of the directory the forked conversation last worked in, which need not be the directory you typed the command in.

All three produce a detached HEAD. Nothing creates a branch for you.

What does a Codex CLI worktree leave behind in your checkout?

Everything Git does not track, and everything you have not committed. The new checkout had the tracked files and nothing else:

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

No .env, no node_modules, and my uncommitted edit to web/index.html was not there either. It stayed in the source checkout, untouched. An agent sent to "run the tests" in that worktree starts by discovering that there are no dependencies. The source adds three more gaps I did not test: the checkout is populated with Git hooks and filters disabled, with Git LFS smudging skipped, and without recursing into submodules.

This is where the CLI and the desktop app differ. OpenAI's worktree documentation describes the app only. It says the app applies uncommitted changes to the new worktree and that "Codex only copies ignored files that match .worktreeinclude". I committed a .worktreeinclude listing .env and ran the flag again. The worktree got the .worktreeinclude file and no .env. On 0.162.1 the CLI path does not read it.

The app's cleanup does not carry over either. The same page says that by default the app keeps "your most recent 15 Codex-managed worktrees" and deletes older ones. The CLI shares the app's directory, and the source comment on its settings reads "CLI allocation uses the same root without enabling automatic cleanup." Nothing I created was removed for me.

Can two Codex CLI agents work in parallel on one repository with Git worktrees?

Yes for the files. The rest is still yours to arrange. I started two codex exec --worktree -s workspace-write sessions at once, one editing api/cart.py, the other web/index.html. Each got its own checkout and neither saw the other's edit.

Neither could commit:

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

A linked worktree keeps its index inside the source repository's .git, outside the worktree, and I first took that for the whole cause. A control run in a plain clone, same sandbox, failed the same way on its own .git/index.lock, so the worktree is not the reason. OpenAI's sandbox documentation says a writable workspace's .git "is protected as read-only whether it appears as a directory or file", and that when it is a gitdir: pointer, as in a linked worktree, "the resolved Git directory path is also protected". With the approval policy at never, as in these runs, the agent cannot ask for an exception, and adding the source .git with --add-dir changed nothing. So in this configuration the agents edit and you commit. Under another approval policy the agent can ask; I did not test that.

What a worktree shares is the repository itself. Branches are common to all checkouts, and Git allows a branch in only one of them at a time. Once I had given one worktree a branch name (the next section shows how), the source checkout could no longer switch to it:

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

Ports, a local database, caches outside the tree and anything else keyed to the machine are untouched by any of this. A worktree isolates files. It does not coordinate, and it does not know which agent owns which task. In an earlier post I argued that a multi-agent system is a distributed system and that ownership of a unit of work has to live outside the agents. A second checkout does not change that. It removes exactly one failure, two writers in the same directory, and leaves the rest where they were.

The same holds when the second agent is a different tool: Claude Code and Codex on one repository pass artifacts between them, a diff or a plan file, and never a live conversation.

How do you get the work back from a Codex CLI worktree, and remove it?

Give the detached HEAD a branch name before anything else, then merge from the source checkout. The CLI has no equivalent of the app's Hand off button.

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

I skipped the branch name once to see what happens. I committed on a detached HEAD in one worktree and removed it. Git removed it without complaint, because the tree was clean, and no branch contained the commit afterwards. git fsck --lost-found still listed it as dangling, so it is recoverable until garbage collection, but nothing will remind you.

A worktree with modified or untracked files is safer: git worktree remove refuses with "contains modified or untracked files, use --force to delete it". Ignored files get no such protection. I put a .env into an otherwise clean worktree and removed it, and Git deleted the directory, .env included, without a word. Anything an agent generated into an ignored path goes the same way.

Two leftovers to know about. git worktree remove deletes the checkout and leaves its four-character bucket directory behind, empty. After ten worktrees I had ten empty directories under ~/.codex/worktrees. And two runs that I killed before their first turn left checkouts the browser lists as "No attached thread". They are ordinary worktrees, and git worktree list in the source repository shows them all.

When is a hand-made Git worktree still the better choice than a Codex CLI one?

When you already know the branch. git worktree add -b fix/login ../shop-login origin/main gives you a named branch, a starting commit you chose and a directory you chose, in one command. The detached HEAD, the question of which commit it started from, and the bucket directory do not come up. You can start codex in that directory and the picker's first option, "Use current Git worktree", keeps it there.

The managed kind pays off when the checkout is disposable and the conversation is the thing you want to find again. The browser under /worktree lists each checkout by the title of the conversation that owns it and offers to resume that conversation. A directory you made by hand has no such link.

Before starting a second agent on the same repository, I would check three things. Commit or stash first, because the new checkout starts from a commit and will not see your working tree. Say which ref you want whenever the model creates the worktree. And put a branch name on the result before you remove anything.

Discussion

No comment section here — all discussions happen on X.

Max Nardit

Max Nardit

@mnardit

More articles