Appearance
Git Worktree Development
Git worktrees enable you to work on multiple branches simultaneously without constantly switching contexts. This is particularly useful for the Jubiloop monorepo where builds can take time and environment files need to be maintained.
What Are Git Worktrees?
Git worktrees allow you to have multiple working directories from the same repository, each checked out to different branches. Instead of switching branches in place (and potentially losing uncommitted work or having to rebuild), you can have separate directories for each feature you're working on.
Benefits for Jubiloop Development
- Parallel Development: Work on multiple features simultaneously without context switching
- Preserve Build Artifacts: Each worktree maintains its own
node_modulesand build outputs - Source Isolation: Each worktree has its own checkout and
.envfiles - Quick Bug Fixes: Jump to a bugfix without stashing or committing work-in-progress
- Testing Different Versions: Compare behavior across branches side-by-side
- Faster Context Switching: Source and host build outputs remain in place; container dependencies may still need reinstalling when branches differ
Quick Start
Initial Setup
First, make the worktree scripts executable:
bash
./scripts/setup-scripts.shThis will:
- Check for required dependencies (git, pnpm)
- Create the worktrees directory structure
- Set executable permissions on all scripts
- Display available commands
Creating Your First Worktree
Create a new worktree for a feature branch:
bash
./scripts/worktree.sh feature/sc-187-new-featureThis automatically:
- Creates a new branch from
developwhen run in the main worktree; from a secondary worktree it defaults to that worktree's current branch - Sets up a worktree in
../jubiloop-worktrees/feature-sc-187-new-feature - Copies all
.envfiles from the main worktree - Copies supported gitignored local editor and agent settings when present
- Opens the worktree in your preferred editor
Worktree Management Scripts
worktree.sh - Create and Set Up Worktrees
Creates a new worktree with automatic environment configuration.
bash
./scripts/worktree.sh <branch-name> [options]Options:
-b, --base <branch>- Base for a new branch. Defaults todevelopin the main worktree and to the current branch in a secondary worktree-e, --copy-env-from <worktree>- Copy .env files from specified worktree (default: main)-n, --no-editor- Don't open the worktree in an editor-i, --install- Run pnpm install after creating worktree-o, --open-dir- Open worktree directory in file manager
For a new branch, the script fetches first and prefers origin/<base> when that remote branch exists; otherwise it uses the local base. The base option has no effect when opening an existing local or remote branch.
Examples:
bash
# Create feature branch from develop
./scripts/worktree.sh feature/sc-123-user-auth
# Create from specific base branch
./scripts/worktree.sh hotfix/critical -b main
# Create with pnpm install and open in file manager
./scripts/worktree.sh feature/new-ui -i -o
# Copy environment from another worktree
./scripts/worktree.sh feature/test -e feature/sc-123-user-authworktree-remove.sh - Safely Remove Worktrees
Removes worktrees with safety checks for uncommitted changes.
bash
./scripts/worktree-remove.sh <branch-name> [options]Options:
-f, --force- Skip confirmation prompts (use with caution)
Safety Features:
- Warns about uncommitted changes
- Warns about unpushed commits
- Asks for confirmation before deletion
- Optionally deletes the associated branch
- Cleans up orphaned worktree registrations
Example:
bash
# Remove with confirmations
./scripts/worktree-remove.sh feature/sc-123-user-auth
# Automated cleanup only after independently checking status and upstream commits
./scripts/worktree-remove.sh feature/old-feature -fworktree-list.sh - View All Worktrees
Lists all worktrees with their status and configuration.
bash
./scripts/worktree-list.sh [options]Options:
-v, --verbose- Show detailed environment file information
Information Displayed:
- Branch name and location
- Current worktree indicator
- Clean/dirty status (uncommitted changes)
- Environment file presence
- Last commit information
Visual Indicators:
[MAIN]- Main worktree← YOU ARE HERE- Current worktree✓- Present/clean⚠- Warning/partial✗- Missing/error
Example:
bash
# Basic list
./scripts/worktree-list.sh
# Detailed view with env file sizes and line counts
./scripts/worktree-list.sh -vworktree-switch.sh - Quick Switch Between Worktrees
Opens a different worktree in your editor without manual navigation.
bash
./scripts/worktree-switch.sh <branch-name>Features:
- Automatically opens in Zed or VS Code
- Shows worktree status before switching
- Displays current commit information
- Lists available worktrees if branch not found
Example:
bash
# Switch to another worktree
./scripts/worktree-switch.sh develop
# Switch to main worktree
./scripts/worktree-switch.sh mainCommon Workflows
Working on Multiple Features
When you need to work on several features simultaneously:
bash
# Create worktrees for each feature
./scripts/worktree.sh feature/sc-100-api-endpoints -i
./scripts/worktree.sh feature/sc-101-ui-components -i
./scripts/worktree.sh feature/sc-102-documentation
# List all worktrees to see status
./scripts/worktree-list.sh
# Switch between them as needed
./scripts/worktree-switch.sh feature/sc-100-api-endpointsEmergency Hotfix
When you need to fix a critical bug while working on a feature:
bash
# Create hotfix worktree from main
./scripts/worktree.sh hotfix/critical-bug -b main -i
# After fixing and pushing
./scripts/worktree-remove.sh hotfix/critical-bug
# Return to your feature
./scripts/worktree-switch.sh feature/sc-100-api-endpointsTesting Changes Across Branches
Compare behavior between branches one at a time with the shared Docker stack:
bash
# Set up worktrees for comparison
./scripts/worktree.sh develop -i
./scripts/worktree.sh feature/new-implementation -i
# Stop containers created from the current worktree without deleting volumes
pnpm run dev:stack:down
# Open the comparison worktree in your editor
./scripts/worktree-switch.sh feature/new-implementation
# In this terminal, explicitly enter that worktree before starting its stack
cd ../jubiloop-worktrees/feature-new-implementation
pnpm run dev:stack:upworktree-switch.sh opens an editor and prints the destination path; it cannot change the current directory of the terminal that invoked it.
The Compose project uses fixed container names, host ports, and named volumes, so two full-stack worktrees cannot run at the same time. The PostgreSQL, Redis, and container node_modules volumes are shared. docker compose restart only restarts existing containers and does not change their source bind mount; stop and recreate the containers when changing worktrees.
Code Review Preparation
Review code locally before creating a PR:
bash
# Create worktree for the branch to review
./scripts/worktree.sh feature/sc-200-colleague-branch
# Open in editor to review
# The worktree is automatically opened
# Clean up after review
./scripts/worktree-remove.sh feature/sc-200-colleague-branchEnvironment File Management
The scripts automatically handle environment files to ensure each worktree is ready to run:
Automatic Copying
When creating a worktree, the following files are copied:
apps/server/.envapps/webapp/.envapps/marketing/.env
Local Configurations
The script copies these gitignored machine-local files when present:
.claude/settings.local.json.zed/settings.json.cursor/rules/better_auth.mdc
Committed editor and agent files already come from the checked-out branch and are not overwritten.
HTTPS Certificates
The generated infra/local_dev/certs/ directory is gitignored and is not copied by the worktree script. Before running Docker in a new worktree, run its infra/local_dev/setup-https.sh or copy the certificate directory from a trusted local worktree. The mkcert CA trust is machine-wide, but each worktree's Caddy container bind-mounts certificate files from that worktree.
Fallback Behavior
If .env files don't exist in the source:
- Falls back to
.env.examplefiles - Warns you about missing configurations
- Creates basic
.envfrom examples
Directory Structure
Worktrees are organized in a sibling directory to keep the main repository clean:
parent-directory/
├── jubiloop/ # Main repository
│ ├── apps/
│ ├── packages/
│ └── scripts/
│ ├── worktree.sh
│ ├── worktree-remove.sh
│ ├── worktree-list.sh
│ └── worktree-switch.sh
│
└── jubiloop-worktrees/ # All worktrees
├── feature-sc-100-api/ # Each branch gets its own directory
├── feature-sc-101-ui/ # Branch name with slashes replaced
└── hotfix-critical-bug/ # Completely isolated workspaceBest Practices
When to Use Worktrees
Good Use Cases:
- Working on long-running features
- Reviewing colleague's code
- Testing changes that require different configurations
- Emergency fixes while preserving work in progress
- Comparing behavior between versions
When NOT to Use:
- Quick one-line fixes (just use regular branch switching)
- When disk space is very limited
- For branches you'll only visit once briefly
Naming Conventions
Follow the standard branch naming:
feature/sc-XXX-descriptionfor featuresbugfix/sc-XXX-descriptionfor bugshotfix/descriptionfor urgent fixes
The scripts automatically sanitize branch names for filesystem compatibility.
Maintenance
Regular Cleanup:
bash
# List worktrees to see what's active
./scripts/worktree-list.sh
# Remove completed feature worktrees
./scripts/worktree-remove.sh feature/completed-feature
# Inspect registrations; use the project removal script for cleanup
./scripts/worktree-list.sh -vDisk Space: Each worktree includes:
- Full copy of source code
- Separate
node_modules(can be 1-2GB) - Build artifacts
Consider removing unused worktrees to save space.
Troubleshooting
"Worktree already exists"
If you get this error, the worktree directory already exists:
bash
# Check existing worktrees
./scripts/worktree-list.sh
# Remove if needed
./scripts/worktree-remove.sh <branch-name>Missing Environment Files
If .env files aren't copied:
bash
# Manually copy from main worktree
cp ../jubiloop/apps/server/.env ../jubiloop-worktrees/your-branch/apps/server/.envEditor Not Opening
If the editor doesn't open automatically:
bash
# Check installed editors
which zed
which code
# Manually open
code ../jubiloop-worktrees/your-branchOrphaned Worktrees
If a worktree directory was manually deleted:
bash
# Use the project script; it cleans a missing registered path when it can match the branch
./scripts/worktree-remove.sh <branch-name>The removal script's checks have blind spots: git diff-index does not report every untracked file, and a branch without an upstream can make the unpushed-commit check inconclusive. Before confirming removal, run git status --short in the target worktree and verify its upstream and commits. Avoid --force unless losing the directory contents is intentional.
The script also does not stop the shared Docker stack or determine which worktree its bind mounts reference. If the target worktree owns the running stack, run pnpm run dev:stack:down from that worktree before removal. This preserves the shared named data volumes while preventing containers from retaining mounts to a removed directory.
Branch Conflicts
If the branch already exists:
bash
# The script will use the existing branch
./scripts/worktree.sh existing-branch
# Or create with a different name
./scripts/worktree.sh feature/sc-XXX-alternativeAdvanced Usage
Custom Base Branches
Create worktrees from any branch or commit:
bash
# From a specific remote branch
./scripts/worktree.sh feature/new -b origin/experimental
# From the current branch (the default when run inside a secondary worktree)
cd ../jubiloop-worktrees/feature-base
./scripts/worktree.sh feature/extensionEnvironment Variations
Test with different configurations:
bash
# Create worktree
./scripts/worktree.sh feature/test-config
# Modify environment for this specific worktree
cd ../jubiloop-worktrees/feature-test-config
vim apps/server/.env # Edit for different API endpointParallel Testing
Use separate worktrees for repeatable comparisons, but run the shared full stack from only one at a time:
bash
# In the active worktree
pnpm run dev:stack:down
# In the destination worktree; this recreates containers with its bind mounts
pnpm run dev:stack:upFor truly simultaneous comparisons, use host mode with explicit, non-conflicting ports for every app and dependency. The standard Docker configuration does not support that topology.
Tips and Tricks
Tab Completion: Use tab completion with branch names for faster navigation
Aliases: Add to your shell config:
bashalias wt='./scripts/worktree.sh' alias wts='./scripts/worktree-switch.sh' alias wtl='./scripts/worktree-list.sh' alias wtr='./scripts/worktree-remove.sh'Quick Status Check: Run
./scripts/worktree-list.shregularly to track active workShared pnpm Store: pnpm's content-addressable store is shared between worktrees, speeding up installs
Git Commands: Regular git commands work in any worktree:
bashcd ../jubiloop-worktrees/feature-branch git add . git commit -m "[sc-123] implement feature" git push origin feature-branch
Summary
Git worktrees provide a powerful way to manage parallel development in the Jubiloop monorepo. The custom scripts handle the complexity of environment setup, making it easy to:
- Work on multiple features without context switching
- Preserve build state and configurations
- Quickly respond to urgent fixes
- Review code without disrupting your work
Use worktrees whenever you need to maintain multiple working states or when switching branches would be disruptive to your current work.
Related Documentation: