Git Worktrees: The Missing Primitive for Parallel AI Coding
On this page
The incident that pushed me to learn git worktree properly looked like this. Two Claude Code sessions, two terminals, one repo. Session A ran git checkout feature/billing to look at a migration. Session B, mid-edit on feature/profile-page, suddenly found itself on the wrong branch with the wrong files. Both sessions were operating on the same checkout. One git checkout flipped the floor under the other.
If you’ve ever opened a second terminal in the same repo and felt the dread of “wait, what branch am I on right now,” git worktree is the answer. Once you’re running AI coding agents in parallel, it becomes load-bearing.
This post covers what worktrees are, the commands worth knowing, and how Claude Code, Cursor, and the GitHub Copilot CLI ship native support.
What git worktree is
A normal git checkout has one working tree. One directory of files, one current branch, one HEAD, one index. Switching branches mutates that single working tree in place.
git worktree lets one repository have multiple working trees on disk simultaneously, each on a different branch. They share the underlying git object database. HEAD, the index, and the working files belong to each tree individually.
From the official docs:
The new worktree is linked to the current repository, sharing everything except per-worktree files such as HEAD, index, etc.
The feature shipped in Git 2.5 in July 2015. It has been stable for a decade. Most developers have never used it.
The mental model that helped me: a repo is a database (.git/) plus zero or more views into it. The original clone is one view, called the main worktree. Every additional view is a linked worktree with its own subdirectory under .git/worktrees/<name>/ holding that view’s HEAD, index, logs, and config.
The six commands worth knowing
add
# Check out an existing branch into a new directory
git worktree add ../myproj-billing feature/billing
# Create a new branch and check it out into a new directory
git worktree add -b feature/profile ../myproj-profile main
# Detached HEAD, useful for "I just want to look at a tag"
git worktree add --detach ../myproj-v2.1 v2.1.0
The path can be anywhere. I keep mine in sibling directories (../myproj-feature-x) or under .worktrees/ inside the repo, gitignored.
The constraint that surprises people: by default, you cannot check out the same branch in two worktrees. Git refuses. From the docs:
addrefuses to create a new worktree when<commit-ish>is a branch name and is already checked out by another worktree.
You can override with --force, but ninety percent of the time the refusal is correct: two working trees diverging on the same branch is a merge conflict waiting to happen.
list
$ git worktree list
/Users/me/code/myproj a3f2c9d [main]
/Users/me/code/myproj-billing 1b8e7a4 [feature/billing]
/Users/me/code/myproj-profile c4d2f1e [feature/profile]
Add --porcelain for script-friendly output.
remove
git worktree remove ../myproj-billing
Refuses if the worktree has uncommitted changes. Add --force to discard. The branch stays; only the working directory and the metadata go.
prune
If you delete a worktree directory manually (e.g., rm -rf ../myproj-billing), git’s .git/worktrees/billing/ metadata is now stale. git worktree prune cleans those up.
lock and unlock
If a worktree lives on a removable drive, git worktree lock tells git not to prune it as missing.
move
git worktree move ../myproj-billing ../myproj-billing-v2
Won’t work if the worktree contains submodules. From the man page: “the main worktree or linked worktrees containing submodules cannot be moved with this command.”
If you’ve moved a worktree by hand (mv ../myproj-billing ../somewhere-else) and forgotten that git tracked the old path, run git worktree repair in the moved directory. It fixes the metadata so git stops complaining the worktree is missing.
Things worth knowing upfront
A few non-obvious behaviors that bit me before I learned them.
Untracked files do not transfer. A new worktree is a fresh checkout of the branch. node_modules/, build caches, and locally generated configs are all absent. Reinstall dependencies, regenerate the artifacts, or use a tool that handles it for you (more on this below). Secrets are a separate concern: load them from a secrets manager rather than copying .env between worktrees.
Submodules are partially broken. From the BUGS section of the man page:
Multiple checkout in general is still experimental, and the support for submodules is incomplete. It is NOT recommended to make multiple checkouts of a superproject.
If your project leans heavily on submodules, test carefully before adopting worktrees for parallel work.
$GIT_DIR vs $GIT_COMMON_DIR. Inside a linked worktree, $GIT_DIR is .git/worktrees/<name>/ (per-worktree state). The shared object database, refs, hooks, and config sit at $GIT_COMMON_DIR, which git rev-parse --git-common-dir resolves to the main worktree’s .git/. Any script that writes to shared state (handoff files, queues) needs to resolve to $GIT_COMMON_DIR instead of $GIT_DIR or it will silently write into the per-worktree dir and lose the data when the worktree is removed. I learned this the hard way.
Per-worktree config exists, opt-in. Enable git config extensions.worktreeConfig true, then git config --worktree <key> <value> writes to a config scope that only affects this worktree. Useful when one worktree needs different remote tracking or sparse-checkout settings.
The main worktree cannot be removed. It can only be deleted by removing the entire repository.
Why this matters for AI coding tools
Single-developer git is mostly serial. You work on one branch at a time, mentally context-switch when you need to, and the cost of being on the wrong branch is “lose ten minutes.”
AI coding agents break that assumption in two ways.
They run in parallel. You don’t watch one agent type. You delegate three tasks across three terminals, three Claude Code sessions, three Cursor agents, then circle back to review. Each agent expects a stable working tree.
They mutate aggressively. A normal session edits five files. An agent session can edit fifty, run migrations, install dependencies, and reformat the codebase. If two sessions are operating on the same checkout, the second one’s idea of “the current state of the files” is wrong from the moment the first one runs.
Without worktrees, the parallel-AI workflow is sharp-edged. Either you keep all your agents on the same branch (and pray for non-overlapping edits), or you git stash/checkout/pop between them and watch agents lose their place.
With worktrees, each agent gets its own working directory on its own branch. No filesystem collisions, no HEAD fights. The shared object database still lets you cherry-pick, merge, or diff across them like any normal git workflow.
Built-in support today
Native worktree support landed in Claude Code, Cursor, and the GitHub Copilot CLI between October 2025 and February 2026. Each one does it differently.
Claude Code
Claude Code ships a built-in worktree flag. Run claude --worktree feature-x (or claude -w feature-x) and it creates .claude/worktrees/feature-x/ on a branch worktree-feature-x, then starts the session inside it.
.worktreeinclude(gitignore syntax) tells Claude Code which gitignored files to copy into new worktrees: local config, generated assets, per-machine settings. Keep secrets out of it; those still belong in a secrets manager loaded at runtime.- PR mode:
claude --worktree "#1234"fetches the PR and creates.claude/worktrees/pr-1234. - Sub-agent isolation: subagents declared with
isolation: worktreein their frontmatter spawn their own worktree, do their work, and auto-clean. - Auto-cleanup: a clean session (no uncommitted, no untracked, no new commits) auto-removes the worktree and branch. Dirty sessions and named sessions prompt instead.
-pnon-interactive runs are not auto-cleaned, so cron-style invocations don’t silently drop work.
The best-practices page lists worktrees as one of four official parallelism approaches, alongside the desktop app, Claude Code on the web, and agent teams.
Cursor
Cursor 3.0 replaced the older in-editor worktree and best-of-n selectors with /worktree and /best-of-n slash commands. Each task runs in its own worktree with isolated files and dependencies.
One thing to know: Cursor’s 2.0 changelog notes that worktrees use a simplified LSP compared to the main editor. If your AI loop leans on full type-checker feedback, you’ll get less of it inside a worktree.
GitHub Copilot CLI in VS Code
The Copilot CLI integration in VS Code exposes two isolation modes: worktree isolation (separate folder per session) and workspace isolation (same folder, separate state). In worktree mode, the agent commits to the worktree at the end of each turn and the worktree shows up in the Source Control view’s Worktrees node.
One important constraint: in worktree mode, permissions are pinned to Bypass Approvals and can’t be changed. Read that twice before you turn it on against a repo you care about.
The pattern I actually use
My setup runs a different shape than claude --worktree. I want every Claude Code invocation to be isolated, automatically, without me typing a flag. So I wrap the claude binary in a shell function in my ~/.zshrc:
source "$HOME/work/cto/scripts/claude-wrapper.sh"
The wrapper does five things on every claude invocation from my main repo:
- Mints a session id from timestamp and PID.
- Creates an ephemeral worktree at
.claude/worktrees/sessions/<id>/on a freshsession/<id>branch offdevelop. cds into the worktree.- Execs the real
claudebinary there. - On exit, removes the worktree if clean. If dirty, leaves it alone so nothing is lost.
Open three terminals, run claude in each, and you have three isolated sessions on three branches off the same base. No flags, no thinking. Outside the main repo the wrapper is a transparent passthrough, so claude works normally everywhere else.
Shared state lives outside the per-session worktree. Session registration, the work queue, and the snapshot file all sit at .handoff/ and .state/. Any script that touches them resolves the path via git rev-parse --git-common-dir so the writes land in the main tree’s .git/.., not in the per-worktree directory that’s about to get deleted.
If I’d skipped that detail, every session’s handoff record would have evaporated on cleanup. The first version of my wrapper did exactly that, and I lost two days of handoff history before I figured out why nothing was persisting.
When not to use worktrees
- Heavy submodules. Officially experimental; expect rough edges.
- Multi-GB build artifacts. Each worktree gets its own copy of generated files. If your project produces 5 GB of build output, four worktrees is 20 GB.
- Solo serial work. If you’re working alone in a single session, a single checkout is simpler. Worktrees pay off when concurrency is real.
- Repos with deeply path-dependent tooling. Some legacy build systems hardcode absolute paths. Worktrees break those assumptions.
If you’re running AI tools at any real scale, you’re going to want worktrees.