Dev

Verwaltete Git-Worktrees in der Codex CLI: drei Wege, einen zu starten, und das Tool des Modells beginnt beim Standard-Branch des Remotes

Ein praktischer Durchlauf mit Codex CLI 0.162.1 in einem Wegwerf-Repository: wo die Checkouts landen, bei welchem Commit jeder Einstieg beginnt, warum deine .env und deine nicht committeten Änderungen zurückbleiben, wie du die Arbeit zurückholst und was das Aufräumen auf der Platte hinterlässt.

Codex CLI 0.162.0, erschienen am 8. Oktober, hat in den Release Notes eine einzige Zeile zu Worktrees: "Add tools for creating and listing managed Git worktrees from trusted local projects when the worktrees feature is enabled." Diese Zeile beschreibt einen von drei Wegen, in der CLI an einen verwalteten Worktree zu kommen, und zwar den, der aus der Reihe fällt: Das Tool beginnt beim Standard-Branch des Remotes, das Flag und die Auswahl in der TUI beginnen bei dem Commit, auf dem du gerade stehst.

Ich habe alle drei in einem Wegwerf-Repository ausprobiert, mit codex-cli 0.162.1 und git 2.43.0 unter Linux. Alles Folgende stammt aus diesem Durchlauf, es sei denn, ich sage dazu, dass es aus dem Quellcode kommt. Pfade sind auf ~/lab/shop gekürzt.

Was ist ein verwalteter Git-Worktree in der Codex CLI?

Ein gewöhnlicher verknüpfter Git-Worktree, den Codex für dich in einem eigenen Verzeichnis anlegt und an eine Konversation bindet. Ein Git-Worktree ist ein zweiter Checkout desselben Repositorys: eigene Dateien, eigener HEAD und eigener Index, derselbe Objektspeicher und dieselben Branches. Zwei Agenten, die jeweils in ihrem Worktree bleiben, bearbeiten getrennte Dateien.

Mit "verwaltet" kommen drei Dinge hinzu. Codex wählt den Ort, standardmäßig ~/.codex/worktrees. Es schreibt die ID der zugehörigen Konversation in die Git-Metadaten des Worktrees, in eine Datei namens codex-thread.json. Und es gibt dir einen Browser, der die Checkouts eines Repositorys auflistet, geordnet nach der Konversation, zu der sie gehören.

Einen Schritt zum Aktivieren gibt es womöglich nicht. In meinem Build ist das Feature schon an:

text
$ codex features list | grep worktrees
worktrees                                stable             true

Ist es aus, verweigert das Flag den Start und nennt die Abhilfe:

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

Wie startest du eine Codex-CLI-Session in einem neuen Git-Worktree?

Es gibt drei Einstiege: ein Kommandozeilen-Flag, eine Auswahl in der TUI und ein Tool, das vom Modell aufgerufen wird.

Das Flag. codex und codex exec akzeptieren --worktree, in --help beschrieben als "Run the session in a new managed Git worktree". codex fork auch, wobei der Quellcode zeigt, dass es eine explizite Session-ID verlangt. codex resume --help führt das Flag ebenfalls auf, aber codex resume --worktree bricht ab und endet mit "--worktree supports new interactive sessions, codex fork, codex exec, and codex exec fork". Die Release Notes zu 0.162 erwähnen das Flag nicht, und ich habe nicht geprüft, mit welchem Release es dazukam.

Mein Test-Repository stand auf einem lokalen Branch mit einem nicht gepushten Commit und einer nicht committeten Änderung. Außerdem hatte es eine .env und ein Verzeichnis node_modules, beide ignoriert. Die Ausgabe ist auf die Zeilen gekürzt, auf die es ankommt:

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

Die Session lief in ~/.codex/worktrees/0867/shop: ein Bucket aus vier Zeichen, dann der Verzeichnisname des Repositorys. Ein zweiter Lauf, gestartet aus dem Unterverzeichnis api/, öffnete sich in …/551a/shop/api: Die relative Position wird übernommen.

