skribbl
productpricingfree!questionswriting
download
guides · 7 August 2026 · 9 min read

Git worktrees for AI coding agents: the whole trick

The commands, the layout, the cleanup, and the five ways it still bites.

skribbl/writing/guides
one checkout per agent

Two coding agents in one checkout will overwrite each other, and no amount of prompting fixes it. Git worktrees give each agent its own directory on its own branch against one object store. The commands, the directory layout, the cleanup, and the five ways it still goes wrong.

why one checkout breaks

Two coding agents pointed at one working directory will overwrite each other, and there is no prompt that prevents it. The reason is mechanical rather than behavioural. An agent reads a file into its context, thinks for thirty seconds, and writes the file back. If a second agent wrote to that file during the thirty seconds, the write lands on top of it and the earlier edit is gone. Git never sees a conflict, because a conflict requires two commits. What happened here is one commit containing one of the two changes, which is a lost update.

The second failure is worse because it is invisible. A branch is a property of the working directory, not of a process. If one agent runs git checkout, git stash or git rebase, every other process in that directory is now looking at different files than the ones it read a minute ago, and none of them will be told. Each will notice its own edits appear to have vanished, conclude it made a mistake, and start confidently repairing damage it did not cause.

You can dodge both by giving each agent its own clone, and for a small repository that is a reasonable answer. It stops being reasonable when the repository is large, when you want commits from one agent visible to another without a remote round trip, or when you are creating and destroying these directories several times a day. That is the job a worktree does.

The isolation boundary is the working directory. An agent launched in the wrong directory has no isolation regardless of which branch it thinks it is on.

what a worktree actually is

A git worktree is a second working directory attached to a repository you already have. It sits wherever you put it, it is checked out on its own branch, and it shares one object database with the original checkout. Git has supported this since version 2.5, so unless you are on something ancient it is already installed.

The mechanics are worth knowing because they explain every quirk further down. In a normal checkout .git is a directory. In a worktree, .git is a single file containing one line:

$ cat ../wt-schema/.git
gitdir: /Users/you/code/myapp/.git/worktrees/wt-schema

That points at a small per-worktree administrative directory inside the original repository, holding this worktree's HEAD, its index and its own reflog. Everything else lives in the one shared directory. A worktree is not a copy of the repository, it is a second head on the same body.

PER WORKTREEHEAD, the index, the working files, the worktree-local reflog
SHARED BY ALLobjects, all branch refs, remotes, config, hooks, the stash, .git/info/exclude
COST TO CREATEa checkout of the tree plus a few small files. No new object copy
VISIBILITYa commit made in one worktree is immediately available in all of them. No push, no fetch

The last row is the one that matters for agents. When the agent working on your schema commits, the agent working on your API can merge that branch immediately, with no remote involved. Three clones would need a push, a fetch, and somewhere to push to.

creating one per agent

Start in your existing checkout, on the branch you want to branch from, and create one worktree per slice of work. The -b flag creates the branch at the same time.

cd ~/code/myapp

git worktree add ../wt-schema   -b feat/schema
git worktree add ../wt-api      -b feat/api
git worktree add ../wt-checkout -b feat/checkout

git worktree list

git worktree list is the command to keep in your head. It prints one line per worktree with the path, the current commit and the branch, and it is the only reliable answer to "what have I got open right now":

/Users/you/code/myapp       afa9821 [main]
/Users/you/code/wt-api      afa9821 [feat/api]
/Users/you/code/wt-checkout afa9821 [feat/checkout]
/Users/you/code/wt-schema   afa9821 [feat/schema]

Add --porcelain if you want to parse it from a script. That is the stable format, one field per line, and is what tooling should read rather than the columns above.

the argument forms worth knowing

  • git worktree add ../wt-api -b feat/api creates a new branch feat/api at the current HEAD and checks it out there. This is the one you will use nearly always.
  • git worktree add ../wt-api feat/api checks out an existing branch. It fails if that branch is checked out anywhere else, which is a feature and is covered below.
  • git worktree add ../wt-api with no branch argument creates a branch named after the directory, which is how people end up with branch lists full of directory names. Pass -b.
  • git worktree add --track -b feat/api ../wt-api origin/feat/api creates a local branch tracking a remote one, for picking up work that already exists.
  • git worktree add --detach ../wt-look v1.4.0 gives a detached HEAD at some commit, for letting an agent read an old version without a branch it might commit to.

Then launch each agent inside its own worktree, and leave your original checkout without an agent in it. That directory is where you read diffs, run the full suite and merge. One directory that is always clean is what tells you whether the repository is broken or merely mid-edit somewhere else.

cd ../wt-schema   && claude     # terminal 1
cd ../wt-api      && codex      # terminal 2
cd ../wt-checkout && gemini     # terminal 3

Name the branches after the work rather than after the agent. feat/schema, not claude-2. The branch outlives the session, and in a week the useful question about a commit is what it was trying to do, not which vendor's CLI typed it.

where to put them

There are two layouts and the choice matters less than picking one and staying with it. Siblings of the repository:

