Claude on a refactor. You know it’s going to take a while, and you know exactly what comes next.
- Either you sit there and watch the stream scroll, doomscroll a bit while waiting, half afraid that touching anything will corrupt the run. Twenty minutes of your day, gone, because you’re scared of your own workflow.
- Or you start poking at small things while you wait. A typo in a docstring, a stray import, a README line that’s been bugging you. Then the Agent finishes, looks at the diff, and starts talking to itself: “I don’t remember adding this, let me clean it up”. You watch it calmly revert your work because it doesn’t fit the scope it was given.
Both options are the same bug with different symptoms. You and the Agent are sharing one working directory, and a working directory can only hold one train of thought at a time.
The fix is not a better prompt or a tighter scope. It is a second working directory. That is what Git worktrees give you, and this post covers what they are, how they work, the setup cost nobody warns you about, and a small Claude Code toolkit I put together to make that cost go away.
One directory, one train of thought
A working directory holds exactly one train of thought at a time. One Agent running in your repo is easy. Two Agents, or one Agent plus you, is where it breaks, because two entities cannot edit the same files on disk without colliding. Prompts, context, and review don’t fix that. It is a physical constraint.
Branching doesn’t help, either. Switching branches still mutates the one directory you have open. If the Agent is halfway through edits on feature/auth and you git checkout main to check a detail, you either can’t switch (Git blocks you on the dirty tree) or you drag the Agent’s in-progress changes onto the wrong branch. Either way, the isolation you wanted is gone.
What you actually need is a second copy of the repo that the Agent can own, ideally without the overhead of a full git clone and a fresh remote. That’s exactly what a worktree is.
Worktrees in one sentence
A worktree is a second working directory pointing at the same Git repository, locked to a different branch.
In plain terms: you ask Git to create a new folder somewhere on your disk, and it fills that folder with your repo’s files for a branch of your choosing. One command, one flag:
# from inside your main repo
git worktree add .worktrees/myrepo-auth -b feature/auth
That command creates a new folder at .worktrees/myrepo-auth inside your repo, checks out a brand new branch feature/auth into it, and leaves your original directory completely untouched. You now have two checkouts of the same repo, each on its own branch, sharing one underlying .git database. Your original terminal, your editor, and your local dev server keep running on the branch you were already on, as if nothing happened. Make sure .worktrees/ is in your .gitignore so the new folder never ends up tracked by the very repo it lives inside. Alternatively, you can park the new worktree outside the repo entirely, like ../myrepo-auth.
You can also let Claude Code create worktrees for you directly. It has a native --worktree flag (-w) that creates the worktree and launches the session in one step:
claude --worktree feature-auth
The argument is a worktree name, not a branch to check out. Claude parks the new directory under .claude/worktrees/feature-auth and branches off your default remote branch into worktree-feature-auth. Same rule as before: gitignore .claude/worktrees/ so it never gets committed.
Worktrees cheat sheet
Five commands cover the day-to-day worktree lifecycle. Bookmark this block and you have the full surface:
# create a worktree on a new branch
git worktree add .worktrees/myrepo-auth -b feature/auth
# create a worktree on an existing branch
git worktree add .worktrees/myrepo-hotfix hotfix/login
# list all worktrees
git worktree list
# remove a worktree when you're done (--force if it has uncommitted changes)
git worktree remove [--force] .worktrees/myrepo-auth
# clean up stale metadata after a manual rm -rf
git worktree prune
Inside a worktree you commit, push, pull, rebase, and open PRs exactly the way you always have. There is no worktree-specific Git dialect.
Branches and worktrees live on different axes
The key mental model: a branch is a named line of commits in your repo’s history, nothing more. It has no physical location. A worktree is a directory on your filesystem. It’s a place where you can see and edit the files for a specific branch.
Without worktrees, you have one window onto the repo and git checkout swaps what that window shows. With worktrees, you have multiple windows open side by side, each locked to a different branch. You move between them by moving between terminals (or editor windows, or tmux panes) without the need to switch branches constantly.
Two important things to be aware of:
- Removing a worktree does not remove the branch.
git worktree remove ../myrepo-authdeletes the physical directory, butfeature/authand all its commits are still in the repo. The directory was just a viewing window. Closing it doesn’t touch the history. - Commits are visible across worktrees instantly. All your worktrees share one
.gitdatabase, so a commit made in one shows up ingit logfrom another with no push, pull, or sync.
Insight: Branches live in your repo’s history. Worktrees live on your filesystem. They exist on different axes, which is why removing a worktree does not touch the branch, and why a commit made in one worktree shows up in every other worktree instantly.