Die Auswahl. In der TUI fragen /new und /fork, wo die Konversation laufen soll. Der Wortlaut unten stammt aus Pull Request #50416, der die beiden Optionen umbenannt hat:

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 gab "Working directory changed to: /home/me/.codex/worktrees/a861/shop" aus, und die Session lief dort weiter. Derselbe Auswahl-Code steckt hinter /worktree, "start or continue a conversation in a new worktree". Der Befehl bietet an, die aktuelle Konversation in einem neuen Checkout fortzusetzen, dort eine frische zu beginnen oder vorhandene Worktrees zu durchsuchen. Ich zähle ihn zur Auswahl. Fortsetzen forkt die Konversation samt Verlauf in den neuen Checkout. Das ist eine Überlegung wert, wenn der Grund für den neuen Checkout ein sauberer Anfang war: Ein Fork erbt die verworfenen Vermutungen zusammen mit den Fakten.

Die Tools. Sie sind das, was die Release Notes ankündigen, und stammen aus Pull Request #50148. In der TUI, in einem vertrauenswürdigen lokalen Projekt, bekommt das Modell create_worktree, get_worktree_creation_status und list_worktrees. Das Anlegen läuft asynchron, das Modell stößt es also an und fragt dann den Status ab (IDs gekürzt):

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

Anders als das Flag und die Auswahl verlegt das Tool die Session nicht. Ich habe danach nach pwd gefragt, und es war immer noch der ursprüngliche Checkout. Das Modell bekommt einen Pfad und muss ihn ausdrücklich verwenden, und die Meldung des Tools sagt selbst "request filesystem permissions if needed": Den Checkout anzulegen macht ihn für die Session noch nicht schreibbar. Auch list_worktrees ist enger, als es klingt. Es liefert die Worktrees, die an der aktuellen Konversation hängen. In einem Repository, das schon mehrere verwaltete Worktrees hatte, bekam eine neue Konversation deshalb eine leere Liste.

Die Tools gibt es nur in der TUI. Ich habe eine codex exec-Session gefragt, ob sie sie hat, und sie meldete keine. Aus einem Skript heraus ist das Flag der einzige Weg.

Bei welchem Commit beginnt ein Codex-CLI-Worktree?

Das hängt vom Einstieg ab. Das Flag und die Auswahl beginnen beim aktuellen Commit des Checkouts, in dem du sie startest. Das Tool beginnt beim Standard-Branch des Remotes.

Mein Repository stand auf feature/discount und war origin/main voraus:

EinstiegHEAD des WorktreesDieser Commit ist
codex exec --worktree42b4cf1mein aktueller Commit, nicht gepusht
/new, "Create new Git worktree"9a95de4mein aktueller Commit zu diesem Zeitpunkt, ein paar Commits später
create_worktree, ohne ref84e5a0aorigin/main

Die Beschreibung des Tools sagt es selbst: "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." Ein Agent, der mitten in der Aufgabe beschließt, einen Worktree zu öffnen, und ref weglässt, bekommt einen Checkout ohne den Branch, an dem du gearbeitet hast. Wenn du in normalem Text um einen Worktree bittest, nenn die Ref.

Aus dem Quellcode, nicht nachgestellt: Der Standard wird aus Refs ermittelt, die schon im Repository liegen, ohne Fetch. Bevorzugt wird der HEAD eines Remotes, origin zuerst, dann origin/main, origin/master, lokales main, lokales master. Ein Repository ohne eine dieser Refs, etwa ohne Remote und mit einem einzigen Branch namens trunk, endet mit dem Fehler "Could not determine the project's default branch." Ein veraltetes origin/main bedeutet einen veralteten Startpunkt. Ebenfalls aus dem Quellcode: codex fork mit dem Flag beginnt beim aktuellen Commit des Verzeichnisses, in dem die geforkte Konversation zuletzt gearbeitet hat, und das muss nicht das Verzeichnis sein, in dem du den Befehl eingetippt hast.

Alle drei erzeugen einen detached HEAD. Keiner der drei legt einen Branch für dich an.

Was lässt ein Codex-CLI-Worktree in deinem Checkout zurück?

Alles, was Git nicht verfolgt, und alles, was du nicht committet hast. Der neue Checkout hatte die versionierten Dateien und sonst nichts:

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