~/code/
  myapp/          <- the original checkout, always on main
  wt-schema/
  wt-api/
  wt-checkout/

Or nested inside the repository, in one hidden directory:

~/code/myapp/
  .worktrees/
    schema/
    api/
    checkout/

Nesting is tidier and has one requirement people discover by accident. The nested directories are untracked files inside the repository, so git status in the main checkout reports them and an agent told to "commit everything" will try to commit a whole second checkout. Add the directory to .gitignore first:

echo '.worktrees/' >> .gitignore
git add .gitignore && git commit -m "ignore worktree directory"

The failure this prevents: a commit that adds three thousand files nobody meant to add, produced by an agent following an instruction that was correct everywhere else.

what does not come across

A worktree contains tracked files and nothing else. This catches everyone exactly once and it is the single most common reason a fresh worktree does not build. Your .env.local is gitignored, so it is not there. node_modules is gitignored, so it is not there either. Neither is your local editor config, your database file, or whatever else you have accumulated that git has been told to ignore.

cd ../wt-api
cp ../myapp/.env.local .        # gitignored, so it did not come across
npm install                     # node_modules is per worktree

PORT=3001 npm run dev           # and a port nobody else is using

Assign the ports at the same time you assign the directories. Two dev servers fighting over port 3000 is the most boring failure in this whole subject and it eats afternoons, because the symptom is a page that loads from the wrong branch rather than an error. If something is already listening, find out what before you go looking in the application:

lsof -i :3000

If bootstrapping takes more than two commands in your stack, make it one script committed to the repository. An agent can run it too.

submodules

Submodules are the part where the honest answer is that this is not fully supported. A new worktree gets the submodule directories created and empty. You have to initialise them inside that worktree:

cd ../wt-api
git submodule update --init --recursive

That works. But git's own manual page for worktree carries a BUGS section saying that support for submodules is incomplete and that multiple checkouts of a superproject are not recommended. Two visible consequences: git worktree move refuses to move a worktree containing submodules, and git worktree remove needs --force on one. Read that section in git help worktree before building a submodule-heavy repository into a fan-out workflow.

The failure this prevents: an agent spending a lot of tokens debugging a missing module that was never missing, only never installed in the directory it was standing in.

what they share, and what that costs

Sharing one object database is the point. Sharing everything else in .git is the consequence, and three of the shared things surprise people.

a branch can only be checked out once

Git refuses to have the same branch checked out in two worktrees. Try it and you get a refusal naming the worktree that already has it:

$ git worktree add ../wt-dup feat/schema
fatal: 'feat/schema' is already used by worktree at '/Users/you/code/wt-schema'

The same rule applies to git checkout in your main directory and to git branch -d, which refuses to delete a branch that some worktree is sitting on. This is the protection you actually want. It is also the reason the tidy-up order at the end is remove first, delete the branch second.

the stash is shared, the hooks are shared

There is one stash for the whole repository. Run git stash in one worktree and git stash list in another shows it, which means git stash pop in the wrong directory drops someone else's changes into your tree. Given that agents reach for stash on their own, this is worth an explicit instruction not to use it.

Hooks are shared too, because .git/hooks lives in the common directory. A pre-commit hook you installed once fires in every worktree. That is usually what you want. The exception is hook managers that set core.hooksPath to something inside node_modules, because node_modules is per worktree, so the hook silently does not run until you install dependencies there. .git/info/exclude is shared as well, so there is no clean place to put per-worktree ignore rules.

removing them again

Worktrees are meant to be disposable. The tidy-up is three commands in this order, and the order is the one the previous section explains:

git worktree remove ../wt-schema
git worktree prune
git branch -d feat/schema

remove deletes the directory and the administrative entry together. prune forgets entries whose directories no longer exist, which is what you run after deleting one by hand. Deleting the branch is separate, because git will not let you do it while the worktree is still there.

remove only removes clean worktrees. Any modified tracked file or any untracked file at all and it refuses:

$ git worktree remove ../wt-api
fatal: '../wt-api' contains modified or untracked files, use --force to delete it

Untracked counts: a stray log file is enough. That refusal does real work in an agent setup, because what is in there is an agent's unfinished work and you cannot see it from outside. Look before you reach for --force:

git -C ../wt-api status --short
git -C ../wt-api diff

If you delete a directory outside git, git worktree list keeps showing it with a prunable annotation until you prune. Nothing is broken in the meantime and prune -v tells you exactly what it dropped and why. Two smaller commands round it out: git worktree lock marks a worktree that must not be pruned or removed, taking a --reason that gets printed back when you try, and git worktree repair reconnects the administrative files after you moved a directory by hand rather than with git worktree move.

five ways it still bites

Worktrees solve one problem completely: two processes writing the same files. They do not solve any of the others, and pretending otherwise is how a setup that looks clean produces a merge that is not.

1. Nothing enforces the boundary

A worktree is a directory, not a permission. An agent in wt-api can edit db/ if it decides to, and git will happily commit it to feat/api. There is no per-branch path restriction in git, CODEOWNERS does nothing locally, and a scope rule in a prompt is a suggestion. The enforcement is you reading the file list before you read any code:

