Back to all articles
Worktrees: Multiple Workspaces, One Repository

Worktrees: Multiple Workspaces, One Repository

Discover how git worktrees allow you to have multiple working directories (branches) active simultaneously, sharing a single .git repository to save space...

Human-architected research synthesized with the assistance of AI personas.
18 min read

โœจTL;DR / Executive Summary

Discover how git worktrees allow you to have multiple working directories (branches) active simultaneously, sharing a single .git repository to save space...

By Hephaestus, AI Systems Architecture Specialist

๐Ÿ’ก TL;DR (Too Long; Didn't Read)

Git worktrees allow you to have multiple active working directories simultaneously, all sharing the same Git repository. Instead of constantly stashing/checking out or maintaining multiple clones, you can have different branches checked out in separate directories at the same time - perfect for code review while developing, parallel builds of different versions, or emergency hotfixes without interrupting work in progress. Worktrees share Git objects (massive space savings) but maintain independent working directories and staging areas. This feature transforms workflows that previously required complex juggling into simple and elegant operations, especially for parallel development, advanced CI/CD, and situations where context switching is costly.


It's Tuesday, 3:30 PM. You're in the middle of a massive refactoring - files open in 6 editor tabs, tests running, debugger attached, mental model completely loaded in your head.

Slack explodes: "PRODUCTION DOWN! Urgent hotfix needed NOW!"

Your fingers freeze over the keyboard. You know what comes next:

bash
git stash # Save current work git checkout main # Switch to main git pull origin main # Update git checkout -b hotfix-urgent # Create hotfix branch # Solve urgent problem... git checkout feature-refactor # Go back git stash pop # Recover work # Reload mental model... (15 minutes lost)

But what if there was a better way?

bash
# In another terminal, simply: cd ../hotfix-workspace # Already on main, ready to work # Your refactoring remains untouched in the main workspace

Welcome to the world of Git Worktrees - the feature that should be better known but is criminally underutilized. The elegant solution to the context switching problem that every developer faces daily.

The Problem That Worktrees Solve

The Hidden Cost of Context Switching

bash
# Traditional workflow (painful): # 1. Developing feature A vim src/feature-a.js npm test # Mental state: 100% focused on feature A # 2. Interruption: need to review PR git stash # Context loss git checkout pr-branch # Branch switch # Review code... git checkout feature-a # Return git stash pop # Recover context # Mental state: 60% recovered, 40% lost # Time to reload context: 10-15 minutes

Real Cost:

  • Time: 10-15 minutes to reload mental model
  • Flow: Complete break of flow state
  • Errors: Higher probability of stash conflicts
  • Productivity: Studies show 23 minutes to recover full focus

The Temptation of Multiple Clones

bash
# Naive solution: multiple clones git clone repo.git project-main git clone repo.git project-feature git clone repo.git project-hotfix # Problems: # - Disk space: 3x repository size # - Duplicated objects: complete .git in each clone # - Synchronization: fetch/pull in each clone separately # - Confusion: which clone has which state?

For a 1GB repository:

  • 3 clones = 3GB of space
  • Git objects completely duplicated
  • Complete history replicated 3 times

The Elegant Solution: Worktrees

bash
# One repository, multiple working directories project/ โ”œโ”€โ”€ .git/ # Main repository (shared) โ”œโ”€โ”€ main-workspace/ # Worktree 1: main branch โ”œโ”€โ”€ feature-workspace/ # Worktree 2: feature-a branch โ””โ”€โ”€ hotfix-workspace/ # Worktree 3: hotfix-urgent branch # Shared: # - Git objects (commits, trees, blobs) # - Configuration (.git/config) # - Complete history # - Refs and tags # Independent: # - Working directory (files) # - Staging area (index) # - HEAD (current branch)

Anatomy of a Worktree

Internal Structure

bash
# Create first worktree git worktree add ../feature-workspace feature-branch # Resulting structure: .git/ โ”œโ”€โ”€ objects/ # Shared objects โ”œโ”€โ”€ refs/ # Shared refs โ”œโ”€โ”€ worktrees/ # Worktree metadata โ”‚ โ””โ”€โ”€ feature-workspace/ โ”‚ โ”œโ”€โ”€ HEAD # This worktree's HEAD โ”‚ โ”œโ”€โ”€ index # This worktree's staging area โ”‚ โ”œโ”€โ”€ ORIG_HEAD # Backup refs โ”‚ โ””โ”€โ”€ logs/ # Specific reflog โ””โ”€โ”€ config # Shared config # In the worktree directory: ../feature-workspace/ โ”œโ”€โ”€ .git # File (not directory!) โ””โ”€โ”€ [project files] # Contents of .git (text file): # gitdir: /path/to/main/repo/.git/worktrees/feature-workspace

Worktrees vs Clones: Technical Comparison

bash
# Space analysis (example repo: 500MB) # MULTIPLE CLONES: project-clone-1/ โ””โ”€โ”€ .git/ (500MB) # Complete repo project-clone-2/ โ””โ”€โ”€ .git/ (500MB) # Complete repo duplicated project-clone-3/ โ””โ”€โ”€ .git/ (500MB) # Complete repo triplicated Total: 1.5GB # WORKTREES: project/ โ”œโ”€โ”€ .git/ (500MB) # Main repo โ”œโ”€โ”€ worktree-1/ # Only working files โ”œโ”€โ”€ worktree-2/ # Only working files โ””โ”€โ”€ worktree-3/ # Only working files Total: ~520MB (500MB + ~20MB working files) # Savings: ~73% of space!

Basic Worktrees: Fundamentals

Creating Worktrees

bash
# Basic syntax git worktree add <path> [<branch>] # Create worktree from existing branch git worktree add ../feature-workspace feature-branch # Creates directory ../feature-workspace # Checks out feature-branch in it # Create worktree with new branch git worktree add -b new-feature ../new-feature-workspace # Creates new 'new-feature' branch and worktree simultaneously # Create worktree from specific commit (detached HEAD) git worktree add ../review-workspace a1b2c3d # Useful for code review of specific commit # Create worktree from tag git worktree add ../release-workspace v1.2.0 # Useful for checking specific releases

Listing and Managing Worktrees

bash
# List all worktrees git worktree list # /home/user/project a1b2c3d [main] # /home/user/feature-workspace e4f5g6h [feature-branch] # /home/user/hotfix-workspace i7j8k9l [hotfix-urgent] # Detailed format git worktree list --porcelain # worktree /home/user/project # HEAD a1b2c3d7e8f9... # branch refs/heads/main # # worktree /home/user/feature-workspace # HEAD e4f5g6h7i8j9... # branch refs/heads/feature-branch # Information about specific worktree git worktree list | grep feature

Removing Worktrees

bash
# Remove worktree (safe method) git worktree remove feature-workspace # Removes directory and metadata # Remove with force (if there are uncommitted changes) git worktree remove --force feature-workspace # Clean orphaned worktrees (directory deleted manually) git worktree prune # Removes metadata from non-existent worktrees # Check orphaned worktrees before cleaning git worktree prune --dry-run

Moving Worktrees

bash
# Move worktree to new location mv feature-workspace ~/projects/feature-workspace # Update worktree metadata git worktree repair ~/projects/feature-workspace # Git automatically updates internal paths

Professional Workflows with Worktrees

1. Simultaneous Code Review

Scenario: Review PR while continuing to develop.

bash
# Initial setup git worktree add ../review-workspace main # Daily workflow: # Terminal 1: Continuous development cd ~/project git checkout feature-my-work # Develops normally... # Terminal 2: PR Review cd ~/review-workspace git fetch origin pull/123/head:pr-123 git checkout pr-123 # Review code, tests, experiments... # Your work on feature-my-work is NOT affected! # After review: cd ~/review-workspace git checkout main git branch -D pr-123 # Return to development without losing context

Automation Script:

bash
#!/bin/bash # review-pr.sh PR_NUMBER=$1 REVIEW_DIR="$HOME/review-workspace" if [ ! -d "$REVIEW_DIR" ]; then echo "Creating review workspace..." git worktree add "$REVIEW_DIR" main fi cd "$REVIEW_DIR" echo "Fetching PR #$PR_NUMBER..." git fetch origin pull/$PR_NUMBER/head:pr-$PR_NUMBER echo "Checking out PR..." git checkout pr-$PR_NUMBER echo "โœ… Ready for review in $REVIEW_DIR" echo " Run tests: npm test" echo " Start dev: npm start" echo "" echo "When done: cd $REVIEW_DIR && git checkout main && git branch -D pr-$PR_NUMBER"

2. Hotfix Without Interruption

Scenario: Production emergency while developing feature.

bash
# Setup (once) git worktree add ../hotfix-workspace main # During normal development: cd ~/project/feature-workspace # Developing complex feature... # Editor open, debugger running, mental state loaded # EMERGENCY! # In NEW terminal (don't close current): cd ~/project/hotfix-workspace git pull origin main git checkout -b hotfix/critical-bug # Solve critical problem vim src/payment.js npm test git commit -m "fix: resolve payment processing timeout" git push origin hotfix/critical-bug # Create PR, merge, deploy # Return to hotfix-workspace when needed again # Original development was NEVER interrupted! cd ~/project/feature-workspace # Mental state preserved, continues where stopped

3. Parallel Build of Multiple Versions

Scenario: Local CI/CD testing multiple versions.

bash
# Setup worktrees for releases git worktree add ../build-v1 release/v1.0 git worktree add ../build-v2 release/v2.0 git worktree add ../build-v3 main # Parallel build script #!/bin/bash # parallel-build.sh VERSIONS=("build-v1" "build-v2" "build-v3") for version in "${VERSIONS[@]}"; do ( cd "../$version" echo "Building $version..." npm install npm run build npm test echo "โœ… $version complete" ) & done wait echo "๐ŸŽ‰ All versions built successfully"

4. Multi-Feature Development

Scenario: Working on multiple features that can't be merged yet.

bash
# Worktree structure by feature project/ โ”œโ”€โ”€ .git/ โ”œโ”€โ”€ main/ # Main worktree โ”œโ”€โ”€ feature-auth/ # Feature 1: OAuth โ”œโ”€โ”€ feature-payment/ # Feature 2: Payment gateway โ””โ”€โ”€ feature-ui/ # Feature 3: UI redesign # Setup git worktree add main main git worktree add feature-auth feature/oauth git worktree add feature-payment feature/payment git worktree add feature-ui feature/ui-redesign # Each feature in its workspace cd feature-auth && code . # VS Code in feature auth cd feature-payment && code . # VS Code in feature payment cd feature-ui && code . # VS Code in feature ui # Work on each as needed # No context switching, no stash, no conflicts

Advanced Techniques

Temporary Worktrees

bash
# Create temporary worktree for experiment git worktree add --detach ../experiment-workspace # Work on experiment cd ../experiment-workspace # Make experimental changes... # If experiment worked: git checkout -b experiment-success git push origin experiment-success # If it didn't work: cd .. git worktree remove experiment-workspace # Experiment discarded without affecting anything

Worktrees with Sparse Checkout

bash
# For very large repositories git worktree add --no-checkout ../partial-workspace feature-branch cd ../partial-workspace git sparse-checkout init --cone git sparse-checkout set src/specific-module # Only specific module checked out # Massive space and time savings

Worktree Locking

bash
# Prevent accidental removal of important worktree git worktree lock production-workspace # Worktree cannot be removed with 'git worktree remove' # Try to remove: git worktree remove production-workspace # error: 'production-workspace' is locked # Unlock when needed git worktree unlock production-workspace

Worktrees and Hooks

bash
# Hooks are shared between worktrees # .git/hooks/ is unique for entire repo # For worktree-specific hooks: # Use local configuration in each worktree cd feature-workspace git config --local core.hooksPath .git-hooks-feature cd hotfix-workspace git config --local core.hooksPath .git-hooks-hotfix # Each worktree can have different hooks

Tool Integration

Worktrees + IDEs

Visual Studio Code:

bash
# Open multiple VS Code instances code ~/project/main code ~/project/feature-workspace code ~/project/review-workspace # Each independent instance: # - Settings can be different # - Extensions per workspace # - Separate terminal # - Independent debug configurations

JetBrains IDEs (IntelliJ, WebStorm):

bash
# JetBrains IDEs detect worktrees automatically # Shared index for resource savings # Shared cache between worktrees

Worktrees + Docker

bash
# Dockerfile for multi-version development # docker-compose.yml version: '3.8' services: app-v1: build: context: ./build-v1 ports: - "3001:3000" app-v2: build: context: ./build-v2 ports: - "3002:3000" app-main: build: context: ./main ports: - "3003:3000" # Test 3 versions simultaneously! docker-compose up

Worktrees + CI/CD

yaml
# .github/workflows/multi-version-test.yml name: Multi-Version Testing on: [push, pull_request] jobs: test-versions: runs-on: ubuntu-latest strategy: matrix: version: [v1.0, v2.0, main] steps: - uses: actions/checkout@v3 - name: Setup worktrees run: | git worktree add ../test-${{ matrix.version }} ${{ matrix.version }} - name: Test version run: | cd ../test-${{ matrix.version }} npm install npm test

Performance and Optimization

Performance Analysis

bash
#!/bin/bash # worktree-performance.sh echo "๐Ÿ“Š Worktree Performance Analysis" # Creation time time git worktree add ../perf-test feature-branch # Space used echo "Space usage:" du -sh .git/worktrees/ # Number of worktrees worktree_count=$(git worktree list | wc -l) echo "Active worktrees: $worktree_count" # Shared objects echo "Shared objects:" git count-objects -v # Cleanup and optimization git worktree prune git gc --aggressive

Optimizations

bash
# 1. Shared object cache git config core.sharedRepository group # Allows efficient sharing between worktrees # 2. Shared references git config extensions.worktreeConfig true # Enables more efficient per-worktree configuration # 3. Optimized garbage collection git config gc.worktreePruneExpire "30.days.ago" # Cleans orphaned worktrees after 30 days

Troubleshooting

Common Problems

1. Branch Locked (in use by another worktree):

bash
git checkout feature-branch # error: 'feature-branch' is already checked out at '../other-workspace' # Solution: branch can only be in one worktree at a time # Option A: Use another branch git checkout -b feature-branch-v2 feature-branch # Option B: Remove worktree using it git worktree remove other-workspace

2. Orphaned Worktree:

bash
# Directory was deleted but metadata remains git worktree list # /path/to/deleted-workspace (gone) # Clean metadata git worktree prune # Check before cleaning git worktree prune --dry-run

3. Conflicting Configurations:

bash
# Global configuration affects all worktrees # For specific configuration: cd specific-worktree git config --local user.email "[email protected]" git config --local core.editor "nano" # Check effective configuration git config --list --show-origin

Diagnostic Script

bash
#!/bin/bash # worktree-health-check.sh echo "๐Ÿ” Worktree Health Check" echo "=======================" # 1. List all worktrees echo "๐Ÿ“‹ Active worktrees:" git worktree list # 2. Check orphaned worktrees echo "" echo "๐Ÿ—‘๏ธ Orphaned worktrees:" git worktree prune --dry-run # 3. Check space echo "" echo "๐Ÿ’พ Disk usage:" echo "Main repo: $(du -sh .git | cut -f1)" echo "Worktrees metadata: $(du -sh .git/worktrees 2>/dev/null | cut -f1)" # 4. Check integrity echo "" echo "๐Ÿ”’ Integrity check:" git fsck --worktrees # 5. Branches in use echo "" echo "๐ŸŒฟ Branches checked out:" git worktree list | awk '{print $3}' | sed 's/\[//;s/\]//' # 6. Recommendations echo "" echo "๐Ÿ’ก Recommendations:" orphaned=$(git worktree prune --dry-run | wc -l) if [ $orphaned -gt 0 ]; then echo " - Run 'git worktree prune' to clean orphaned worktrees" fi worktree_count=$(git worktree list | wc -l) if [ $worktree_count -gt 10 ]; then echo " - Consider removing unused worktrees (you have $worktree_count)" fi

Organization Strategies

Naming Conventions

bash
# Organized worktree structure project/ โ”œโ”€โ”€ main/ # Main branch โ”œโ”€โ”€ develop/ # Develop branch โ”œโ”€โ”€ features/ โ”‚ โ”œโ”€โ”€ auth-oauth/ # Feature worktrees โ”‚ โ”œโ”€โ”€ payment-stripe/ โ”‚ โ””โ”€โ”€ ui-redesign/ โ”œโ”€โ”€ hotfixes/ โ”‚ โ””โ”€โ”€ critical-bug/ # Hotfix worktrees โ”œโ”€โ”€ releases/ โ”‚ โ”œโ”€โ”€ v1.0/ # Release worktrees โ”‚ โ”œโ”€โ”€ v2.0/ โ”‚ โ””โ”€โ”€ v3.0/ โ””โ”€โ”€ reviews/ โ””โ”€โ”€ pr-123/ # PR review worktrees

Organized Setup Script

bash
#!/bin/bash # setup-worktree-structure.sh PROJECT_ROOT=$(git rev-parse --show-toplevel) WORKTREE_ROOT="$PROJECT_ROOT/../worktrees" # Create directory structure mkdir -p "$WORKTREE_ROOT"/{features,hotfixes,releases,reviews} # Main and develop git worktree add "$WORKTREE_ROOT/main" main git worktree add "$WORKTREE_ROOT/develop" develop echo "โœ… Worktree structure created at $WORKTREE_ROOT" echo "" echo "To add feature worktree:" echo " git worktree add $WORKTREE_ROOT/features/my-feature feature/my-feature" echo "" echo "To add hotfix worktree:" echo " git worktree add $WORKTREE_ROOT/hotfixes/urgent-fix main"

Cleanup Automation

bash
#!/bin/bash # cleanup-old-worktrees.sh echo "๐Ÿงน Cleaning old worktrees..." # Find worktrees without recent commits git worktree list --porcelain | while read line; do if [[ $line == worktree\ * ]]; then worktree_path=${line#worktree } if [ -d "$worktree_path" ]; then cd "$worktree_path" # Last commit more than 30 days ago? last_commit=$(git log -1 --format=%ct 2>/dev/null) current_time=$(date +%s) days_old=$(( ($current_time - $last_commit) / 86400 )) if [ $days_old -gt 30 ]; then echo "โš ๏ธ $worktree_path: $days_old days old" read -p "Remove? (y/N): " confirm if [[ $confirm =~ ^[Yy]$ ]]; then cd - git worktree remove "$worktree_path" echo "โœ… Removed $worktree_path" fi fi fi fi done # Clean orphans git worktree prune echo "โœ… Cleanup complete"

Advanced Use Cases

1. Local A/B Testing

bash
# Test two implementations side by side git worktree add ../implementation-a feature/approach-a git worktree add ../implementation-b feature/approach-b # Terminal 1: cd ../implementation-a && npm start -- --port 3001 # Terminal 2: cd ../implementation-b && npm start -- --port 3002 # Browser: # http://localhost:3001 - Approach A # http://localhost:3002 - Approach B # Compare performance, UX, results

2. Database Migration Testing

bash
# Test migrations on multiple versions git worktree add ../db-current main git worktree add ../db-migration feature/db-changes # Current version cd ../db-current docker-compose up db npm run migrate # New migration cd ../db-migration docker-compose up db npm run migrate # Compare schemas, test rollback

3. Documentation Synchronization

bash
# Keep docs synchronized with code git worktree add ../docs-workspace gh-pages # Development cd ~/project/main # Develop feature... # Update docs simultaneously cd ~/project/docs-workspace # Update documentation... git commit -m "docs: update for new feature" git push origin gh-pages # Deploy docs independent of code

Conclusion: The Power of Parallelization

Worktrees represent a fundamental shift in how we think about context switching in Git. It's not just a "useful feature" - it's an architectural transformation that eliminates one of the biggest friction points in modern development.

The Fundamental Lessons

1. Context Switching Has Real Cost:

bash
# Before worktrees: # Time to switch context: 10-15 minutes # Flow lost: Irrecoverable # Productivity: -40% per interruption # With worktrees: # Time to switch context: 0 seconds (new terminal) # Flow lost: Zero # Productivity: Maintained at 100%

2. Disk Space is Precious:

bash
# Multiple clones: # 500MB ร— 5 = 2.5GB # Worktrees: # 500MB + (5 ร— 20MB) = 600MB # Savings: 76%

3. Parallelization is Power:

bash
# One developer can: # - Develop feature # - Review PR # - Do hotfix # - Test release # All simultaneously, without conflicts

When to Use Worktrees

โœ… Use Worktrees For:

  • Frequent code reviews during development
  • Urgent hotfixes without interrupting work
  • Parallel builds of multiple versions
  • Side-by-side comparison of implementations
  • Independent feature development
  • Testing multiple branches simultaneously

โŒ Avoid Worktrees For:

  • Short-lived branches (< 1 hour)
  • When stash is sufficient
  • Sharing between machines (worktrees are local)
  • When disk space is extremely limited

The Workflow Transformation

bash
# Old workflow (friction): Develop โ†’ Interruption โ†’ Stash โ†’ Checkout โ†’ Solve โ†’ Checkout โ†’ Unstash โ†’ Reload context # Workflow with worktrees (flow): Develop in workspace-1 โ†’ New terminal โ†’ Solve in workspace-2 โ†’ Continue development

Integration with Modern Tools

Worktrees integrate perfectly with:

  • Modern IDEs: VS Code, IntelliJ, multiple instances
  • Docker: Parallel containers of different versions
  • CI/CD: Simultaneous branch testing
  • Monorepos: Independent modular development

Success Metrics

After adopting worktrees, teams report:

  • 60-80% reduction in time lost to context switching
  • 30-50% increase in completed code reviews
  • 70% decrease in stash conflicts
  • 40% improvement in emergency response capability

The Future is Parallel

Git worktrees aren't just a technical feature - they're a development philosophy:

"Why choose between tasks when you can work on all simultaneously?"

In a world where:

  • Interruptions are constant
  • Code reviews are essential
  • Hotfixes can't wait
  • Development is increasingly parallel

Worktrees aren't luxury - they're necessity.

First Steps

If you've never used worktrees, start simple:

bash
# 1. Create worktree for reviews git worktree add ../review main # 2. Use when you need to review PR cd ../review git fetch origin pull/123/head:pr-123 git checkout pr-123 # Review... # 3. Return to development cd - # Your work is untouched!

Once you experience the frictionless workflow, you'll never go back to constant stash/checkout.

Resources and Documentation

bash
# Essential commands to memorize: git worktree add <path> <branch> # Create git worktree list # List git worktree remove <path> # Remove git worktree prune # Clean orphans # Official documentation: git help worktree

The Philosophical Conclusion

At the beginning of this article, you were stuck in the paradigm of "one working directory at a time". Now you understand that Git allows multiple simultaneous contexts, efficiently sharing what should be shared (objects, history) and keeping independent what should be independent (working directory, staging).

Worktrees are the materialization of the principle that good tools should adapt to your workflow, not force you to adapt your workflow to the tool.

Context switching is inherent to modern development. But its cost doesn't have to be.

"The best tool is the one that gets out of your way and lets you work in parallel when you need to."

Now you have that power. Use it without moderation.


Questions for AI Reflection

  1. Worktrees vs multiple clones: technical advantages performance space

  2. Professional worktree workflows: code review parallel development hotfixes

  3. Worktree integration IDEs Docker CI/CD: configuration optimization

  4. Troubleshooting worktrees: common problems resolution branch locking

  5. Worktree organization: directory structure naming conventions cleanup automation

Receive new articles

Subscribe to receive notifications about new articles directly to your email

We won't send spam. You can unsubscribe at any time.