Das übliche Problem bei paralleler Entwicklung ist nicht, einen weiteren Branch anzulegen. Das macht Git trivial. Schwierig ist sicherzustellen, dass das, was man gerade testet, wirklich der eben geänderte Branch ist, dass der Dienst aus dem richtigen Verzeichnis läuft und dass der finale Commit keine fremden Dateien mitnimmt.

Ich bin schließlich bei einem deutlich saubereren Muster gelandet: Das Hauptverzeichnis des Repositories bleibt stabil, und mit git worktree bekommt jeder Feature-Branch ein eigenes isoliertes Arbeitsverzeichnis. Seit ich das konsequent so mache, ist parallele Arbeit an mehreren Features auf derselben Maschine wesentlich weniger fehleranfällig.

Wenn du nach paralleler Branch-Entwicklung mit Git, einem Git-worktree-Workflow oder einem praktischen Standardprozess für isolierte Branches suchst, geht es hier genau darum.

Warum ich nicht mehr ständig zwischen Branches wechsle

Das Kernproblem ist nicht das Erstellen von Branches, sondern Kontamination.

In einem einzigen Repository-Verzeichnis führen wiederholte checkout-Zyklen häufig zu denselben Problemen:

  • du glaubst, ein neues Feature zu testen, aber der laufende Dienst stammt noch aus einem anderen Verzeichnis
  • Build-Artefakte eines Features gelangen in einen anderen Branch
  • git add . nimmt unbemerkt Dateien auf, die niemals committed werden sollten
  • Bugfixes und Feature-Arbeit teilen sich zunehmend denselben unordentlichen Workspace

Genau das löst Git worktree sehr gut. Es hilft nicht nur, schneller einen Branch anzulegen, sondern gibt jedem Branch einen physisch getrennten Ort.

Die Verzeichnisregeln, die bei mir fest sind

Damit der Workflow angenehm langweilig bleibt, behandle ich einige Konventionen als nicht verhandelbar:

  • Haupt-Repository: /Users/kuangyongjie/charting_library
  • Worktree-Root: /Users/kuangyongjie/worktrees
  • Branch-Format: codex/<feature-name>
  • Haupt-Branch: master

Das Haupt-Repository dient also Stabilität und Merge, während die eigentliche Feature-Arbeit zum Beispiel hier stattfindet:

/Users/kuangyongjie/worktrees/hotspot-ff

Das wirkt wie eine Kleinigkeit, verändert aber viel. Wenn schon der Pfad zeigt, wo man sich befindet, verwechselt man Haupt-Repo und Feature-Workspace wesentlich seltener.

Der Standardablauf ist kurz, aber jeder Schritt zählt

Zuerst sicherstellen, dass das Worktree-Root existiert:

mkdir -p /Users/kuangyongjie/worktrees

Dann vom Haupt-Repository aus einen neuen Worktree samt Branch anlegen:

REPO=/Users/kuangyongjie/charting_library
WT=/Users/kuangyongjie/worktrees/<feature-name>
BRANCH=codex/<feature-name>
BASE=master

# If the remote is available, update master first
# git -C "$REPO" pull --rebase central "$BASE"

git -C "$REPO" worktree add "$WT" -b "$BRANCH" "$BASE"
cd "$WT"
git branch --show-current

Erwartete Ausgabe:

codex/<feature-name>

Diese Bestätigung ist wichtig. Ab diesem Punkt sollten Entwicklung, Build-Befehle und lokale Verifikation vollständig innerhalb von "$WT" stattfinden.

Entwicklung und Tests nur im Branch-Verzeichnis

Sobald der Worktree existiert, bin ich hier streng: Build und Run nur aus diesem Branch-Verzeichnis.

Liegt das Frontend beispielsweise in einem Unterordner:

cd "$WT/trendradar-service/trendradar-web"
npm run build
# production obfuscated build
# npm run build:multi-layer

Eine der häufigsten falschen Debug-Spuren lautet: „Ich habe den Code geändert, aber die Seite sieht noch alt aus.“ Sehr oft ist der Code korrekt. Der Prozess läuft nur aus einem anderen Verzeichnis.

Wenn etwas veraltet wirkt, prüfe ich deshalb zuerst die cwd des Prozesses:

lsof -nP -iTCP:<port> | sed -n '1,5p'
ps -p <pid> -o pid,ppid,command
lsof -p <pid> | rg "cwd"

Zu bestätigen ist, dass das aktuelle Arbeitsverzeichnis tatsächlich hierhin zeigt:

/Users/kuangyongjie/worktrees/<feature-name>/...

