- What Are Git Worktrees? (First Principles)
- The Problem Worktrees Solve
- How Worktrees Work
- Worktrees + Submodules: Monolith Setup
- Practical Tutorial: Creating Your First Worktree
- Advanced Workflows
- Best Practices
- Troubleshooting
- For Non-Technical Stakeholders
Normally, when you work with Git, you have:
- One working directory (the folder with your code)
- One active branch at a time
- Switching branches changes all files in that directory
my-project/
├── src/
├── README.md
└── .git/
When you run git checkout feature-branch, ALL files change to match that branch.
Git worktrees let you have:
- Multiple working directories from the same repository
- Different branches checked out simultaneously
- Independent work in each directory
my-project/ ← Main repo (main branch)
├── src/
├── README.md
└── .git/
my-project-feature-1/ ← Worktree 1 (feature-1 branch)
├── src/
├── README.md
└── .git ← File pointing to main repo
my-project-feature-2/ ← Worktree 2 (feature-2 branch)
├── src/
├── README.md
└── .git ← File pointing to main repo
Think of worktrees as parallel universes of your code:
- Each universe (worktree) shows your project at a different point in time (branch/commit)
- Changes in one universe don't affect the others
- All universes share the same Git history (they're connected to the same
.gitrepository)
# Working on feature A
git checkout feature-a
# Make changes, test, etc.
# Need to quickly check feature B
git stash # Save current work
git checkout feature-b # Switch context
# Look at feature B code
# Back to feature A
git checkout feature-a
git stash pop # Restore work
# Continue where you left offProblems:
- Context switching overhead
- Risk of losing uncommitted work
- Can't compare branches side-by-side
- Can't run tests on multiple branches simultaneously
- Interrupts your flow
# Create separate worktrees
git worktree add ../project-feature-a feature-a
git worktree add ../project-feature-b feature-b
# Work in parallel
cd ../project-feature-a # Work on feature A
cd ../project-feature-b # Work on feature B (in another terminal)Benefits:
- No context switching
- Work on multiple features simultaneously
- Compare code side-by-side
- Run different tests in parallel
- Perfect for AI tools like Claude Code
When you create a worktree, Git:
- Creates a new directory with all your project files
- Links it to the main repository (shares Git history)
- Checks out a specific branch in that directory
- Prevents conflicts (same branch can't be checked out twice)
main-repo/
└── .git/
├── objects/ ← All Git data (shared)
├── refs/ ← Branch references (shared)
└── worktrees/ ← Worktree metadata
├── worktree-1/
└── worktree-2/
worktree-1/
├── your-files/ ← Working files (independent)
└── .git ← File pointing to main-repo/.git
worktree-2/
├── your-files/ ← Working files (independent)
└── .git ← File pointing to main-repo/.git
Git prevents the same branch from being checked out in multiple worktrees:
# This works
git worktree add ../repo-feature-a feature-a
# This fails if feature-a is already checked out
git worktree add ../repo-feature-a-copy feature-a
# Error: 'feature-a' is already checked outThis prevents confusion and conflicts.
A monolith repository can contain multiple repositories as submodules:
monolith/
├── frontend/ ← Git submodule (separate repo)
├── backend/ ← Git submodule (separate repo)
├── infrastructure/ ← Git submodule (separate repo)
├── shared-components/ ← Git submodule (separate repo)
└── .gitmodules ← Submodule configuration
When you create a worktree of a repository with submodules:
- The main repository gets a new worktree
- The submodules need to be initialized separately
- Each submodule can be on different branches in different worktrees
Create automation scripts to handle this complexity:
create_worktree.py- Creates worktrees with proper submodule setupupdate_submodules.py- Manages submodule updates
monolith/ ← Main repo (main branch)
├── frontend/ (main branch)
├── backend/ (main branch)
└── infrastructure/ (main branch)
monolith-feature-123/ ← Worktree (feature-123 branch)
├── frontend/ (feature-123-frontend branch)
├── backend/ (main branch)
└── infrastructure/ (feature-123-infra branch)
Notice how:
- Main repo worktree is on
feature-123branch - Each submodule can be on different branches
- This lets you work on related changes across multiple repositories
# Make sure you're in the main repository
cd /path/to/your-monolith
# Check current setup
git status
git worktree list# Run smart worktree creation script
python scripts/create_worktree.py
# or with uv: uv run scripts/create_worktree.pyWhat this script does:
- Asks for a worktree name (suggests smart defaults)
- Shows available branches for main repo (with search)
- For each submodule, lets you choose which branch to use
- Creates the worktree one directory above current location
- Initializes all submodules with your chosen branches
- Handles conflicts gracefully
Example interaction:
Enter worktree name (e.g., 'feature-auth'): feature-oauth
Creating worktree: ../monolith-feature-oauth
Select branch for main repository:
[1] main
[2] feature/oauth-integration
[3] develop
Choice: 2
Select branch for frontend submodule:
[1] main
[2] feature/oauth-ui
[3] develop
Choice: 2
... (continues for each submodule)
✅ Worktree created successfully!
📁 Location: ../monolith-feature-oauth
🌳 Branch: feature/oauth-integration
# Create worktree in parent directory
git worktree add ../monolith-feature-oauth feature/oauth-integration
# Navigate to the new worktree
cd ../monolith-feature-oauth# Initialize all submodules (this takes time)
git submodule update --init --recursive
# Check what branches submodules are on
git submodule foreach 'echo "Submodule: $name, Branch: $(git branch --show-current)"'# Switch submodules to feature branches if needed
cd frontend
git checkout feature/oauth-ui
cd ../backend
git checkout main # or whatever branch you need
cd ../infrastructure
git checkout feature/oauth-config
cd ..# Check worktree status
git worktree list
# Check branch in main repo
git branch --show-current
# Check submodule branches
git submodule foreach 'echo "$name: $(git branch --show-current)"'# Create two worktrees for comparison
python scripts/create_worktree.py # Create approach-a
python scripts/create_worktree.py # Create approach-b
# Work in parallel
cd ../monolith-approach-a
# Implement solution A
cd ../monolith-approach-b
# Implement solution B
# Compare side-by-side in your IDE
code ../monolith-approach-a ../monolith-approach-b# Create worktree for AI session
python scripts/create_worktree.py
# In the new worktree, run Claude Code
cd ../monolith-feature-ai-assist
claude
# Meanwhile, continue manual work in main repo
cd /path/to/monolith
# Continue your work without interruption# In your worktree
cd ../monolith-feature-123
# Make changes in frontend
cd frontend
git checkout -b feature-123-frontend
# Make changes
git add . && git commit -m "Frontend changes for feature 123"
git push origin feature-123-frontend
# Make changes in backend
cd ../backend
git checkout -b feature-123-backend
# Make changes
git add . && git commit -m "Backend changes for feature 123"
git push origin feature-123-backend
# Update monolith to point to new commits
cd ..
git add frontend backend
git commit -m "Update submodules for feature 123"
git push origin feature-123# Use update script
python scripts/update_submodules.py
# Or manually
git submodule update --recursive --remote# List all worktrees
git worktree list
# Remove completed worktree
git worktree remove ../monolith-feature-123
# Clean up any leftover references
git worktree prune# Good worktree names
../monolith-feature-auth # Feature-based
../monolith-bugfix-12345 # Issue-based
../monolith-hotfix-critical # Purpose-based
../monolith-experiment-new-arch # Experiment-based
# Avoid
../temp # Too generic
../test # Unclear purpose
../monolith # Confusing with main# Recommended structure
projects/
├── monolith/ ← Main repository
├── monolith-feature-auth/ ← Feature worktree
├── monolith-feature-payments/ ← Another feature
└── monolith-hotfix-urgent/ ← Hotfix worktree# Main repository branches
main ← Production
develop ← Integration
feature/auth-system ← Feature branch
hotfix/critical-bug ← Hotfix branch
# Submodule branches (can be independent)
frontend: feature/auth-ui
backend: feature/auth-api
infrastructure: main ← Unchanged for this feature- Don't reuse worktrees for different features
- Create new worktrees for each major task
- Clean up when done
# Always run after creating worktree
git submodule update --init --recursive# Let automation handle complexity
python create_worktree.py # Download from: https://gist.github.com/ashwch/79177b4af7f2ea482418d6e9934d4787
python update_submodules.py # Download from: https://gist.github.com/ashwch/909ea473250e8c8a937a8a4aa4a4dc72# Weekly cleanup of old worktrees
git worktree list
git worktree remove ../old-worktree-name
git worktree prune# Error when trying to create worktree
fatal: 'main' is already checked out at '/path/to/monolith'
# Solution: Use a different branch or create new branch
git worktree add ../monolith-new -b new-branch-name main# Symptoms: Empty submodule directories
ls frontend/ # Empty
# Solution: Initialize submodules
git submodule update --init --recursive# Check current branches
git submodule foreach 'echo "$name: $(git branch --show-current)"'
# Fix: Switch to correct branch
cd problematic-submodule
git checkout correct-branch
cd ..# Error: worktree contains modified or untracked files
git worktree remove ../old-worktree
# Solution: Force removal (loses uncommitted changes!)
git worktree remove --force ../old-worktree# When merging between branches with different submodule commits
# Solution: Choose which submodule commits to keep
git add problematic-submodule
git commit -m "Resolve submodule conflicts"# Speed up submodule operations
git config submodule.recurse true
git config diff.submodule log
git config status.submodulesummary 1# Share objects between worktrees (done automatically)
# But be aware: multiple worktrees = multiple working copies
# Check disk usage
du -sh ../monolith*Imagine you're writing a document and need to:
- Work on Chapter 1 revisions
- Also draft Chapter 5
- Compare two different approaches for Chapter 3
Traditionally, you'd have to:
- Save Chapter 1 work
- Switch to Chapter 5
- Save Chapter 5 work
- Switch to Chapter 3
- Go back and forth constantly
This is inefficient and error-prone.
Now we can have:
- Document-v1/ folder for Chapter 1 work
- Document-v2/ folder for Chapter 5 work
- Document-experiment/ folder for Chapter 3 approaches
All folders stay synchronized with the main document, but you can work on different parts independently.
- No context switching delays - developers stay in flow
- Parallel development - multiple features simultaneously
- Reduced merge conflicts - isolated work streams
- Side-by-side comparisons - easy to evaluate approaches
- Isolated testing - test features without affecting others
- Safer experimentation - try ideas without risk
- AI tools work better - can analyze multiple implementations
- Code reviews easier - compare implementations directly
- Knowledge sharing - team members can see different approaches
Week 1: Start Feature A
Week 2: Urgent bug interrupt → Switch context → Fix bug
Week 3: Return to Feature A → Re-understand code → Continue
Week 4: Complete Feature A
Total: 4 weeks for Feature A + 1 bug fix
Week 1: Start Feature A (main worktree)
Week 2: Urgent bug → Create hotfix worktree → Fix bug in parallel
Week 3: Complete Feature A (uninterrupted) + Complete bug fix
Week 4: Start Feature B
Total: 3 weeks for Feature A + 1 bug fix + start Feature B
- Faster feature delivery - less time lost to context switching
- Better estimations - developers can work more predictably
- Easier demonstrations - can show multiple versions side-by-side
- Quick iterations - developers can implement multiple design approaches
- Live comparisons - see different implementations running simultaneously
- Faster feedback loops - no delay switching between versions
- More reliable timelines - fewer interruptions and delays
- Higher quality output - easier to compare and choose best solutions
- Reduced risk - experiments don't affect main development
Git worktrees with submodules give us superpowers:
- Multiple parallel universes of our codebase
- No context switching between features
- AI tools work better with isolated environments
- Complex projects simplified through automation
- Team productivity increased through better workflows
# Create worktree (automated)
python scripts/create_worktree.py
# List worktrees
git worktree list
# Remove worktree
git worktree remove ../worktree-name
# Update submodules
python scripts/update_submodules.py- Try creating your first worktree with our script
- Experiment with parallel development
- Use worktrees for your next feature
- Share this guide with your team
- Contribute improvements to automation scripts
This guide covers Git worktrees with submodules for modern development workflows. Questions? Contributions welcome!