Wie ich ein automatisiertes Git Worktree System gebaut hab, das Port-Konflikte für immer beendet
Die Reise eines Entwicklers vom manuellen Umgebungs-Switching-Albtraum zum automatisierten Multi-Branch-Paradies
- Der 3-Uhr-Morgens-Panik-Anruf
- Der Aha-Moment: Git Worktrees
- Das Problem: Es geht nicht nur um Git
- Die Lösung: Automatisierung, Automatisierung, Automatisierung
- Schritt 1: Die Port-Allokations-Formel
- Schritt 2: Environment Templates
- Schritt 3: Docker Integration
- Schritt 4: Das Master-Script
- Schritt 5: IDE Integration (Die Geheimzutat)
- Die Workflow-Revolution
- Real-World Ergebnisse
- Lessons Learned (auf die harte Tour)
- Der unerwartete Benefit: Furchtloses Experimentieren
- Wie du das auch bauen kannst
- Der Code
- Was kommt als Nächstes
- Fazit
Der 3-Uhr-Morgens-Panik-Anruf
Stell dir vor: Du bist voll im Flow, baust grad ein komplexes User-Authentication-System für deine Web-App. Drei Terminal-Fenster offen, Docker-Container surren vor sich hin, Datenbank-Migrationen laufen smooth. Dann vibriert dein Handy.
“Hey, das Payment-System ist komplett broken in Production. Kannst du SOFORT schauen?”
Wir kennen das alle. Dieses sinkende Gefühl, wenn dir klar wird, dass du jetzt:
- Deine aktuelle Arbeit stoppen musst
- Halbfertigen Code stashen/committen musst
- Den Branch wechseln musst
- Docker-Container neu bauen musst
- Mit Port-Konflikten kämpfen musst, weil dein Auth-System Port 3000 nutzt
- Möglicherweise deinen Datenbank-State verlierst
- Den dringenden Bug fixen musst
- Das ganze Zeug rückgängig machen musst, um wieder zu deiner Authentication-Arbeit zurückzukommen
Bis du wieder bei deiner ursprünglichen Aufgabe bist, hast du 30 Minuten verloren und deinen kompletten mentalen Kontext. Es musste doch einen besseren Weg geben.
Der Aha-Moment: Git Worktrees
Ich hatte schon mal von Git Worktrees gehört, aber nie wirklich verstanden, was das Ding kann. Die Dokumentation ließ es klingen wie irgendeine advanced Git-Zauberei für Kernel-Entwickler. Aber als ich das Konzept endlich gecheckt hab, war’s wie die Entdeckung des Feuers:
Git Worktrees lassen dich mehrere Branches gleichzeitig in separaten Verzeichnissen auschecken.
Anstatt Branches in einem Ordner zu wechseln, kannst du haben:
my-project/ # Main branch
my-project-auth/ # Authentication feature branch
my-project-payments/ # Payment system fixes
my-project-integration/ # Integration testing branch
Jedes Verzeichnis ist eine komplette Working Copy, aber alle teilen sich das gleiche Git Repository. Mind = blown.
Das Problem: Es geht nicht nur um Git
Aber hier ist der Haken – nur mehrere Verzeichnisse zu haben löst das echte Problem nicht. Jeder Branch braucht seine eigene:
- Datenbank-Instanz (kann keine Daten zwischen Features teilen)
- Port-Allokation (kann nicht mehrere Server auf Port 3000 laufen lassen)
- Umgebungs-Konfiguration (verschiedene API-Keys, Settings)
- IDE-Setup (separate Debug-Konfigurationen)
Das alles manuell für jeden Worktree zu managen? Das ist nur ein Albtraum gegen einen anderen eingetauscht.
Die Lösung: Automatisierung, Automatisierung, Automatisierung
Ich hab beschlossen, ein System zu bauen, das:
- Automatisch Git Worktrees erstellt mit ordentlichem Branch-Management
- Intelligent Ports allokiert um Konflikte zu vermeiden
- Isolierte Umgebungen generiert für jeden Worktree
- IDE-Konfigurationen automatisch aufsetzt
- Docker-Integration nahtlos handled
So hab ich’s gebaut, Schritt für Schritt.
Schritt 1: Die Port-Allokations-Formel
Die erste Challenge waren die Ports. Wenn mein Main Branch Ports 3000-3010 nutzt, welche Ports sollten meine Feature Branches nutzen?
Ich kam auf diese simple Formel:
SERVICE_PORT = BASE_PORT + (WORKTREE_INDEX * 10) + SERVICE_OFFSET
Das bedeutet:
- Base Port: 40000 (schöne hohe Nummer, unwahrscheinlich dass sie kollidiert)
- Worktree Index: 0 für main, 1 für ersten Worktree, 2 für zweiten, etc.
- Service Offset: Unterschiedlich für jeden Service (0=Database, 1=Web, 2=Cache, etc.)
Also für Worktree Index 1:
- Database: 40000 + (1 × 10) + 0 = 40010
- Web server: 40000 + (1 × 10) + 1 = 40011
- Redis: 40000 + (1 × 10) + 2 = 40012
Das gibt jedem Worktree einen vorhersagbaren Block von 10 Ports, eliminiert Konflikte komplett.
Schritt 2: Environment Templates
Nächstes Problem: Jeder Worktree braucht seine eigene Environment-Konfiguration.
Ich hab eine .env.template
Datei erstellt:
# .env.template
APP_NAME=MyApp-${WORKTREE_NAME}
DATABASE_URL=postgresql://user:pass@localhost:${POSTGRES_PORT}/myapp
REDIS_URL=redis://localhost:${REDIS_PORT}
WEB_PORT=${WEB_PORT}
WORKTREE_INDEX=${WORKTREE_INDEX}
Mein Script verarbeitet dieses Template, ersetzt Variablen mit berechneten Werten:
# Generierte .env für feature-auth worktree
APP_NAME=MyApp-feature-auth
DATABASE_URL=postgresql://user:pass@localhost:40010/myapp
REDIS_URL=redis://localhost:40012
WEB_PORT=40011
WORKTREE_INDEX=1
Jetzt kriegt jeder Worktree eine komplett isolierte Umgebung ohne manuelle Konfiguration.
Schritt 3: Docker Integration
Die Docker-Integration war kniffliger. Ich wollte:
- Base Images und Volumes für Effizienz teilen
- Datenbank-Daten pro Worktree separat halten
- Container-Namen-Konflikte vermeiden
Hier ist mein docker-compose Ansatz:
services:
web:
ports:
- "${WEB_PORT:-3000}:80"
environment:
- WORKTREE_INDEX=${WORKTREE_INDEX:-0}
database:
ports:
- "${POSTGRES_PORT:-5432}:5432"
volumes:
- db-data-wt${WORKTREE_INDEX:-0}:/var/lib/postgresql/data
volumes:
db-data-wt0: # Main branch database
db-data-wt1: # Worktree 1 database
# ... etc
Die ${VAR:-default}
Syntax bedeutet, dass der Main Branch immer noch mit Default-Ports funktioniert, aber Worktrees ihre berechneten Ports bekommen.
Schritt 4: Das Master-Script
Die ganze Logik kam in ein Bash-Script namens setup-worktree.sh
. Hier ist der Workflow:
#!/bin/bash
./setup-worktree.sh feature/user-authentication
Das Script:
- Validiert den Branch-Namen und checkt den Git Status
- Findet den nächsten verfügbaren Worktree Index durch Scannen existierender Worktrees
- Berechnet alle Ports mit der Formel
- Erstellt den Git Worktree in
../feature-user-authentication/
- Generiert die
.env
Datei aus dem Template - Setzt Docker Container mit den neuen Ports auf
- Konfiguriert IDE Settings (PhpStorm Run Configurations)
- Testet, dass alle Services erfolgreich starten
Gesamtzeit: etwa 30 Sekunden. Was früher 10+ Minuten manuelle Arbeit war.
Schritt 5: IDE Integration (Die Geheimzutat)
Hier wird’s richtig geil. Das Script generiert auch PhpStorm Run Configurations:
<!-- Generierte .idea/runConfigurations/Debug_WT1.xml -->
<component name="ProjectRunConfigurationManager">
<configuration name="Debug WT1" type="PhpWebAppRunConfigurationType">
<server name="localhost" host="localhost" port="40011" />
<path_mappings>
<mapping local-root="$PROJECT_DIR$" remote-root="/app" />
</path_mappings>
</configuration>
</component>
Wenn ich jetzt PhpStorm im Worktree-Verzeichnis öffne, hab ich ready-to-use Debug-Konfigurationen mit den richtigen Ports. Kein manuelles Setup nötig.
Die Workflow-Revolution
So hat sich mein Workflow geändert:
Alter manueller Prozess:
Neuer automatisierter Prozess:
Vorher:
# Arbeite an Feature A
git stash
git checkout main
git checkout -b hotfix/payment-bug
# Container stoppen, Ports ändern, neu bauen...
# 10 Minuten später, endlich am Bug arbeiten
# Bug fixen, committen, pushen
# Jetzt alles rückgängig machen um zu Feature A zurückzukommen...
# Nochmal 10 Minuten weg
Nachher:
# Arbeite an Feature A, dringender Bug kommt rein
./setup-worktree.sh hotfix/payment-bug
cd ../hotfix-payment-bug
# 30 Sekunden später, voll funktionsfähige Umgebung
# Bug fixen, committen, pushen
cd ../feature-a
# Sofort wieder bei der Arbeit, nichts unterbrochen
Der Unterschied ist wie Tag und Nacht.
Real-World Ergebnisse
Nach 3 Monaten mit diesem System, hier sind die tatsächlichen Benefits, die ich gemessen hab:
Zeitersparnis:
- Umgebungswechsel: 10 Minuten → 30 Sekunden
- Kontext-Recovery: 15 Minuten → 0 Minuten (kein Kontext verloren)
- Bug-Fix-Unterbrechungen: ~25 Minuten → ~2 Minuten
Qualitäts-Verbesserungen:
- Null Production-Bugs durch Umgebungs-Verwirrung
- Mehr Bereitschaft, Branches für Experimente zu erstellen
- Bessere Trennung der Concerns zwischen Features
Team-Produktivität:
- Keine “mein Branch kollidiert mit deinem” Gespräche mehr
- Easy verschiedene Branches gleichzeitig zu reviewen
- Integrationstests zwischen Branches werden trivial
Lessons Learned (auf die harte Tour)
Fehler #1: Cache Permission Hell
Meine erste Version hat Docker Volumes zwischen Worktrees geteilt um Speicherplatz zu sparen. Schlechte Idee. Die Cache-Verzeichnis-Permissions wurden komplett durcheinander gebracht, weil verschiedene Container mit verschiedenen User IDs liefen.
Fix: Jeder Worktree kriegt sein eigenes Cache Volume, aber ich hab optimiert, indem ich den Composer Dependency Cache teile (der ist read-only nach der Installation).
Fehler #2: Port-Formel Edge Cases
Meine initiale Port-Formel hat gelöschte Worktrees nicht gut gehandled. Wenn ich Worktree Index 2 gelöscht hab, würde das System versuchen, diesen Index wiederzuverwenden, aber die Ports könnten noch von hängenden Containern benutzt werden.
Fix: Port-Verfügbarkeits-Check vor der Allokation hinzugefügt:
check_port_available() {
local port=$1
if lsof -Pi :$port -sTCP:LISTEN -t >/dev/null 2>&1; then
return 1 # Port in use
fi
return 0 # Port available
}
Fehler #3: Branch-Namen-Annahmen
Ich bin initial davon ausgegangen, dass Branch-Namen immer simpel sein würden wie feature/auth
. Dann hat jemand feature/user-auth-with-2FA-and-social-login
erstellt. Der lange Name hat meine Verzeichnis-Benennungs-Logik gesprengt.
Fix: Branch-Namen-Sanitization hinzugefügt:
sanitize_branch_name() {
echo "$1" | sed 's/[^a-zA-Z0-9-]/-/g' | cut -c1-50
}
Fehler #4: Datenbank-State-Verwirrung
Mit mehreren Datenbanken am Laufen, hab ich manchmal vergessen, welcher Worktree welchen Daten-State hatte. War der Test-User-Account in Worktree 1 oder 2?
Fix: Datenbank-Benennung hinzugefügt, die den Branch inkludiert:
DATABASE_URL=postgresql://user:pass@localhost:40010/myapp_feature_auth
Jetzt kriegt jeder Worktree eine einzigartig benannte Datenbank, und ich kann genau sehen, welche Daten wohin gehören.
Der unerwartete Benefit: Furchtloses Experimentieren
Der größte Benefit war nicht, was ich erwartet hatte. Zero-Cost Branch-Erstellung hat komplett geändert, wie ich entwickle.
Vorher bedeutete einen neuen Branch zu erstellen:
- “Ist dieses Experiment die Setup-Zeit wert?”
- “Was wenn ich meine Main-Umgebung vermassele?”
- “Ich hack das erstmal in meinen aktuellen Branch…”
Nachher:
- “Lass mich schnell einen Worktree aufspinnen, um diese Idee zu testen”
- “Ich kann ohne Risiko experimentieren”
- “Warum nicht einen separaten Branch für jeden Ansatz erstellen?”
Ich erstelle jetzt 3x mehr Branches, aber jeder ist fokussiert und clean. Meine Commit-History ist viel besser, und Code Reviews sind einfacher, weil jeder PR genau eine Sache macht.
Wie du das auch bauen kannst
Willst du was Ähnliches implementieren? Hier ist mein Rat:
Fang simpel an
Bau nicht alles auf einmal. Starte nur mit der Port-Allokations-Formel und manueller Umgebungs-Erstellung. Werd erstmal mit Git Worktrees vertraut.
Wähl deinen Stack
Meine Beispiele nutzen:
- Docker Compose für Containerisierung
- PostgreSQL für die Datenbank
- PHP/Symfony für die App
- PhpStorm für die IDE
Aber die Konzepte funktionieren mit jedem Stack. Die Port-Formel funktioniert überall, und jede IDE hat irgendeine Form von Run Configuration.
Fokussier dich auf deine Pain Points
Was dauert bei dir am längsten beim Kontext-Wechsel? Für mich waren’s Port-Konflikte und Docker-Rebuilds. Für dich sind’s vielleicht Datenbank-Migrationen oder Frontend-Build-Prozesse.
Mach es reversibel
Bau Cleanup von Tag eins ein. Es ist easy, Worktrees zu erstellen; stell sicher, dass du sie auch sauber zerstören kannst.
Test mit deinem Team
Was für einen Entwickler funktioniert, funktioniert vielleicht nicht für ein Team. Test deine Automatisierung mit Kollegen, bevor du sie ausrollst.
Der Code
Ich hab das Core-Script und die Konfigurations-Templates open-sourced. Die Hauptkomponenten sind:
setup-worktree.sh
(232 Zeilen Bash)Makefile
mit Worktree-Commands.env.template
für Environment-Generierung- Docker Compose Integration
- PhpStorm Konfigurations-Generierung
Du findest es im [Projekt-Repository] und kannst es für deinen Stack anpassen.
Was kommt als Nächstes
Dieses System läuft jetzt seit 6 Monaten in Production für unser Team von 8 Entwicklern. Wir arbeiten an:
- Auto-Cleanup für veraltete Worktrees
- Resource-Monitoring um Per-Branch-Resource-Usage zu tracken
- Cloud-Integration für Remote Development Environments
- Team-Synchronisation um Worktree-Konfigurationen zu teilen
Fazit
Dieses automatisierte Git Worktree System zu bauen war eines dieser seltenen Projekte, wo die Lösung meine Erwartungen übertroffen hat. Ich dachte, ich löse nur Port-Konflikte, aber ich hab am Ende meinen kompletten Development-Workflow transformiert.
Die Key-Erkenntnis ist, dass Development-Umgebungen Infrastruktur sind, und Infrastruktur sollte automatisiert werden. Genau wie wir keine Server mehr manuell provisionieren, sollten wir Development-Umgebungen nicht manuell managen.
Wenn du den Branch-Switching-Tanz satt hast, wenn du jemals Arbeit durch Umgebungs-Konflikte verloren hast, oder wenn du einfach furchtlos experimentieren willst, probier diesen Ansatz aus. Dein zukünftiges Ich wird dir danken.
Hast du was Ähnliches gebaut? Was ist dein Ansatz für das Management mehrerer Development-Umgebungen? Ich würd’s gern in den Kommentaren hören oder meld dich auf [Twitter/LinkedIn].
Weiterführende Literatur:
- Git Worktrees Offizielle Dokumentation
- Docker Compose Environment Variables
- Why You Should Use Git Worktrees
Ursprünglich veröffentlicht auf [Your Blog]. Wenn das dir geholfen hat, überleg’s mit deinem Team zu teilen!