Keine .env, kein node_modules, und meine nicht committete Änderung an web/index.html war auch nicht da. Sie blieb im Quell-Checkout, unangetastet. Ein Agent, der in diesem Worktree "die Tests laufen lassen" soll, stellt als Erstes fest, dass keine Abhängigkeiten da sind. Der Quellcode zeigt drei weitere Lücken, die ich nicht getestet habe: Der Checkout wird mit deaktivierten Git-Hooks und -Filtern befüllt, Git LFS Smudge wird übersprungen, und Submodule werden nicht rekursiv geholt.

Hier unterscheiden sich CLI und Desktop-App. OpenAIs Worktree-Dokumentation beschreibt nur die App. Dort steht, dass die App noch nicht committete Änderungen in den neuen Worktree überträgt und dass "Codex only copies ignored files that match .worktreeinclude". Ich habe eine .worktreeinclude committet, in der .env steht, und das Flag noch einmal ausgeführt. Der Worktree bekam die Datei .worktreeinclude und keine .env. In 0.162.1 liest der CLI-Pfad sie nicht.

Auch das Aufräumen der App gilt hier nicht. Dieselbe Seite sagt, dass die App standardmäßig "your most recent 15 Codex-managed worktrees" behält und ältere löscht. Die CLI teilt sich das Verzeichnis mit der App, und der Kommentar im Quellcode zu ihren Einstellungen lautet "CLI allocation uses the same root without enabling automatic cleanup." Nichts von dem, was ich angelegt habe, wurde für mich entfernt.

Können zwei Codex-CLI-Agenten mit Git-Worktrees parallel an einem Repository arbeiten?

Für die Dateien ja. Den Rest musst du weiterhin selbst regeln. Ich habe zwei Sessions mit codex exec --worktree -s workspace-write gleichzeitig gestartet, die eine bearbeitete api/cart.py, die andere web/index.html. Jede bekam ihren eigenen Checkout, und keine sah die Änderung der anderen.

Committen konnte keine von beiden:

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

Ein verknüpfter Worktree hält seinen Index im .git des Quell-Repositorys, also außerhalb des Worktrees, und ich hielt das zunächst für die ganze Ursache. Ein Kontrolllauf in einem gewöhnlichen Klon, gleiche Sandbox, scheiterte genauso an seinem eigenen .git/index.lock. Am Worktree liegt es also nicht. OpenAIs Sandbox-Dokumentation sagt, das .git eines schreibbaren Workspace "is protected as read-only whether it appears as a directory or file", und wenn es ein gitdir:-Zeiger ist, wie in einem verknüpften Worktree, "the resolved Git directory path is also protected". Steht die Approval-Policy auf never, wie in diesen Läufen, kann der Agent nicht um eine Ausnahme bitten, und das Quell-.git mit --add-dir hinzuzufügen hat nichts geändert. In dieser Konfiguration bearbeiten also die Agenten, und du committest. Mit einer anderen Approval-Policy kann der Agent fragen. Das habe ich nicht getestet.

Was ein Worktree teilt, ist das Repository selbst. Branches gehören allen Checkouts gemeinsam, und Git erlaubt einen Branch immer nur in einem davon. Sobald ich einem Worktree einen Branch-Namen gegeben hatte (wie, zeigt der nächste Abschnitt), konnte der Quell-Checkout nicht mehr dorthin wechseln:

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

Ports, eine lokale Datenbank, Caches außerhalb des Baums und alles andere, was an der Maschine hängt, teilen sich weiterhin alle Checkouts. Ein Worktree isoliert Dateien. Er koordiniert nicht, und er weiß nicht, welcher Agent welche Aufgabe besitzt. In einem früheren Beitrag habe ich argumentiert, dass ein Multi-Agenten-System ein verteiltes System ist und dass der Besitz einer Arbeitseinheit außerhalb der Agenten liegen muss. Ein zweiter Checkout ändert daran nichts. Er beseitigt genau einen Fehlerfall, zwei Schreiber im selben Verzeichnis, und lässt die übrigen, wo sie waren.

Dasselbe gilt, wenn der zweite Agent ein anderes Tool ist: Claude Code und Codex in einem Repository geben Artefakte aneinander weiter, einen Diff oder eine Plan-Datei, und nie eine laufende Konversation.

Wie holst du die Arbeit aus einem Codex-CLI-Worktree zurück, und wie entfernst du ihn?

Gib dem detached HEAD vor allem anderen einen Branch-Namen und merge dann vom Quell-Checkout aus. Ein Gegenstück zum Hand-off-Button der App hat die CLI nicht.

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

