Skip to content

Parallel Git Worktrees with Claude Code: Run Multiple AI Agents Simultaneously

I was stuck. I had Claude Code running in one terminal, happily working on a feature, when I got a Slack notification about a critical bug. I needed to switch context immediately, but I didn’t want to lose my AI assistant’s context. Do I stash? Commit half-done work? Or just open a new terminal and… wait, that won’t work either because they’d both be editing the same files.

This was the moment I realized I needed git worktrees.

The Context Switching Problem

Traditional git branching forces you into sequential work:

  1. Start feature A, make progress
  2. Need to fix bug B? Stash or commit A
  3. Switch branches, fix bug
  4. Switch back, remember where you were

With Claude Code, this problem compounds. The AI has built up context about your current work—variable names, patterns you’ve established, the direction you’re heading. When you switch branches, all that context is lost. You have to re-explain everything.

I tried multiple solutions:

Attempt 1: Multiple terminals, same directory

  • Opened two terminal tabs
  • Ran Claude Code in both
  • Disaster: Both instances edited the same files simultaneously
  • Conflict after conflict

Attempt 2: Git stash dance

  • Stash work, switch branches, fix bug
  • Switch back, pop stash
  • Problem: Lost Claude’s mental model of the code
  • Had to re-explain the entire feature context

Attempt 3: Quick commits

  • Commit work-in-progress with “WIP” message
  • Switch branches, fix bug
  • Switch back, amend or reset
  • Problem: Dirty git history, and still lost AI context

Then I remembered something from an X thread about Claude Code tips: Boris (the Claude Code creator) and his team use git worktrees with 5+ parallel Claude instances in numbered terminal tabs.

What Are Git Worktrees?

Git worktrees let you have multiple working directories from a single repository. Each worktree:

  • Has its own checked-out branch
  • Shares the same .git directory (history, refs, config)
  • Operates independently of other worktrees

Think of it like this:

Traditional Approach:
┌─────────────────────────────────┐
│ my-app/ │
│ ├── .git/ │
│ ├── src/ │ ← Only ONE branch at a time
│ └── package.json │
└─────────────────────────────────┘
Git Worktrees Approach:
┌─────────────────────────────────┐
│ my-app/ │
│ ├── .git/ │ ← Shared history
│ ├── src/ │ ← main branch
│ └── package.json │
└─────────────────────────────────┘
┌─────────────────────────────────┐
│ my-app-auth/ │
│ ├── src/ │ ← auth-feature branch
│ └── package.json │
└─────────────────────────────────┘
┌─────────────────────────────────┐
│ my-app-api/ │
│ ├── src/ │ ← api-refactor branch
│ └── package.json │
└─────────────────────────────────┘

Each directory is a full working copy. You can have VS Code open in one, Claude Code running in another, and tests executing in a third—all on different branches, all from the same repository.

Setting Up Parallel Claude Code Sessions

Step 1: Create Worktrees for Each Task

I started with a simple structure:

Terminal window
# From my main project directory
cd ~/projects/my-app
# Create worktree for auth feature
git worktree add ../my-app-auth -b auth-feature
# Create worktree for API refactor
git worktree add ../my-app-api -b api-refactor
# Create worktree for critical bugfix
git worktree add ../my-app-hotfix -b critical-bug

The -b flag creates a new branch. If you have an existing branch:

Terminal window
git worktree add ../my-app-existing existing-branch

Step 2: Open Terminal Tabs for Each Worktree

Here’s where Boris’s team’s numbering strategy comes in:

┌─────────────────────────────────────────┐
│ Tab 01: my-app (main branch) │
├─────────────────────────────────────────┤
│ Tab 02: my-app-auth (auth-feature) │
├─────────────────────────────────────────┤
│ Tab 03: my-app-api (api-refactor) │
├─────────────────────────────────────────┤
│ Tab 04: my-app-hotfix (critical-bug) │
└─────────────────────────────────────────┘

I use a naming convention: ##-project-branch to keep track.

Step 3: Launch Claude Code in Each Tab

Terminal window
# In each terminal tab:
cd ~/projects/my-app-auth
claude
# Or with specific instructions:
claude --instructions "Focus on authentication flow, no API changes"

Now I have four independent Claude Code instances, each with its own context, working directory, and focus area.

The Productivity Boost

This changed my workflow completely.

Before: Context switch overhead of 5-15 minutes per switch. Re-explaining to Claude what we were doing. Mental model fragmentation.

After: Instant context availability. Each Claude instance knows exactly what it’s working on. No stash/pop dance.

Here’s how I work now:

Morning:
├── Tab 01 (main): Code review, merge PRs
├── Tab 02 (auth-feature): Implementing OAuth flow
├── Tab 03 (api-refactor): Refactoring REST endpoints
└── Tab 04 (hotfix): Emergency bug fix
Afternoon:
├── Auth feature done → Merge PR, close tab
├── Hotfix deployed → Merge PR, close tab
├── Tab 05 (test-coverage): New worktree for tests
└── Continue on API refactor

The key insight: I never lose context. Claude in Tab 02 remembers the authentication flow decisions. Claude in Tab 03 knows the API refactor strategy. They don’t interfere with each other.

Common Pitfalls I Hit

Pitfall 1: Too Many Worktrees

I went overboard initially and created 8 worktrees. Bad idea.

  • Too many terminal tabs to track
  • Disk space usage grew (each worktree has its own node_modules)
  • Lost track of which Claude was working on what

Solution: I limit myself to 3-5 active worktrees. When one task is done, I clean it up before creating another.

Pitfall 2: Forgetting to Clean Up

After a month, I had 12 worktrees accumulated from abandoned experiments.

