The Single Checkout Bottleneck
I’m developing a menu bar app on macOS. I have three features in the backlog: a consumption sparkline, native notifications, and a desktop widget. All three are independent. All three I’m going to build 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 at once, my classic options are:
Stash ping-pong:
git stash, switch branch, work,git stash pop, pray there are no conflicts. Repeat until madness or retirement, whichever comes first.Clone the repo three times: It works, but now I have three copies of
.git/, three independent histories, and agit fetchto do in each one. A waste.Accept the serial life: One feature after another. Safe, predictable, and slow as a hand-written merge sort.
None of them 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 table where you could only have one book open. A worktree is adding more tables. Each one with a different book open, but all drawing from the same bookshelf.
~/code/myapp/ ← table 1 (main)
.git/ ← the library (just one)
~/code/myapp-sparkline/ ← table 2 (feature/sparkline)
.git ← file, not folder (pointer to the library)
~/code/myapp-notifications/ ← table 3 (feature/notifications)
.git ← another pointer
Each directory is a complete checkout with all the files. You can compile in one, run tests in another, and have your AI agent working in the third. At the same time.
Creating It Is One Line
From your main repo:
| |
That’s it. 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 make a 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 table has its working copy)
- The staging area (each has its own
git add) - The HEAD (each points to its branch)
In plain English: the state of “what I’m working on” 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:
| |
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 of the agents finishes and pushes, the others already see that branch.
Merging: Exactly the Same as Always
Worktrees don’t change anything about the merge flow. They’re normal branches in separate directories:
| |
When you’re done, you clean up:
| |
The Pitfalls Nobody Tells You About
1. One Branch, One Worktree
You can’t have main checked out in two worktrees at once. It’s by design: 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 is going to be slow. After that, each worktree maintains its independent cache, which is precisely the advantage over classic git checkout (which invalidates your 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 have to recreate them or make symlinks.
4. Apps with Shared State on Disk
If your app writes data to ~/Library/Application Support/ or similar, two app instances from different worktrees will compete for the same file. It’s not a worktree problem, it’s a problem of running two instances of the same app. The solution: don’t run two at once, or parameterize the data directory by build.
5. Don’t Delete the Directory by Hand
If you do rm -rf on 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 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
That way ls ~/code/myapp* shows you everything at a glance.
One worktree per feature, not per whim: create worktrees for work that’s actually going to be parallel. If you’re going to do one thing after another, a normal branch with checkout is enough.
Clean up when you’re done: abandoned worktrees are like branches nobody deletes — they pile up 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:
| |
The cycle is: create → work in parallel → push/PR → merge → clean up. Each worktree lives as long as the feature lives, no more, no less.
To Close
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 switch context. And worktrees eliminate that context switching completely: you don’t change branches, you change directories. cd instead of checkout.
Which is, after all, what we should have always been doing.
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 you’re done.