Einmal habe ich den Branch-Namen weggelassen, um zu sehen, was passiert. Ich habe in einem Worktree auf einem detached HEAD committet und ihn entfernt. Git hat ihn ohne Einwand entfernt, weil der Baum sauber war, und danach enthielt kein Branch den Commit. git fsck --lost-found listete ihn noch als dangling, er ist also bis zur Garbage Collection wiederherstellbar, aber nichts wird dich daran erinnern.

Ein Worktree mit geänderten oder nicht versionierten Dateien ist sicherer: git worktree remove bricht ab mit "contains modified or untracked files, use --force to delete it". Ignorierte Dateien haben diesen Schutz nicht. Ich habe eine .env in einen sonst sauberen Worktree gelegt und ihn entfernt, und Git hat das Verzeichnis gelöscht, samt .env, ohne ein Wort. Alles, was ein Agent in einen ignorierten Pfad generiert hat, geht denselben Weg.

Zwei Überbleibsel solltest du kennen. git worktree remove löscht den Checkout und lässt dessen Bucket-Verzeichnis aus vier Zeichen leer zurück. Nach zehn Worktrees hatte ich zehn leere Verzeichnisse unter ~/.codex/worktrees. Und zwei Läufe, die ich vor ihrem ersten Turn abgebrochen habe, hinterließen Checkouts, die der Browser als "No attached thread" führt. Es sind gewöhnliche Worktrees, und git worktree list im Quell-Repository zeigt sie alle.

Wann ist ein von Hand angelegter Git-Worktree noch die bessere Wahl als ein von der Codex CLI verwalteter?

Wenn du den Branch schon kennst. git worktree add -b fix/login ../shop-login origin/main gibt dir einen benannten Branch, einen Start-Commit deiner Wahl und ein Verzeichnis deiner Wahl, in einem Befehl. Der detached HEAD, die Frage nach dem Start-Commit und das Bucket-Verzeichnis kommen gar nicht erst auf. Du kannst codex in diesem Verzeichnis starten, und die erste Option der Auswahl, "Use current Git worktree", lässt die Session dort.

Die verwaltete Variante lohnt sich, wenn der Checkout ein Wegwerfartikel ist und die Konversation das ist, was du wiederfinden willst. Der Browser unter /worktree listet jeden Checkout mit dem Titel der Konversation auf, der er gehört, und bietet an, diese Konversation fortzusetzen. Ein von Hand angelegtes Verzeichnis hat keine solche Verbindung.

Bevor ich einen zweiten Agenten im selben Repository starte, würde ich drei Dinge tun. Erst committen oder stashen, denn der neue Checkout beginnt bei einem Commit und sieht deinen Arbeitsbaum nicht. Die gewünschte Ref nennen, wann immer das Modell den Worktree anlegt. Und dem Ergebnis einen Branch-Namen geben, bevor du irgendetwas entfernst.

Diskussion

Hier gibt es keine Kommentarspalte. Diskussionen laufen auf X.

Max Nardit

Max Nardit

@mnardit

Weitere Artikel

Claude Code Skills als Lieferkette: 2,2 Millionen Übernahmen, keine Registry und ein Scanner, der den Quelltext liest, während Python den Cache ausführt

Einen Skill zu lesen, bevor du ihn ausführst, ist der Standardrat, und er hat zwei Lücken. Ein kopierter Skill hat keinen Update-Kanal, die Kopie, die du gelesen hast, kann sich also unbemerkt von ihrer Quelle entfernen. Und bei einem Skill, der Python mitbringt, ist die Datei, die du liest, nicht immer die Datei, die der Interpreter lädt.

Claude Code vs Codex: sieben Arten, wie Entwickler die Arbeit zwischen beiden aufteilen, statt sich für eines zu entscheiden

Die Übergaben sind inzwischen von den Herstellern dokumentiert: ein Review-Befehl, ein Session-Transfer, ein Importer in jede Richtung. Welches Tool welche Aufgabe bekommen soll, steht nirgends. Sieben Aufteilungen, jede bis zu ihrem Ursprung zurückverfolgt und als dokumentiert oder berichtet markiert, dazu die Sandbox-Voreinstellungen, die sich unterscheiden, und die Integrationen, die dieses Jahr entfernt wurden.