Skip to content

Instantly share code, notes, and snippets.

@ashwch
Last active November 23, 2025 13:42
Show Gist options
  • Select an option

  • Save ashwch/946ad983977c9107db7ee9abafeb95bd to your computer and use it in GitHub Desktop.

Select an option

Save ashwch/946ad983977c9107db7ee9abafeb95bd to your computer and use it in GitHub Desktop.
Git Worktrees: From Zero to Hero - A comprehensive guide to using Git worktrees with submodules

Git Worktrees: From Zero to Hero

Table of Contents

  1. What Are Git Worktrees? (First Principles)
  2. The Problem Worktrees Solve
  3. How Worktrees Work
  4. Worktrees + Submodules: Monolith Setup
  5. Practical Tutorial: Creating Your First Worktree
  6. Advanced Workflows
  7. Best Practices
  8. Troubleshooting
  9. For Non-Technical Stakeholders

What Are Git Worktrees? (First Principles)

The Traditional Git Model

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.

The Worktree Model

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

Key Insight

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 .git repository)

The Problem Worktrees Solve

Without Worktrees (Traditional Workflow)

# 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 off

Problems:

  • 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

With Worktrees

# 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

How Worktrees Work

The Git Magic

When you create a worktree, Git:

  1. Creates a new directory with all your project files
  2. Links it to the main repository (shares Git history)
  3. Checks out a specific branch in that directory
  4. Prevents conflicts (same branch can't be checked out twice)

Under the Hood

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

The "One Branch Per Worktree" Rule

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 out

This prevents confusion and conflicts.


Worktrees + Submodules: Monolith Setup

What Makes This Setup Special

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

The Challenge

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

Automation Solution

Create automation scripts to handle this complexity:

Visual Example

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-123 branch
  • Each submodule can be on different branches
  • This lets you work on related changes across multiple repositories

Practical Tutorial: Creating Your First Worktree

Prerequisites

# Make sure you're in the main repository
cd /path/to/your-monolith

# Check current setup
git status
git worktree list

Method 1: Using Automated Script (Recommended)

# Run smart worktree creation script
python scripts/create_worktree.py
# or with uv: uv run scripts/create_worktree.py

What this script does:

  1. Asks for a worktree name (suggests smart defaults)
  2. Shows available branches for main repo (with search)
  3. For each submodule, lets you choose which branch to use
  4. Creates the worktree one directory above current location
  5. Initializes all submodules with your chosen branches
  6. 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

Method 2: Manual Creation (Educational)

Step 1: Create the Basic Worktree

# Create worktree in parent directory
git worktree add ../monolith-feature-oauth feature/oauth-integration

# Navigate to the new worktree
cd ../monolith-feature-oauth

Step 2: Initialize Submodules

# 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)"'

Step 3: Set Up Submodule Branches (Optional)

# 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 ..

Verify Your Setup

# 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)"'

Advanced Workflows

Working Across Multiple Worktrees

Scenario: Comparing Two Feature Implementations

# 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

Scenario: AI-Assisted Development

# 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

Managing Submodule Changes

Making Changes Across Multiple Repos

# 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

Updating Submodules from Remote

# Use update script
python scripts/update_submodules.py

# Or manually
git submodule update --recursive --remote

Cleaning Up Worktrees

When Feature is Complete

# List all worktrees
git worktree list

# Remove completed worktree
git worktree remove ../monolith-feature-123

# Clean up any leftover references
git worktree prune

Best Practices

Naming Conventions

# 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

Directory Organization

# Recommended structure
projects/
├── monolith/                    ← Main repository
├── monolith-feature-auth/       ← Feature worktree
├── monolith-feature-payments/   ← Another feature
└── monolith-hotfix-urgent/      ← Hotfix worktree

Branch Strategy

# 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

Workflow Tips

1. One Worktree Per Feature

  • Don't reuse worktrees for different features
  • Create new worktrees for each major task
  • Clean up when done

2. Initialize Submodules Immediately

# Always run after creating worktree
git submodule update --init --recursive

3. Use Automation Scripts

# 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

4. Regular Cleanup

# Weekly cleanup of old worktrees
git worktree list
git worktree remove ../old-worktree-name
git worktree prune

Troubleshooting

Common Issues and Solutions

Issue: "Branch is already checked out"

# 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

Issue: Submodules Not Initialized

# Symptoms: Empty submodule directories
ls frontend/  # Empty

# Solution: Initialize submodules
git submodule update --init --recursive

Issue: Submodule in Wrong Branch

# 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 ..

Issue: Can't Remove Worktree

# Error: worktree contains modified or untracked files
git worktree remove ../old-worktree

# Solution: Force removal (loses uncommitted changes!)
git worktree remove --force ../old-worktree

Issue: Submodule Conflicts During Merge

# 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"

Performance Tips

Large Repository Optimization

# Speed up submodule operations
git config submodule.recurse true
git config diff.submodule log
git config status.submodulesummary 1

Disk Space Management

# Share objects between worktrees (done automatically)
# But be aware: multiple worktrees = multiple working copies

# Check disk usage
du -sh ../monolith*

For Non-Technical Stakeholders

What This Means for You

The Problem We Solved

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:

  1. Save Chapter 1 work
  2. Switch to Chapter 5
  3. Save Chapter 5 work
  4. Switch to Chapter 3
  5. Go back and forth constantly

This is inefficient and error-prone.

Our Solution: Git Worktrees

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.

Business Benefits

1. Faster Development

  • No context switching delays - developers stay in flow
  • Parallel development - multiple features simultaneously
  • Reduced merge conflicts - isolated work streams

2. Better Quality

  • Side-by-side comparisons - easy to evaluate approaches
  • Isolated testing - test features without affecting others
  • Safer experimentation - try ideas without risk

3. Enhanced Collaboration

  • AI tools work better - can analyze multiple implementations
  • Code reviews easier - compare implementations directly
  • Knowledge sharing - team members can see different approaches

Impact on Project Timelines

Before Worktrees

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

With Worktrees

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

What You'll Notice

As a Product Manager

  • 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

As a Designer

  • Quick iterations - developers can implement multiple design approaches
  • Live comparisons - see different implementations running simultaneously
  • Faster feedback loops - no delay switching between versions

As a Stakeholder

  • 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

Summary

Git worktrees with submodules give us superpowers:

  1. Multiple parallel universes of our codebase
  2. No context switching between features
  3. AI tools work better with isolated environments
  4. Complex projects simplified through automation
  5. Team productivity increased through better workflows

Key Commands to Remember

# 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

Next Steps

  1. Try creating your first worktree with our script
  2. Experiment with parallel development
  3. Use worktrees for your next feature
  4. Share this guide with your team
  5. Contribute improvements to automation scripts

This guide covers Git worktrees with submodules for modern development workflows. Questions? Contributions welcome!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment