Parallel arbeiten mit Git-Worktrees

Mehrere Claude Sessions und ein Git Repository

Veröffentlicht
Aktualisiert
Kategorien
KI

Das Problem kennt wahrscheinlich jeder, der regelmäßig zwischen mehreren Aufgaben in einem Repository wechselt: Ein Feature ist halb fertig, dann kommt ein Review oder ein kleiner Fix dazwischen. Also Änderungen stashen, Branch wechseln, kurz etwas prüfen, wieder zurückwechseln und den Stash zurückholen.

Mit einem zweiten Entwickler ist das kein großes Thema — jeder hat schließlich seinen eigenen Checkout. Mit mehreren Coding-Agenten auf demselben Rechner sieht das anders aus. Wenn zwei Agenten im gleichen Arbeitsverzeichnis Dateien verändern, können sie sich gegenseitig Änderungen überschreiben. Und wenn einer davon den Branch wechselt, ändert sich der Dateistand gleich für alle Prozesse, die in diesem Verzeichnis arbeiten.

Man kann natürlich einfach ein zweites Repository klonen. Das funktioniert, ist aber unnötig schwergewichtig. Git hat für genau diesen Fall Worktrees.

Ein Repository, mehrere Arbeitsverzeichnisse

Ein Git-Worktree ist im Grunde ein zusätzlicher Checkout desselben Repositorys. Jeder Worktree hat sein eigenes Arbeitsverzeichnis und seinen eigenen Index, verwendet aber dieselbe Git-Historie.

Das ist der entscheidende Punkt: Die Objektdatenbank wird nicht für jeden Worktree kopiert. Die ausgecheckten Dateien dagegen schon.

Ein neuer Worktree ist schnell angelegt:

# neuer Branch, neues Arbeitsverzeichnis
git worktree add ../projekt-feature-a -b feature-a

# vorhandener Branch
git worktree add ../projekt-fix fix-issue-456

# alle Worktrees anzeigen
git worktree list

# nach dem Merge wieder entfernen
git worktree remove ../projekt-feature-a
git worktree prune

Danach lässt sich in beiden Verzeichnissen unabhängig arbeiten. git log, git commit oder git push funktionieren wie gewohnt. Nur der ausgecheckte Dateistand ist getrennt.

Das klingt zunächst nach einem Detail, macht bei parallelen Agenten aber einen großen Unterschied.

Was trotzdem geteilt wird

Ganz unabhängig sind Worktrees nicht. Git teilt grundsätzlich die Refs des Repositorys. HEAD und einige spezielle Pseudo-Refs gehören dagegen zum jeweiligen Worktree.

Eine praktische Folge davon: Der Stash ist gemeinsam. refs/stash gehört zum Repository und nicht zum einzelnen Worktree. Ein git stash aus Worktree A erscheint deshalb auch in git stash list von Worktree B.

Das sollte man im Hinterkopf behalten. Ich versuche deshalb, Stashes in parallelen Worktree-Setups möglichst zu vermeiden.

Verwaiste Worktrees wieder loswerden

Git legt für jeden zusätzlichen Worktree Metadaten unter .git/worktrees/<name> an.

Löscht man das Worktree-Verzeichnis einfach im Finder oder mit rm -rf, bleiben diese Informationen bestehen. git worktree list zeigt den Eintrag dann weiterhin als prunable.

Dafür gibt es:

git worktree prune

Besser ist es, den Worktree direkt mit Git zu entfernen:

git worktree remove ../projekt-feature-a

Git prüft dabei auch, ob noch Änderungen oder unversionierte Dateien vorhanden sind. Das ist sinnvoll, weil genau dort noch Arbeit liegen könnte. Mit --force kann man die Prüfung umgehen, sollte dann aber wissen, was man tut.

Worktrees lassen sich außerdem sperren:

git worktree lock <pfad>
git worktree unlock <pfad>

Claude Code nutzt solche Sperren, solange ein Agent in einem Worktree arbeitet.

Das eigentliche Platzproblem ist nicht Git

In einem konkreten Projekt war die Git-Historie ungefähr 18 MB groß. Ein zusätzlicher Worktree belegte nach dem Setup trotzdem rund ein Gigabyte.

Der Grund war erwartbar: node_modules.

Ein neuer Worktree enthält zunächst nur Dateien, die tatsächlich in Git liegen. Alles, was über .gitignore ausgeschlossen ist, fehlt:

  • node_modules/

  • .env.local

  • .next/

  • Turbopack-Caches

  • tsbuildinfo

  • andere lokale Konfigurationen

Gerade bei modernen Next.js-Projekten ist das relevant. Die Git-Daten selbst sind fast zu vernachlässigen. Dependencies und Build-Caches machen den Worktree groß.

Dazu kommt ein weiteres praktisches Problem: Zwei Dev-Server können nicht beide auf Port 3000 laufen. Wenn ich mehrere Worktrees gleichzeitig starte, bekommt jeder einen eigenen Port.

