Git Worktree für parallele Branch-Entwicklung: ein sauber isolierter Workflow
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__*.dbsnapshot_*.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-currentausführen - vor jedem Commit
git status --shortausführen - nur aus dem Haupt-Repository auf
mastermergen
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.