What worktrees are for, and what they are not
The axis split has a practical consequence that trips people up the first time they try to parallelize agents: worktrees parallelize branches, not agents. Git enforces this directly. You cannot check out the same branch in two worktrees at the same time. The second git worktree add will refuse, and it refuses for a good reason, because two directories mutating the same branch pointer is chaos.
What they are for: parallel work on different branches. The canonical scenario looks like this. You have an Agent iterating on feat/auth on a task that takes ten minutes to come back. You do not want to sit and wait, and you do not want to switch branches and lose your place. So you open a worktree for feat/billing and start the next piece of work there. When the feat/auth Agent finishes, you swap terminals, not branches. One tab is already sitting in the feat/auth worktree with its own dev server, its own .env, its own node_modules, exactly as you left it. You review, apply suggestions, push, and go back to the other tab. Neither branch ever had to be stashed, checked out, or re-bootstrapped.
What they are not for: multiple Agents on the same branch. Suppose you are doing a big frontend overhaul on feat/frontend-redo and you want three Agents chipping away at it in parallel. The instinct is “three worktrees on feat/frontend-redo“. Git will refuse, and even if it did not, the Agents would stomp each other’s commits in the shared history. This is a branch-level constraint, and the filesystem has nothing to do with it. The fix is upstream: decompose the overhaul into sub-branches (feat/frontend-redo-nav, feat/frontend-redo-table, feat/frontend-redo-auth) off feat/frontend-redo, run each in its own worktree, and merge them back into the local parent branch as they finish. Worktrees then become the enabling tool, but the actual solution here is task decomposition.
Insight: Worktrees parallelize branches, not Agents. Git refuses to check out the same branch in two worktrees on purpose, and that rule is the shape of the technology. For parallel work on different branches, worktrees are the right tool. For two Agents on the same branch, the fix is to decompose the branch first.
Why this became a big deal when Agents showed up
Zooming out for a moment: this is why a decade-old Git feature is suddenly everywhere. Worktrees have existed in Git since 2015. For most of that decade they were a niche power-user feature that not many needed nor knew about, because humans don’t usually want to work on three branches at the same wall-clock minute.
But AI Agents do.
Once you have a tool that can reliably execute a twenty-minute coding task in the background, the question “how many of these can I run at once?” becomes real. Two? Three? Five? And the moment you try to run two, you hit the working-directory problem, and worktrees are suddenly the obvious answer. This is why claude --worktree exists as a first-class flag, and why most serious write-ups on parallel agentic coding in the last year have worktrees somewhere in the stack.
But the bigger shift is what happens to you. When three Agents are running in parallel, your job stops being “write the code” and starts being “decompose the work, assign it, review what comes back”. You’re not in a coding bubble anymore. You’re in an orchestration bubble, juggling feature branches, checking which PR needs attention, thinking about how the pieces fit together. Worktrees are what make that mode physically possible, because without them you can’t even have three branches checked out at the same time.
A practical note on the ceiling: three to five concurrent Agents in my experience, before things get unwieldy. The bottleneck is rarely Git. It’s rate limits from the model provider, and the cognitive overhead of reviewing five PRs in flight.
Insight: Worktrees were a niche feature for a decade because humans rarely need three branches open at once. Agents changed the demand, and with it the developer’s role: from writing code to orchestrating parallel streams of work.
The setup tax nobody warns you about
Before you hit rate limits or review overload, you’ll hit a more mundane ceiling: every new worktree needs to be bootstrapped from scratch. Here’s the part the enthusiastic blog posts skip. A worktree is a fresh directory with only the files Git tracks. Everything mentioned in your .gitignore is, by definition, not there. That sounds obvious when you read it and painful when you live it.
Things that are not in a new worktree:
node_modules/,.venv/, or whatever your package manager builds. You get to reinstall.- Build artifacts like
dist/,.next/,target/, or__pycache__/. First build is always cold. .envand anything else you wisely kept out of version control. Your app won’t start without them.- Ports. Two worktrees both running
npm run devwill fight over:3000, so running them side by side means giving each one its own port range.
For a small Python repo this is thirty seconds of annoyance. For a modern monorepo with a 400 MB node_modules tree, a Next.js build cache, and a .env with twelve secrets in it, this is five to ten minutes of setup for every worktree you spin up. This raises a fair question: is it still worth opening a new worktree for a ten-minute fix, if you have to spend five minutes setting it up first?
If you answer honestly, sometimes the answer is “no”. So you skip the worktree, share the directory with your Agent again, the Agent undoes your work, and you write a blog post about it.

