Worktree Operations
Detecting a Worktree
You're in a worktree if:
- •Path contains
claude-worktrees(e.g.,~/claude-worktrees/tennis-team/my-feature-123456) - •Path is outside the main project directory but contains project files
- •Branch name starts with
worktree/
Worktrees are isolated checkouts used for parallel development without affecting the main checkout.
Initial Setup (After Worktree Creation)
When a worktree is first created, run these commands:
# Install dependencies with pnpm (runs automatically via worktree creation script) pnpm install # Start Docker infrastructure (PostgreSQL + Redis) pnpm docker:infra # Run database migrations pnpm --filter @tennis/backend run db:migrate
Tennis-Team Project Structure
tennis-team/ ├── apps/ │ ├── backend/ # Express API server (@tennis/backend) │ ├── frontend/ # React frontend app (@tennis/frontend) │ └── website/ # Public website (@tennis/website) ├── packages/ │ └── shared/ # Shared types and utilities (@tennis/shared) ├── docker/ │ ├── docker-compose.yml # Infrastructure services │ └── docker-compose.dev.yml # Dev overrides ├── pnpm-workspace.yaml ├── turbo.json └── package.json
Referencing Packages
Use workspace protocol in package.json:
{
"dependencies": {
"@tennis/shared": "workspace:*"
}
}
Docker Infrastructure Setup
The tennis-team project uses Docker for PostgreSQL and Redis.
Starting Infrastructure
# Start PostgreSQL + Redis (from project root) pnpm docker:infra # Start full dev environment (all services) pnpm docker:dev # Check container status docker compose -f docker/docker-compose.yml ps
Verifying Services
# Check PostgreSQL psql -h localhost -U postgres -c "SELECT 1;" # Check Redis redis-cli ping # Should return: PONG
Troubleshooting Docker
# Containers not starting docker compose -f docker/docker-compose.yml logs postgres docker compose -f docker/docker-compose.yml logs redis # Reset containers docker compose -f docker/docker-compose.yml down -v pnpm docker:infra
Database Setup in Worktrees
Worktrees can use either a shared database (default) or an isolated database (for schema testing).
Shared Database (Default)
Uses the same database as your main development environment. No additional setup needed.
# Your .env already points to the shared dev database # Just ensure Docker is running and start developing! pnpm docker:infra pnpm run dev
Isolated Database (For Schema Testing)
When you need a separate database (for migrations, schema changes, or isolated testing), use the --isolated flag when creating the worktree:
# Via worktree manager .claude/skills/claude-worktree-manager/scripts/worktree.sh create my-feature --isolated
This automatically:
- •Creates a new PostgreSQL database:
tennis_worktree_<timestamp> - •Updates your
.envwith the newDATABASE_URL - •Runs all migrations via
pnpm --filter @tennis/backend run db:migrate
Manual Database Operations
# Run migrations pnpm --filter @tennis/backend run db:migrate # Connect to database psql -h localhost -U postgres -d tennis_dev # List all databases psql -h localhost -U postgres -c "\l"
Isolated DB Cleanup
# List worktree databases psql -h localhost -U postgres -c "SELECT datname FROM pg_database WHERE datname LIKE 'tennis_worktree_%';" # Drop a specific worktree database psql -h localhost -U postgres -c "DROP DATABASE tennis_worktree_<timestamp>;"
When to Use Isolated Database
Use isolated database when:
- •Testing new migrations before merging
- •Developing schema changes
- •Running destructive tests
- •Need a clean database state
Skip isolated database when:
- •Normal feature development
- •Bug fixes
- •Frontend work
- •Changes that don't touch the database
Redis Cache Management
Redis is used for caching. In worktrees:
# Check Redis connection redis-cli ping # Clear all cache (development only) redis-cli FLUSHALL # Monitor Redis activity redis-cli MONITOR
Worktrees share the same Redis instance by default. If you need isolation, use a different Redis database number in your .env:
REDIS_URL="redis://localhost:6379/1" # Use DB 1 instead of default 0
Tennis-Team Dev Workflow
Starting Development
# Start infrastructure first pnpm docker:infra # Start all apps in dev mode (backend + frontend + website) pnpm dev # Or start specific apps pnpm --filter @tennis/backend run dev pnpm --filter @tennis/frontend run dev
Common Commands (pnpm + turbo)
Building
# Build all packages pnpm run build # Build a specific package pnpm --filter @tennis/backend run build pnpm --filter @tennis/shared run build # Build with turbo (uses caching) pnpm turbo run build # Force rebuild (ignore cache) pnpm turbo run build --force
Testing
# Run all tests pnpm test # Run tests for a specific package pnpm --filter @tennis/backend run test # Run with turbo (respects dependencies) pnpm turbo run test
Development
# Start development mode (all apps) pnpm run dev # Run linting pnpm run lint # Type checking pnpm run typecheck
pnpm and Turbo in Monorepos
pnpm Workspace Architecture
How pnpm Works:
- •Content-addressable store at
~/.pnpm-store - •Symlinked
node_modules(not flat like npm) - •Shared dependencies across all worktrees
- •Hard links from store to project
node_modules
Directory Structure:
project/ ├── node_modules/ │ ├── .pnpm/ # Actual packages (symlinked) │ ├── @tennis/shared -> .pnpm/... # Workspace packages │ └── react -> .pnpm/... # External packages ├── apps/ │ ├── backend/ │ │ └── node_modules/ -> ../../node_modules │ └── frontend/ │ └── node_modules/ -> ../../node_modules └── pnpm-lock.yaml # Lock file (CRITICAL)
Dependency Installation Patterns
Pattern 1: Root Level Dependencies (Shared)
# Install dependency for all packages pnpm add -w typescript # -w = workspace root # Install dev dependency at root pnpm add -D -w eslint
Pattern 2: Package-Specific Dependencies
# Install in specific package pnpm --filter @tennis/backend add express # Install dev dependency in package pnpm --filter @tennis/backend add -D jest
Pattern 3: Workspace Protocol Dependencies
// apps/backend/package.json
{
"dependencies": {
"@tennis/shared": "workspace:*"
}
}
Lock File Management
Understanding pnpm-lock.yaml
Critical File: pnpm-lock.yaml must be committed and kept in sync.
Lock File Rules:
- •Commit
pnpm-lock.yamlto git - •Never manually edit lock file
- •Run
pnpm installafter pulling changes - •Don't ignore lock file in
.gitignore
Handling Lock File Conflicts
# During merge conflict - accept theirs and regenerate (RECOMMENDED) git checkout --theirs pnpm-lock.yaml pnpm install # Regenerates lock file with your changes # After resolving git add pnpm-lock.yaml git commit --no-edit
Common pnpm Issues in Worktrees
Issue 1: .pnpm-install.pid Blocking Operations
# Before merging branches rm -f .pnpm-install.pid # If locked by process lsof .pnpm-install.pid # Find process using file kill <PID> rm .pnpm-install.pid
Issue 2: node_modules Symlink Confusion
# Workspace packages symlink ls -la node_modules/@tennis/shared # -> ../packages/shared
Issue 3: Workspace Dependencies Not Updating
# Rebuild affected packages pnpm --filter @tennis/shared run build # Or rebuild everything pnpm turbo run build --force
Issue 4: Different pnpm Versions
# Check required version cat package.json | jq .packageManager # Install correct version globally npm install -g pnpm@9.15.4 # Or use Corepack (recommended) corepack enable corepack prepare pnpm@9.15.4 --activate
Turbo Build Caching
Turbo in Worktrees
Issue: Turbo cache is worktree-local
# Workaround: Share cache between worktrees export TURBO_CACHE_DIR=~/.turbo-cache
Cache Invalidation
# Clear turbo cache pnpm turbo run build --force # Bypass cache # Delete cache directory rm -rf node_modules/.cache/turbo
Debugging Turbo
# See what turbo is doing pnpm turbo run build --dry-run # Verbose output pnpm turbo run build --verbose # Show cache hits/misses pnpm turbo run build --summarize
pnpm Commands Reference
Installation
# Install all dependencies (respects lock file) pnpm install # Install with frozen lock file (CI mode) pnpm install --frozen-lockfile # Force reinstall everything pnpm install --force
Adding Dependencies
# Add to root workspace pnpm add -w <package> # Add to specific package pnpm --filter <package-name> add <dependency> # Add with version constraint pnpm add react@^18.0.0
Removing Dependencies
# Remove from specific package pnpm --filter <package-name> remove <dependency> # Remove from workspace root pnpm remove -w <package>
Workspace Commands
# Run script in all packages pnpm -r run build # -r = recursive # Run in specific package pnpm --filter @tennis/backend run test # Run in packages matching glob pnpm --filter "./apps/*" run start # Run with dependencies first pnpm --filter @tennis/frontend... run build # ... = include dependencies
Best Practices for pnpm in Worktrees
- •
Keep lock file in sync
bashgit pull origin main pnpm install
- •
Clean install for stale worktrees
bashrm -rf node_modules rm pnpm-lock.yaml git checkout main -- pnpm-lock.yaml pnpm install
- •
Use consistent pnpm version
json{ "packageManager": "pnpm@9.15.4" } - •
Ignore temporary files
gitignore.pnpm-install.pid .pnpm-debug.log node_modules/.cache/
- •
Use filters for focused work
bashpnpm --filter @tennis/backend... run build pnpm turbo run test --filter=[HEAD^1]
- •
Handle lock file conflicts properly
bashgit checkout --theirs pnpm-lock.yaml pnpm install git add pnpm-lock.yaml
- •
Clean up before merging
bashrm -f .pnpm-install.pid rm -rf node_modules/.cache pnpm install pnpm run build
- •
Don't mix npm and pnpm
bash# Always use pnpm in this project pnpm install
Troubleshooting pnpm Issues
"EBUSY: resource busy or locked"
pkill -f pnpm rm .pnpm-install.pid pnpm install
"Cannot find module '@tennis/shared'"
pnpm --filter @tennis/shared run build pnpm run build
"integrity check failed"
rm pnpm-lock.yaml pnpm install git add pnpm-lock.yaml
Common Git Worktree Workflows
Workflow 1: Merging Worktree Branch to Main (via PR)
# 1. Ensure branch is pushed cd ~/claude-worktrees/tennis-team/my-feature-1234567 git push -u origin HEAD # 2. Create PR using gh CLI gh pr create \ --base main \ --title "feat: my feature" \ --body "Summary of changes" # 3. After merge, clean up worktree cd /path/to/main/repo git worktree remove ~/claude-worktrees/tennis-team/my-feature-1234567 git fetch origin --prune
Workflow 2: Syncing Worktree with Main Branch
# 1. Navigate to worktree cd ~/claude-worktrees/tennis-team/my-feature-1234567 # 2. Stash any uncommitted changes git stash push -m "WIP: before sync" # 3. Fetch latest from origin git fetch origin main # 4. Rebase onto main (cleaner history) git rebase origin/main # 5. Restore stashed changes git stash pop # 6. Rebuild dependencies and code pnpm install pnpm run build
Workflow 3: Handling Stale Worktrees
Option A: Update and Continue Working
cd ~/claude-worktrees/tennis-team/my-old-feature # 1. Fetch latest git fetch origin --prune # 2. Rebase or merge latest changes git rebase origin/main # 3. Update dependencies (critical for stale worktrees) pnpm install # 4. Rebuild everything pnpm turbo run build --force # 5. Start Docker infrastructure pnpm docker:infra # 6. Run migrations pnpm --filter @tennis/backend run db:migrate
Option B: Abandon and Remove
# From main repo directory cd /path/to/main/repo # 1. Remove worktree (even if dirty) git worktree remove ~/claude-worktrees/tennis-team/my-old-feature --force # 2. Delete local branch git branch -D worktree/my-old-feature # 3. Delete remote branch if exists git push origin --delete worktree/my-old-feature # 4. Prune remote references git fetch origin --prune
Workflow 4: Environment Synchronization
# Get main repo path MAIN_REPO=/path/to/main/repo # Copy environment files cp $MAIN_REPO/.env .env # Copy Claude settings cp $MAIN_REPO/.claude/settings.local.json .claude/settings.local.json
What NOT to Sync:
- •
node_modules/- Should be installed per worktree - •
dist/,build/- Build artifacts are worktree-specific - •
.pnpm-install.pid- Process-specific temporary files
Workflow 5: Safe Worktree Removal
Before Removing - Checklist:
cd ~/claude-worktrees/tennis-team/my-feature # 1. Check if there are uncommitted changes git status # 2. Check if branch is pushed git log origin/HEAD..HEAD # Empty = all commits are pushed # If unpushed commits: git push origin HEAD
Removal Steps:
# From main repo (NOT from worktree directory) cd /path/to/main/repo # 1. Remove worktree git worktree remove ~/claude-worktrees/tennis-team/my-feature # If worktree is dirty: git worktree remove ~/claude-worktrees/tennis-team/my-feature --force # 2. Delete branch (if no longer needed) git branch -D worktree/my-feature # 3. Delete remote branch git push origin --delete worktree/my-feature # 4. Prune git fetch origin --prune git worktree prune
Conflict Resolution Patterns
1. Package Lock File Conflicts
# Accept theirs and regenerate (RECOMMENDED) git checkout --theirs pnpm-lock.yaml pnpm install git add pnpm-lock.yaml git commit --no-edit
2. .pnpm-install.pid Conflicts
# This file should NEVER be committed rm -f .pnpm-install.pid git add .pnpm-install.pid 2>/dev/null || true
3. Environment File Conflicts
# Keep your worktree's environment git checkout --ours .env git add .env
4. Build Artifact Conflicts
# Delete and rebuild rm -rf apps/*/dist packages/*/dist git add -A pnpm turbo run build --force
TypeScript Module Resolution Errors After Merge
# Solution 1: Force Rebuild with Turbo pnpm turbo run build --force # Solution 2: Clean Install and Build rm -rf node_modules rm -rf apps/*/node_modules packages/*/node_modules rm -rf apps/*/dist packages/*/dist pnpm install pnpm turbo run build --force # Solution 3: Build Specific Package First pnpm --filter @tennis/shared run build pnpm turbo run build
Complete Merge Workflow Example
# 1. Pre-merge cleanup rm -f .pnpm-install.pid git stash push -m "WIP before merge" 2>/dev/null || true # 2. Fetch and merge git fetch origin main git merge origin/main --no-edit # 3. If conflicts occur: # Handle lock file if git diff --name-only --diff-filter=U | grep -q "pnpm-lock.yaml"; then git checkout --theirs pnpm-lock.yaml fi # Handle .env (keep ours) if git diff --name-only --diff-filter=U | grep -q ".env"; then git checkout --ours .env fi # Remove temp files from conflicts rm -f .pnpm-install.pid git add pnpm-lock.yaml .env 2>/dev/null || true git commit --no-edit 2>/dev/null || true # 4. Reinstall and rebuild pnpm install pnpm turbo run build --force # 5. Restore stashed changes git stash pop 2>/dev/null || true # 6. Verify pnpm run typecheck echo "Merge complete!"
Best Practices Summary
- •Always remove
.pnpm-install.pidbefore merging - •Use
git checkout --theirs pnpm-lock.yamlfor lock conflicts - •Keep your
.envfile (usegit checkout --ours .env) - •Run
pnpm turbo run build --forceafter every merge - •Use
pnpm installbefore building (lock file may have changed) - •Don't manually edit
pnpm-lock.yaml - •Don't commit build artifacts (
dist/) - •Don't skip the rebuild step after merging
Worktree Branch Naming Conventions
# Feature development worktree/feature-name-<timestamp> # Bug fixes worktree/fix-bug-description-<timestamp> # Environment-specific worktree/staging-env-<timestamp>
Troubleshooting Common Issues
Issue: "fatal: 'branch' is already checked out"
# You can't checkout same branch in multiple worktrees # Solution: Create new branch from it git worktree add ~/worktrees/new -b new-branch existing-branch
Issue: Worktree directory deleted but git still tracks it
git worktree prune
Issue: Cannot remove worktree - "uncommitted changes"
git worktree remove ~/claude-worktrees/tennis-team/feature --force
Issue: Merge conflicts in multiple files
# Abort and rebase instead git merge --abort git fetch origin git rebase origin/main
Cleanup
When done with a worktree:
- •Push any unpushed commits
- •Remove the worktree:
git worktree remove <path> - •Delete the branch:
git branch -D <branch> - •If isolated DB was used, drop it:
psql -h localhost -U postgres -c "DROP DATABASE tennis_worktree_<timestamp>;" - •Prune:
git worktree prune && git fetch origin --prune