- Opublikowano
Git worktrees i Vercel CLI: jak naprawić niekompatybilność
- Autorzy
Dlaczego git worktrees są świetne
Git worktrees pozwalają pracować nad kilkoma branchami naraz, bez stashowania i przełączania. Każdy worktree to pełny katalog roboczy z własnym branchem:
# Main repo on 'dev'
~/project/
# Worktree for feature work
~/project-feature-auth/
# Worktree for bug fix
~/project-bugfix-123/
Przy pracy w monorepo to zmienia wszystko. Możesz:
- Puszczać testy na jednym branchu, kodując na drugim
- Porównywać dwie implementacje obok siebie
- Przełączać kontekst bez tracenia stanu
Problem: Vercel CLI wywala się w worktree
W skrypcie dev używamy vercel env pull, żeby pobrać zmienne środowiskowe:
{
"scripts": {
"dev": "vercel env pull .env -y --environment=development && nuxt dev"
}
}
W zwykłym repo działa to bez zarzutu. W worktree — nie:
$ npm run dev
Error: Could not read from .git/config
Dlaczego się wywala
Kluczowa różnica to sposób, w jaki działa .git:
Zwykłe repo:
.git/ ← Directory containing git internals
config ← Vercel reads this
HEAD
objects/
...
Git worktree:
.git ← File (not directory!) pointing to main repo
# Contents: "gitdir: /path/to/main/repo/.git/worktrees/feature-name"
Vercel CLI próbuje odczytać .git/config, zakładając, że .git jest katalogiem. W worktree to plik, więc taka ścieżka po prostu nie istnieje.
Rozwiązanie: omijamy Vercel CLI w worktree
Wystarczy osobny skrypt dev, który pomija vercel env pull:
Krok 1: dodaj skrypt dev:worktree
// package.json
{
"scripts": {
"dev": "vercel env pull .env -y --environment=development && nuxt dev --port 3000",
"dev:worktree": "npx prisma generate && nuxt dev --port 3000"
}
}
Skrypt dla worktree:
- Pomija
vercel env pull(korzysta ze skopiowanego wcześniej.env) - Regeneruje klienta Prismy (na wypadek zmian w schemacie)
- Startuje dev server jak zwykle
Krok 2: skonfiguruj Turbo (monorepo)
Jeśli używasz Turborepo, dodaj nowe zadanie:
// turbo.json
{
"tasks": {
"dev": {
"cache": false,
"persistent": true
},
"dev:worktree": {
"cache": false,
"persistent": true
}
}
}
Krok 3: skrypt musi trafić do każdej aplikacji
Każda aplikacja w monorepo potrzebuje własnego skryptu worktree:
// apps/main-app/package.json
{
"scripts": {
"dev": "vercel env pull .env -y --environment=development && nuxt dev --port 3000",
"dev:worktree": "npx prisma generate && nuxt dev --port 3000"
}
}
// apps/admin/package.json
{
"scripts": {
"dev": "vercel env pull .env -y --environment=development && nuxt dev --port 3001",
"dev:worktree": "npx prisma generate --schema=../main-app/prisma/schema.prisma && nuxt dev --port 3001"
}
}
// apps/docs/package.json
{
"scripts": {
"dev": "nuxt dev --port 3002",
"dev:worktree": "echo 'docs - skipped in worktree mode'"
}
}
Krok 4: skrypt w root package.json
// package.json (root)
{
"scripts": {
"dev": "turbo run dev",
"dev:worktree": "turbo run dev:worktree"
}
}
Zakładanie nowego worktree
Tak wygląda mój workflow przy tworzeniu worktree:
1. Stwórz worktree
# From main repo directory
git worktree add ../project-feature-name -b feature/branch-name
To tworzy:
- Nowy katalog
../project-feature-name - Nowy branch
feature/branch-name - Checkout kodu w tym katalogu
2. Skopiuj niezbędne pliki
Worktrees nie kopiują plików ignorowanych przez gita. Będziesz potrzebować:
node_modules/(albo instalacji od nowa).env(kopia z głównego repo).nuxt/,.output/(i tak się regenerują)
Do śledzenia tego, co kopiować, używam pliku .worktreeinclude:
# .worktreeinclude
node_modules
.env
.nuxt
.output
.turbo
I krótkiego skryptu:
#!/bin/bash
# scripts/setup-worktree.sh
SOURCE_DIR="$1"
TARGET_DIR="$2"
while read -r file; do
if [ -e "$SOURCE_DIR/$file" ]; then
cp -r "$SOURCE_DIR/$file" "$TARGET_DIR/$file"
fi
done < "$SOURCE_DIR/.worktreeinclude"
3. Odpal dev w worktree
cd ../project-feature-name
npm run dev:worktree
Zarządzanie wieloma worktree'ami
Lista wszystkich worktree
git worktree list
# /Users/you/project abc1234 [dev]
# /Users/you/project-feature def5678 [feature/auth]
# /Users/you/project-bugfix ghi9012 [fix/issue-123]
Usuwanie worktree
Kiedy feature jest gotowy:
# Remove the worktree directory
git worktree remove ../project-feature-name
# If you also want to delete the branch
git branch -d feature/branch-name
Czyszczenie osieroconych worktree
Jeśli skasowałeś katalog worktree ręcznie:
git worktree prune
Typowe problemy
Konflikty portów
Każdy worktree odpala własny dev server. Ustaw różne porty:
// Main repo
"dev:worktree": "nuxt dev --port 3000"
// Worktree 1
"dev:worktree": "nuxt dev --port 3010"
// Worktree 2
"dev:worktree": "nuxt dev --port 3020"
Albo dynamicznie, przez zmienną środowiskową:
PORT=3010 npm run dev:worktree
Problemy z klientem Prismy
Każdy worktree potrzebuje własnej generacji klienta Prismy:
cd ../project-feature-name
npx prisma generate
Właśnie dlatego dev:worktree zawiera npx prisma generate.
Migracje bazy danych
Z migracjami w worktree trzeba uważać. Jeśli siedzisz na branchach z różnymi schematami:
- Używaj osobnej bazy dla branchy eksperymentalnych
- Albo synchronizuj schematy, zanim przeskoczysz między worktree'ami
Czemu nie po prostu branche?
Przełączanie branchy:
- Ubija dev server
- Gubi niezapisany stan w edytorze
- Przebudowuje wszystko od zera
Z worktree:
- Każdy branch ma własny, działający dev server
- Możesz mieć otwartych kilka okien VS Code naraz
- Przełączenie kontekstu jest natychmiastowe
W monorepo, gdzie npm run dev startuje ponad 30 sekund, worktrees oszczędzają godziny.
Docelowy układ
~/project/ # Main repo (dev branch)
├── package.json # dev & dev:worktree scripts
├── turbo.json # Both tasks configured
├── .worktreeinclude # Files to copy to new worktrees
└── apps/
└── main-app/
└── package.json # App-specific worktree script
~/project-feature-auth/ # Worktree (feature/auth branch)
├── node_modules/ # Copied from main
├── .env # Copied from main
└── apps/
└── main-app/
└── .nuxt/ # Regenerated
~/project-bugfix-123/ # Another worktree
└── ...
Ściąga
# Create worktree with new branch
git worktree add ../project-name -b feature/name
# Create worktree on existing branch
git worktree add ../project-name existing-branch
# List worktrees
git worktree list
# Remove worktree
git worktree remove ../project-name
# Run dev in worktree
cd ../project-name && npm run dev:worktree
Jednorazowa konfiguracja zajmuje 10 minut. Zysk na produktywności zostaje na zawsze.
Prawdziwy workflow z monorepo na Nuxcie 3 z Turborepo i Vercelem. Kilka feature'ów rozwijanych równolegle, bez ani jednego przełączania brancha.