Tut es das nicht, testest du wahrscheinlich den falschen Branch – unabhängig davon, was Git anzeigt.

In diesem Workflow verwende ich fast nie git add .

Hier bleibe ich bewusst konservativ.

Bei parallelen Worktrees stage ich lieber nur die gewünschten Dateien, statt das gesamte Verzeichnis einzusammeln. Laufzeitreste, lokale Snapshots, Caches oder generierte Dateien landen sonst zu leicht versehentlich im Commit.

Typische Kandidaten sind:

  • __pycache__
  • *.db
  • snapshot_*
  • .DS_Store

Mein normales Muster sieht daher eher so aus:

cd "$WT"
git add <file1> <file2> <file3>
git status --short
git commit -m "feat/fix: <summary>"

Der zusätzliche Check mit git status --short ist klein, fängt aber viel unnötigen Müll ab.

Zurück mergen nur aus dem Haupt-Repository

Ist das Feature fertig, merge ich nicht aus dem Worktree selbst. Ich gehe zurück ins Haupt-Repository und merge dort:

cd /Users/kuangyongjie/charting_library
git branch --show-current   # must be master

# If the remote is available, update first
# git pull --rebase central master

git merge --no-ff codex/<feature-name>

Wichtiger als --no-ff ist, wirklich auf dem Haupt-Branch und im Hauptverzeichnis zu sein, bevor der Merge beginnt.

Falls diese Meldung erscheint:

MERGE_HEAD exists

ist der vorherige Merge meist nicht sauber abgeschlossen worden. Dann:

git merge --abort

und anschließend Pull/Merge erneut durchführen.

Wenn zusätzlich gepusht werden soll:

cd /Users/kuangyongjie/charting_library
git push <remote> master

Ein praktisches Detail: Auf dieser Maschine heißt der übliche Remote häufig central, nicht origin. Das ist eine Besonderheit der Umgebung, kein Git-worktree-Problem.

Wann ich Fast-Forward statt Merge-Commit verwende

Manchmal ist die Anforderung anders: in einem isolierten Branch-Verzeichnis testen und danach ohne zusätzlichen Merge-Commit zurückführen.

Dafür passt ein Fast-Forward-Ablauf. Zum Beispiel:

cd /Users/kuangyongjie/charting_library
git checkout master
git pull --rebase central master

git worktree add /Users/kuangyongjie/worktrees/hotspot-ff -b codex/hotspot-ff master

Dann den Dienst ausschließlich aus diesem Branch-Worktree starten:

cd /Users/kuangyongjie/worktrees/hotspot-ff/trendradar-service
./venv/bin/uvicorn app.main:app --host 127.0.0.1 --port 9544

Und in einem zweiten Terminal die cwd prüfen:

lsof -nP -iTCP:9544 -sTCP:LISTEN
ps -p <PID> -o pid,ppid,command
lsof -a -p <PID> -d cwd -Fn

Dieser Check lohnt sich immer wieder: „Der Branch hat den Test bestanden“ bedeutet wenig, wenn der Prozess nie aus dem richtigen Verzeichnis lief.

Die Regeln, die für mich inzwischen Pflicht sind

Wenn Worktrees langfristig genutzt werden sollen, würde ich mindestens diese Regeln hart halten:

  • ein Feature pro Worktree-Verzeichnis
  • ein Task oder eine Unterhaltung pro Worktree
  • vor Arbeitsbeginn git branch --show-current ausführen
  • vor jedem Commit git status --short ausführen
  • nur aus dem Haupt-Repository auf master mergen

Keine dieser Regeln ist kompliziert. Genau deshalb funktionieren sie.

Häufige Probleme

Die lokale Seite zeigt noch das alte Ergebnis

Nicht sofort annehmen, dass die Codeänderung fehlgeschlagen ist.

Zuerst prüfen, ob die cwd des laufenden Prozesses im Ziel-Worktree liegt. Danach sicherstellen, dass die Assets tatsächlich aus diesem Branch-Verzeichnis neu gebaut wurden.

fatal: 'origin' does not appear to be a git repository

Meist ist einfach der Remote-Name falsch. Ausführen:

git remote -v

und zum tatsächlich vorhandenen Remote pushen. In dieser Umgebung ist das häufig central.

MERGE_HEAD exists

Das bedeutet meist, dass der letzte Merge unvollständig blieb:

git merge --abort

Danach den Merge-Ablauf neu starten.

Ich habe nur Frontend-Dateien geändert. Muss das Backend neu gestartet werden?

In der Regel nicht. Wenn die statischen Assets korrekt neu gebaut und deployed wurden, reicht oft ein Cache-Refresh.