Die .env.local ist der unangenehmere Fehler

Fehlende Dependencies fallen sofort auf. Eine fehlende .env.local kann deutlich mehr Zeit kosten.

Der Dev-Server startet möglicherweise ganz normal. Erst die CMS-Abfragen oder andere externe Services funktionieren nicht. Dann sucht man schnell im Code nach dem Fehler, obwohl eigentlich nur die lokale Konfiguration fehlt.

Unversionierte Dateien werden von Git nicht automatisch in einen neuen Worktree kopiert.

Claude Code hat dafür .worktreeinclude.

Im Root des Repositorys kann beispielsweise stehen:

.env
.env.local
certificates/

Claude Code kopiert passende Dateien beim Anlegen eines Worktrees mit.

Wichtig dabei: Eine Datei wird nur kopiert, wenn sie sowohl auf ein Muster in .worktreeinclude passt als auch von Git ignoriert wird. Versionierte Dateien werden dadurch nicht versehentlich dupliziert.

Bei komplizierteren Glob-Mustern lohnt es sich, explizit zu werden. Statt beispielsweise

**/config.json

ist ein konkretes

vendor/**/config.json

oft leichter nachvollziehbar.

Wenn man das Erstellen von Worktrees über einen eigenen Hook übernimmt, wird .worktreeinclude nicht automatisch ausgewertet. Dann muss das Kopieren ebenfalls in den Hook.

pnpm install und die Port-Konfiguration bleiben trotzdem Setup-Arbeit. Deshalb lege ich nicht für jede kleine Änderung einen Worktree an.

Claude Code kann Worktrees selbst anlegen

Claude Code unterstützt Worktrees inzwischen direkt.

claude --worktree feature-auth

Dabei entsteht unter

.claude/worktrees/feature-auth/

ein neuer Worktree mit dem Branch:

worktree-feature-auth

In einem zweiten Terminal kann parallel eine weitere Session mit einem anderen Worktree laufen.

Von welchem Stand wird abgezweigt?

Das ist wichtig, wenn lokal bereits Änderungen oder unfertige Branches existieren.

Standardmäßig verwendet Claude Code fresh. Der neue Worktree basiert dann auf dem Default-Branch des Remotes.

Soll der Agent stattdessen vom aktuellen lokalen HEAD starten, kann man das konfigurieren:

{
  "worktree": {
    "baseRef": "head"
  }
}

Welche Einstellung sinnvoll ist, hängt vom Anwendungsfall ab:

  • Neue, unabhängige Aufgabe: lieber vom sauberen Default-Branch.

  • Agent soll auf der aktuellen Arbeit aufbauen: head.

So vermeide ich, dass ein neuer Task unabsichtlich von einem halbfertigen Feature abhängt.

Subagenten lassen sich ebenfalls isolieren

Bei eigenen Subagenten kann die Worktree-Isolation direkt im Frontmatter definiert werden:

---
name: refactorer
description: Wendet mechanische Refactorings über viele Dateien an
isolation: worktree
---

Das trennt zwei Dinge, die man leicht durcheinanderbringt.

Ein Subagent hat einen eigenen Kontext. Er kann also eine Teilaufgabe recherchieren oder bearbeiten, ohne das Kontextfenster der Hauptsession mit allen Zwischenschritten zu füllen.

Der Worktree trennt dagegen die Dateien.

Für wirklich paralleles Arbeiten braucht es im Zweifel beides: einen eigenen Agentenkontext und ein eigenes Arbeitsverzeichnis.

Wenn Claude Code für einen Subagenten einen Worktree anlegt und dort keine Änderungen entstehen, wird er anschließend automatisch entfernt. Gibt es Änderungen, bleibt er bestehen, damit keine Arbeit verloren geht.

Die Isolation ist mehr als eine Anweisung an den Agenten

Interessant finde ich, dass Claude Code die Trennung nicht nur über einen Prompt löst.

Wenn eine Session in einem Worktree läuft, werden Zugriffe auf den Hauptcheckout geprüft und gegebenenfalls blockiert. Das betrifft unter anderem Dateiänderungen, das Arbeitsverzeichnis von Shell-Kommandos und Git-Aufrufe über Konstrukte wie:

git -C ...

oder Umgebungsvariablen wie:

GIT_DIR=...

Auch vorgeschaltete cd-Kommandos werden berücksichtigt.

Kommandos, deren tatsächlicher Wirkungsort vor der Ausführung nicht zuverlässig bestimmt werden kann, können ebenfalls abgelehnt werden. Dazu gehören beispielsweise bestimmte Brace-Expansions oder Heredocs mit unquotiertem Delimiter.

Das ist für mich einer der wichtigeren Punkte an der Implementierung: Man muss sich nicht darauf verlassen, dass ein Agent „daran denkt“, nur in seinem Verzeichnis zu arbeiten.

Einen Pull Request direkt als Worktree öffnen