Insight: The real cost of worktrees is everything gitignored that does not come along:
node_modules,.envfiles, build caches, virtualenvs. Budget several minutes of setup per worktree on a modern monorepo, or automate it away.
Reducing the tax by letting the Agent pay it
The setup tax is exactly the kind of repetitive, mechanical, error-prone glue work that software is good at. The obvious move is to automate it: wrap git worktree add, copy the .env files, run the install command, and surface a single command that does all three. But how?
Through a shell script?
The simplest answer is a bash script. Wrap git worktree add, copy the .env files, run npm install or uv sync, done. It works, and for a single repo with a single stack it works well.
But, it also runs out of road fast. A script cannot easily name a branch for you when you describe a change in one sentence. It cannot decide whether a small fix should go through a PR or fold locally into a parent branch. It cannot read your repo, notice you are on Next.js plus FastAPI, and configure itself accordingly. Every repo you drop it into is a fresh round of hand-editing. The script is the right tool for the mechanical half of the problem and the wrong tool for everything above it. Exactly the things Agents are good at.
So, through a Claude Code Skill then?
If a script is too rigid, the opposite instinct is to hand the whole thing to an Agent. Write a skill, let’s say /wt-open (spoiler: we will actually build this skill later, once we’ve seen why it is not enough on its own), and let Claude Code figure out the branch name, the install command, the port config, and the file copying. That solves the flexibility problem.
Yet, it is still the wrong answer. Mechanical file operations, hash-derived port offsets, and idempotent install commands are exactly the kind of deterministic work an Agent is bad at. It is slower, it can drift between runs, and you’ll spend more time arguing with the Agent or fixing the setup than doing it yourself. There is a smaller, practical wrinkle on top: Claude Code skills won’t discover files in .gitignore through search or glob. .worktreeinclude solves this for a static list of files you maintain by hand, but the moment you want conditional logic, monorepo-aware paths, or anything beyond a flat copy, you are back to a script. You want this layer to be boring.
Combining both is best
As you have probably guessed, the answer is to use each tool for what it is good at:
- A bash script for what the Agent cannot see, and should not be doing anyway. Creating the worktree, walking the main repo, copying every
.env*file into the new directory (preserving subdirectory structure for monorepos), deriving deterministic port offsets so dev servers in different worktrees do not collide, and running the install command. Plain shell, boring on purpose. - A set of Claude Code skills on top for what the Agent is actually good at. Naming a branch from a one-sentence description, picking a merge strategy, writing a PR description, deciding whether to delete a branch after cleanup, and reading the repo’s stack to configure the bash script in the first place.
Insight: A shell script is flexible enough to handle mechanical setup but too rigid for anything above it. A skill is flexible enough to handle the reasoning but should not be doing deterministic file work in the first place. The best answer is to combine both in a unified setup.
One script is actually one script per repo
There is a catch hiding in “just write a bash script”. A script that installs dependencies for a Next.js frontend is not the same script that installs for a Rust workspace or a Python monorepo. The install command differs. The default port numbers differ. Whether there is a make setup target differs. The file-copy and port-derivation logic is generic, but the install and port blocks are repo-specific. So you end up hand-writing those two blocks once per project, and re-writing them whenever your stack drifts.
Reading a repo and figuring out its stack is exactly the kind of thing the GenAI layer is good at. You can write a second Skill whose only job is to read package.json, pyproject.toml, Dockerfile, docker-compose.yml, Makefile, and .env.example, work out the install command and the ports, and fill in the empty slots in a predefined “setup” bash script. Let the Agent audit your repo’s worktree-readiness, and fix the open gaps.
A concrete worktree management tool you can borrow
To save you the hours of wiring the two layers together yourself, I (read: Claude) wrote a small worktree toolkit and put it on GitHub as ThinkVelta/claude-worktree-tools. It is not a product, but a starting point for you to fork and bend to your own liking. If you want to use it as-is, you can drop the Claude code skills and setup script into any git repo through a single command:
npx @thinkvelta/claude-worktree-tools
What the installer drops into your repo
This script will do the following:
- Create scripts/wt-setup.sh, the bash bootstrapping layer that creates the worktree, copies
.envfiles, derives a port offset, and runs the install command. - Drop a collection of Claude Code skills into
.claude/skills/*:- /wt-open creates or reopens a worktree from a branch name or a one-sentence task description, and runs
wt-setup.shfor you. - /wt-merge does a local
git merge --no-ffinto a target branch, for folding sub-branches into a parent or batching small fixes. - /wt-close handles the tear-down of a worktree, and is also where the optional push lives (
--push). - /wt-list a richer
git worktree listwith clean/dirty, ahead/behind, and stale warnings. - /wt-cleanup batch housekeeping for stale worktrees and local branches whose remote was deleted after a PR merge.
- /wt-adopt the personalization skill from the previous section, which reads your stack and fills in the repo-specific sections of
wt-setup.sh. - /wt-help the in-repo FAQ for VSCode integration, gitignore questions, and the port-offset mechanics.
- /wt-open creates or reopens a worktree from a branch name or a one-sentence task description, and runs
From install to first parallel task
End to end, adopting the toolkit in a repo and running a parallel task looks like this:
# one-time: drop the script and skills into the repo
npx @thinkvelta/claude-worktree-tools
# from here, you continue in a Claude session
claude
# once per repo Claude should fill in the install and port blocks for your stack
> /wt-adopt
# start a new parallel task in its own worktree
> /wt-open "add rate limiting to the auth endpoint"
# ... Agent works, dev server runs on an auto-assigned port offset ...
# fold the finished sub-branch into its parent locally
> /wt-merge feat/auth
# tear down the worktree and push the branch in one go
> /wt-close --push
Under the hood
A bit more on the parts that matter. The bash script creates the worktree under .claude/worktrees/ (the short hash guards against branches that differ only in slash placement, like feat/a-b vs feat/a/b), copies every .env* file across, derives a port offset (0–99) from a hash of the worktree path so each worktree runs on its own ports and never fights with another worktree over the same port, and runs the install command for your stack. Because the offset is derived from the path, the same branch always gets the same ports, which means bookmarkable URLs as a free side effect.
The port block and the install block in that script ship commented out, because there is no way to write them generically. Filling them in is what /wt-adopt is for. It also preserves a dedicated USER-DEFINED EXTRA SETUP block at the bottom of the script for your own migrations, seeds, and build warmups. That block is never overwritten, so re-running /wt-adopt after a stack change is always safe.
None of this is sacred. You can always iterate on both wt-setup.sh or the skills locally if needed. Also, the repo is small, MIT-licensed, and deliberately simple. Fork it if you want, rename the skills, rip out the bits you do not need, swap the install section for whatever fits your actual coding workflow best. The point is the approach, not the code itself.
Takeaway
You and your AI Agent cannot share a desk. You’ve felt this every time you’ve sat frozen watching a run finish, or watched an Agent calmly undo a change you made while it was thinking. Worktrees give the Agent its own desk, and they do it without cloning repos, fighting remotes, or inventing a new workflow.
The one real cost is setup overhead, and setup overhead is the exact problem your Agent was built to solve. Give it a worktree. Then give it the skill that sets up the next one. This is a pattern worth internalizing beyond worktrees: any tool that is great in theory but too annoying to set up in practice is a skill waiting to be written. Agents are here to remove the friction of getting started with Agents (how meta).
Insight: Whenever a tool is great in theory but the setup is too annoying to use consistently, that friction is a skill waiting to be written. Use the Agent you are already running to build the tooling that onboards the next one.
Key insights from this post
- Branches live in your repo’s history. Worktrees live on your filesystem. They exist on different axes, which is why removing a worktree does not touch the branch, and why a commit made in one worktree shows up in every other worktree instantly.
- Worktrees parallelize branches, not Agents. Git refuses to check out the same branch in two worktrees on purpose, and that rule is the shape of the technology. For parallel work on different branches, worktrees are the right tool. For two Agents on the same branch, the fix is to decompose the branch first.
- Worktrees were a niche feature for a decade because humans rarely need three branches open at once. Agents changed the demand, and with it the developer’s role: from writing code to orchestrating parallel streams of work.
- The real cost of worktrees is everything gitignored that does not come along:
node_modules,.envfiles, build caches, virtualenvs. Budget several minutes of setup per worktree on a modern monorepo, or automate it away. - A shell script is flexible enough to handle mechanical setup but too rigid for anything above it. A skill is flexible enough to handle the reasoning but should not be doing deterministic file work in the first place. The best answer is to combine both in a unified setup.
- Whenever a tool is great in theory but the setup is too annoying to use consistently, that friction is a skill waiting to be written. Use the Agent you are already running to build the tooling that onboards the next one.
Final insight: Your AI Agent can’t share a desk with you. Give it its own, and let it set up the next ones.
Follow me on LinkedIn for bite-sized AI insights, Towards Data Science for early access to new posts, or Medium for the full archive.
All images in this article were generated by myself using GPT-4o image generation.