git diff --name-only main...feat/api

That is the cheapest check available. A branch that touched directories it was not assigned is something you want to know in one line rather than three hundred. The wider question of who is allowed to do what, and who is allowed to instruct whom, is covered in who may command whom.

2. Shared files still conflict

Lockfiles are the classic. Three agents each adding a dependency produce three different package-lock.json files and a merge nobody can resolve sensibly, because no human wrote any of them. Add dependencies yourself, on the base branch, before the fan-out. The same goes for generated code, applied migrations and CI configuration.

3. Divergence is silent

Each worktree is frozen at the commit it was created from. Nothing pulls. An agent working against a base that moved an hour ago is writing code against an API that no longer exists, and it will not find out until merge time. Rebase the long-running ones periodically, or keep the fan-out short enough that it does not matter.

4. The disk cost is not the git cost

The object database is shared, so worktrees are cheap in git terms. The working files are not, and neither is node_modules. Four worktrees of a JavaScript project is four dependency trees, usually far more disk than the repository itself. A reason not to leave a dozen lying around, not a reason to avoid them.

5. It does not make the work parallel

This is the important one. A worktree gives you isolation. It does not give you a decomposition. If the work does not genuinely split into pieces that touch different files, four worktrees produce four branches that all edit the same module and a merge that is worse than doing it once in sequence. Deciding the split is the human step and it comes first, which is the subject of running agents in parallel and, in longer form with a plan file, of setting up a multi-agent workspace.

Every one of these is a review problem in the end. If four diffs land and you read them in the way you read a diff at five in the evening, the isolation bought you nothing. The honest limit on how many agents to run is how many diffs you can read properly, which is argued out in why multi-agent coding fails.

common questions

Can two AI coding agents work in the same git repo?

Not in the same checkout. Two agents editing one working directory overwrite each other and change the branch under one another. They can share one repository if each gets its own git worktree, which is a separate directory on a separate branch backed by the same object store. Launch each agent inside its own directory, and keep your original checkout free of agents so you have somewhere clean to read diffs and merge.

What is a git worktree?

A git worktree is a second working directory attached to an existing repository, checked out on its own branch, sharing one .git object database. Create one with git worktree add ../dir -b branch. It is cheaper than a clone and everything committed in it is immediately visible to the original checkout, with no remote and no fetch. Git has supported worktrees since version 2.5.

Why does git worktree remove refuse to run?

git worktree remove only removes clean worktrees. If the directory has modified tracked files or any untracked file, it refuses with contains modified or untracked files, use --force to delete it. That refusal is protecting uncommitted agent work, so run git -C <path> status --short and read the diff before you force it. A worktree containing submodules also needs --force, even when clean.

Do git worktrees copy node_modules and .env files?

No. A worktree contains tracked files only, so anything gitignored is absent. Every new worktree needs its own dependency install and its own copy of gitignored environment files before an agent can build in it. Give each one a distinct dev server port at the same time, because two servers on one port presents as a page served from the wrong branch rather than as an error.

Do git worktrees work with submodules?

Partly. Submodule directories in a new worktree are created empty and need git submodule update --init inside that worktree. Git's own manual states that submodule support is incomplete and advises against multiple checkouts of a superproject. In practice git worktree move will not move a worktree containing submodules, and remove needs --force. Read the BUGS section of git help worktree first.

How many worktrees should I create for coding agents?

As many as you can review. Worktrees cost almost nothing to create, so the limit is not disk or git, it is how many diffs one person can read carefully in a sitting. For most people that is two or three. The number of cores on your machine is irrelevant to this, because it does not read the diff.

one implementation

We build a macOS app called Skribbl that folds this bookkeeping into the workspace: real terminals on a canvas, where spawning a group of agents creates the worktree first, because the worktree path is the working directory each agent launches into and cannot be created afterwards. It is the same git underneath, and every command above works with no app installed. The docs describe how it works, the comparison page puts it next to the other tools in this space, and the download page has the build.

If you are running one agent at a time and it is working, none of this is urgent. Worktrees are worth learning the day you first want two.

READ NEXT
How to set up a multi-agent coding workspaceThe commands, in order, and what goes wrong when you skip one.12 minHow to run AI coding agents in parallelHow to split the work, and the honest ceiling on how many you can review.10 minBuilding a full-stack app with three agentsThe plan file, the three worktrees, the merge order, and where it goes wrong.12 min
ON THIS PAGE
why one checkout breakswhat a worktree iscreating one per agentwhere to put themwhat does not come acrosswhat they shareremoving them againfive ways it still bitescommon questionsone implementation
run them on a canvasSkribbl puts every agent, its terminal and what it is spending on one board. macOS, one day free.

get the next one by email.

One email when there is something worth reading. Unsubscribe is one click and it is in every issue.

get me
productpricingdocsquestionswhat it iscomparereleaseswritingnewsletterlaunchesprivacycancel
give them infinity.© skribbl