Für Reviews ist noch eine Variante praktisch:

claude --worktree "#1234"

Claude Code holt den Head-Commit des Pull Requests und legt dafür einen Worktree unter

.claude/worktrees/pr-1234

an.

Damit lässt sich ein PR in einer isolierten Umgebung starten, testen oder von einem Agenten analysieren lassen, ohne meinen aktuellen Checkout anzufassen.

Was sich in der Praxis bewährt

Ein paar Regeln haben sich inzwischen als sinnvoll erwiesen.

.claude/worktrees/ gehört in .gitignore

Sonst tauchen die vollständigen Arbeitskopien als unversionierte Dateien im Hauptcheckout auf. Spätestens bei einem unbedachten

git add -A

wird das unangenehm.

Also:

.claude/worktrees/

Ein Worktree pro Thema

Ich behandle einen Worktree ähnlich wie einen Branch: eine Aufgabe, ein Thema, möglichst ein Pull Request.

Wenn mehrere unabhängige Änderungen im selben Worktree landen, verliere ich einen großen Teil der Übersicht wieder.

Neue Themen möglichst vom Default-Branch starten

Ein Worktree für Task B sollte normalerweise nicht vom halbfertigen Branch von Task A abzweigen.

Sonst hängt der zweite Pull Request plötzlich vom ersten ab und beide lassen sich nicht mehr sauber unabhängig reviewen oder mergen.

git worktree list regelmäßig ansehen

Ich hatte tatsächlich drei Worktrees im Projekt liegen, einer davon gehörte zu einem Branch, der längst gemergt war.

Aufgefallen ist das nicht wegen eines Fehlers, sondern weil git status seit Tagen eine zusätzliche Zeile zeigte.

In solchen Setups ist

git worktree list

die verlässlichere Übersicht als offene Terminal-Tabs oder die Erinnerung daran, welche Agenten irgendwann einmal gestartet wurden.

Nach dem Merge sollte aufgeräumt werden:

git worktree remove <pfad>
git worktree prune

Parallelität braucht trotzdem ein WIP-Limit

Mit Worktrees kann man problemlos drei Agenten gleichzeitig Änderungen produzieren lassen. Danach gibt es aber auch drei Änderungen, die jemand verstehen und reviewen muss.

Damit wird häufig die verfügbare Review-Kapazität zur eigentlichen Grenze — nicht Git oder Claude Code.

Mehr parallele Agenten bedeuten nicht automatisch mehr Durchsatz. Irgendwann verschiebt sich der Engpass einfach vom Schreiben zum Review.

Wo Worktrees problematisch werden

Git-Submodule

Git weist selbst darauf hin, dass mehrere Checkouts bei Repositories mit Submodulen nicht vollständig unterstützt werden.

Wenn ein Projekt stark auf Submodule setzt, sollte man Worktrees deshalb nicht einfach als Standard-Setup einführen.

Git LFS

Ein weiterer Sonderfall ist Git LFS, wenn es lokal für das Repository eingerichtet wurde:

git lfs install --local

Dabei landet der LFS-Filter in der .git/config des Repositorys.

Claude Code führt repository-eigene Filter-Driver beim Erstellen eines Worktrees nicht aus. Der neue Worktree kann deshalb zunächst nur die LFS-Pointer statt der eigentlichen Dateien enthalten.

Dann hilft:

git lfs pull

Das Verhalten hat einen Sicherheitsgrund. Ein Filter-Driver ist letztlich ein Shell-Kommando, das beim Ein- oder Auschecken Dateien transformiert. Würde Claude Code beliebige Filter aus dem Repository automatisch ausführen, könnte ein Repository beim Anlegen eines Worktrees eigenen Code ausführen.

Der zusätzliche manuelle Schritt ist in dem Fall die sinnvollere Voreinstellung.

Worktrees lösen keine Merge-Konflikte

Die getrennten Arbeitsverzeichnisse verhindern, dass sich zwei Agenten während der Arbeit gegenseitig Dateien überschreiben.

Wenn beide dieselbe Datei ändern, kann beim Merge natürlich trotzdem ein Konflikt entstehen.

Worktrees helfen mir deshalb vor allem dabei, mehrere Arbeitsstände sauber voneinander zu trennen. Die Abstimmung darüber, wer welchen Teil des Codes verändert, bleibt trotzdem notwendig.

Und genau deshalb sollte man auch nicht beliebig viele Worktrees parallel öffnen. Technisch geht das schnell. Die eigentliche Arbeit beginnt häufig erst danach: Änderungen lesen, testen, verstehen und zusammenführen.

Für zwei oder drei klar getrennte Aufgaben funktioniert das für mich inzwischen sehr gut. Vor allem bei Claude Code ist es deutlich angenehmer, einem Agenten einen eigenen Worktree zu geben, als ständig zu prüfen, ob gerade noch jemand anderes im selben Checkout arbeitet.

Jetzt kennenlernen