Terminal window
# Check what you have
git worktree list
# My embarrassing output:
/home/user/projects/my-app abc123 [main]
/home/user/projects/my-app-auth def456 [auth-feature] # merged 2 weeks ago
/home/user/projects/my-app-test ghi789 [test-coverage] # abandoned
/home/user/projects/my-app-exp1 jkl012 [experiment-1] # ??? no idea
...

Solution: Weekly cleanup routine:

Terminal window
# Remove worktrees for merged branches
for wt in $(git worktree list --porcelain | grep "worktree" | cut -d' ' -f2); do
branch=$(git -C "$wt" branch --show-current)
if git branch --merged main | grep -q "$branch"; then
echo "Merged: $branch - removing worktree"
git worktree remove "$wt"
git branch -d "$branch"
fi
done
# Clean up any stale references
git worktree prune

Pitfall 3: Shared Build Artifacts

I ran npm install in the main directory, then tried to run the app in a worktree. Didn’t work—each worktree needs its own dependencies.

Terminal window
# In EACH worktree:
npm install
# or
pnpm install

This also means each worktree has its own node_modules directory. Plan for disk space.

Pitfall 4: No Naming Convention

Initially I used random names:

Terminal window
git worktree add ../temp-stuff
git worktree add ../working-here
git worktree add ../testing-thing

Three days later, I couldn’t remember which was which.

Solution: Descriptive names with project prefix:

Terminal window
git worktree add ../myapp-auth-feature
git worktree add ../myapp-api-refactor
git worktree add ../myapp-bug-1234

A Practical Example: Emergency Hotfix

Here’s how worktrees saved me during a production incident:

Scenario: 2pm, working on a feature branch. Production goes down.

Without worktrees (old way):

  1. Stash current work
  2. Commit WIP changes
  3. Switch to main
  4. Create hotfix branch
  5. Fix issue
  6. Switch back
  7. Unstash/pop
  8. Try to remember where I was

With worktrees:

  1. Cmd+T to open new terminal tab
  2. cd ~/projects/myapp-hotfix
  3. claude --instructions "Fix production bug, no new features"
  4. Claude already knows the codebase from shared git history
  5. Fix, test, commit, push
  6. Close tab
  7. Continue in original tab like nothing happened

Time saved: ~20 minutes of context recovery.

Automating Worktree Setup

I created helper scripts to streamline this:

Setup Script

setup-worktrees.sh
#!/bin/bash
PROJECT_NAME="myapp"
BASE_DIR=~/projects
# Define worktrees: branch:description
declare -a WORKTREES=(
"auth-feature:Authentication flow improvements"
"api-refactor:API endpoint optimization"
"test-coverage:Increase test coverage to 80%"
)
for item in "${WORKTREES[@]}"; do
IFS=':' read -r branch desc <<< "$item"
worktree_path="$BASE_DIR/${PROJECT_NAME}-${branch}"
echo "Creating worktree: $worktree_path"
echo " Branch: $branch"
echo " Purpose: $desc"
git worktree add "$worktree_path" -b "$branch"
done
echo ""
echo "Worktrees created:"
git worktree list

Launch Claude Sessions

#!/bin/bash
# launch-claude.sh - Open Claude in each worktree
WORKTREES=$(git worktree list --porcelain | grep "worktree" | cut -d' ' -f2)
for wt in $WORKTREES; do
branch=$(git -C "$wt" branch --show-current)
echo "Opening Claude for: $branch"
# macOS Terminal
osascript -e "tell application \"Terminal\" to do script \"cd $wt && claude\""
done

When Worktrees Don’t Make Sense

Worktrees aren’t always the answer. I avoid them for:

  • Quick single-file fixes: Not worth the setup
  • Docs-only changes: Just switch branches
  • Reviewing PRs: Check out the PR branch in main worktree
  • Learning/experimenting with small changes: Overkill

Rule of thumb: If I’m going to spend more than 30 minutes on a task, I consider a worktree.

Integration with IDEs

VS Code

Each worktree opens as a separate VS Code window:

Terminal window
# Open worktree in new VS Code window
code -n ~/projects/myapp-auth-feature

I use the VS Code “Project Manager” extension to quickly switch between them.

JetBrains IDEs (IntelliJ, WebStorm)

Same approach—each worktree is a separate project:

Terminal window
# Open in IntelliJ
idea ~/projects/myapp-auth-feature

Why This Matters for AI-Assisted Development

The real power emerges when combining worktrees with Claude Code:

Traditional development: Sequential, context-switching, mentally draining

Parallel AI development: Concurrent, isolated, context-preserved

Traditional:
Time →
[Feature A] ──── stash ──── [Bug fix] ──── unstash ──── [Feature A continues]
╰──────────────────────────────────────────────────────────╯
Context switching overhead
Parallel Worktrees:
Time →
[Feature A] ════════════════════════════════════════════════════▶
[Bug fix] ═════════════════════▶
[Feature B] ══════════════════════════════════════════════════▶
╰─────────────────────────────────────────────────────╯
Each Claude instance maintains its own context

Summary

Git worktrees solved my biggest pain point with AI-assisted development: context switching. Instead of losing Claude’s understanding every time I needed to work on something else, I can maintain multiple parallel contexts.

Key practices I follow:

  1. Limit to 3-5 active worktrees — beyond that, it’s chaos
  2. Use descriptive namesproject-feature-description format
  3. Weekly cleanup — remove merged/abandoned worktrees
  4. Number terminal tabs — matches Boris’s team approach
  5. Each worktree gets its own dependencies — plan disk space

The setup takes 2-3 minutes, but saves hours of context recovery. When a production issue hits, I can spin up a hotfix worktree, let Claude work its magic, and continue my feature work uninterrupted.

Final Words + More Resources

My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments