The Single Checkout Bottleneck
I'm developing a macOS menu bar app. I have three features in the backlog: a consumption sparkline, native notifications, and a desktop widget. All three are independent. I'm building all three with Claude Code.
The problem: Claude Code works in one directory. One directory has one branch. And git checkout is like a single-lane roundabout: only one gets through.
If I want to advance all three simultaneously, my classic options are:
Stash ping-pong:
git stash, switch branches, work,git stash pop, pray there are no conflicts. Repeat until madness or retirement, whichever comes first.Clone the repo three times: Works, but now I have three
.git/copies, three independent histories, and agit fetchto do in each one. Wasteful.Accept serial life: One feature after another. Safe, predictable, and slow as a hand-written merge sort.
None of these are great. But there's a fourth option that's been in git since 2015 and almost nobody uses.
Worktrees: The Solution You Already Had Installed
A worktree is a second working directory that shares the same .git repository. No copies, no clones, no black magic.
The analogy: your repo is a library. Until now you had one desk where you could only have one book open. A worktree is adding more desks. Each with a different book open, but all drawing from the same bookshelf.
~/code/myapp/ ← desk 1 (main)
.git/ ← the library (just one)
~/code/myapp-sparkline/ ← desk 2 (feature/sparkline)
.git ← file, not folder (pointer to library)
~/code/myapp-notifications/ ← desk 3 (feature/notifications)
.git ← another pointer
Each directory is a complete checkout with all files. You can compile in one, run tests in another, and have your AI agent working in the third. Simultaneously.
Creating One is a Single Line
From your main repo:
git worktree add ../myapp-sparkline -b feature/sparkline
git worktree add ../myapp-notifications -b feature/notifications
Done. Two new directories, each on its branch, sharing the entire git database. No cloning, no configuring remotes, no duplicating history.
What They Share and What They Don't
This is important. Worktrees share the entire repo: commits, branches, tags, remotes, hooks, configuration. If you commit in the sparkline worktree, you can see it immediately from the notifications one without doing fetch or anything, because it's the same database.
What they don't share:
- Files on disk (each desk has its working copy)
- The staging area (each has its own
git add) - The HEAD (each points to its branch)
Simply put: the "what am I working on" state is private to each worktree. Everything else is shared.
The Workflow with Coding Agents
This is where it gets interesting. With worktrees, you can literally have multiple agents working in parallel on the same project:
# Terminal 1: Claude Code on sparkline
cd ~/code/myapp-sparkline
claude
# Terminal 2: Claude Code on notifications
cd ~/code/myapp-notifications
claude
# Terminal 3: main intact, app running
cd ~/code/myapp
make run
Each Claude instance has its own directory, its own branch, its own .build/. They don't step on each other. They don't compete for the index. They don't need to stash anything.
And since they share the git database, when one agent finishes and pushes, the others already see that branch.
Merging: Exactly the Same as Always
Worktrees don't change the merge workflow at all. They're normal branches in separate directories:
# Option A: local merge
cd ~/code/myapp
git merge feature/sparkline
git merge feature/notifications
# Option B: PRs (usual approach)
cd ~/code/myapp-sparkline
git push -u origin feature/sparkline
# Create PR in GitHub/Gitea, review, merge
When you're done, clean up:
git worktree remove ../myapp-sparkline
git branch -d feature/sparkline # if already merged
The Pitfalls Nobody Tells You About
1. One Branch, One Worktree
You can't have main checked out in two worktrees simultaneously. This is by design: it prevents two directories from modifying the same HEAD and corrupting each other. If you need a second checkout of main, create a temporary branch.
2. The First Build is From Scratch
Each worktree has its own build directory. The first compilation will be slow. After that, each worktree maintains its independent cache, which is precisely the advantage over classic git checkout (which invalidates the cache every time you switch branches).
3. Local Untracked Files
Your .env.local, editor configurations, files not in git... don't get copied to the new worktree. You'll need to recreate them or make symlinks.
4. Apps with Shared Disk State
If your app writes data to ~/Library/Application Support/ or similar, two app instances from different worktrees will compete for the same file. This isn't a worktree problem, it's a problem of running two instances of the same app. Solution: don't run two simultaneously, or parameterize the data directory per build.
5. Don't Delete the Directory Manually
If you rm -rf the worktree instead of using git worktree remove, git still thinks the branch is occupied. Run git worktree prune to clean up orphaned references.
6. The Remote Knows Nothing
Worktrees are 100% local. Gitea, GitHub, GitLab... no remote knows they exist. They only see normal git push commands with normal branches. It's like asking if your server has problems with you using Vim or VS Code: it doesn't know, it doesn't care.
Best Practices
Naming convention: Put worktrees as siblings of the original repo, with a descriptive suffix:
~/code/myapp/ ← main
~/code/myapp-sparkline/ ← feature
~/code/myapp-notifications/ ← feature
~/code/myapp-hotfix-login/ ← hotfix
This way ls ~/code/myapp* shows you everything at a glance.
One worktree per feature, not per whim: Create worktrees for work that will actually be parallel. If you're going to do things sequentially, a normal branch with checkout is sufficient.
Clean up when done: Abandoned worktrees are like branches nobody deletes — they accumulate and confuse. git worktree list is your friend.
Don't edit the same file from two worktrees: Technically you can, each has its copy. But if both modify the same file, you'll have conflicts when merging. Try to have features touch different areas of the code.
Complete Workflow Proposal
For those who want an organized workflow, here's what I use:
# 1. Create worktrees for sprint features
cd ~/code/myapp
git worktree add ../myapp-feat-a -b feature/feat-a
git worktree add ../myapp-feat-b -b feature/feat-b
# 2. Launch an agent in each
cd ~/code/myapp-feat-a && claude # terminal 1
cd ~/code/myapp-feat-b && claude # terminal 2
# 3. Merge as they finish
cd ~/code/myapp-feat-a
git push -u origin feature/feat-a # create PR
# 4. Clean up what's already merged
git worktree remove ../myapp-feat-a
git branch -d feature/feat-a
# 5. See what's still active
git worktree list
The cycle is: create → work in parallel → push/PR → merge → clean up. Each worktree lives as long as the feature, no more, no less.
Closing Thoughts
Worktrees have been in git since version 2.5 (July 2015). More than ten years. And most people still do git stash like we're in 2010.
With the arrival of coding agents, the bottleneck is no longer the speed at which you write code — it's the speed at which you can context switch. And worktrees eliminate that context switch completely: you don't switch branches, you switch directories. cd instead of checkout.
Which is, ultimately, what we should have been doing all along.
TL;DR: git worktree add ../name -b branch creates a second working directory on the same repo. No copies, no stash, no invalidating caches. Perfect for having multiple coding agents working in parallel. Clean up with git worktree remove when done.
This article was originally written in Spanish and translated with the help of AI.
Top comments (0)