# Are Git worktrees for coding agents worth the discipline?

> Git worktrees for coding agents prevent checkout collisions, clarify merge order, and give teams a safe way to clean up abandoned work.

Parallel coding agents do not fail because they type into the same repository. They fail because teams mistake parallel file editing for parallel delivery. Give five agents one checkout and you get overwritten changes, accidental staging, test output from the wrong task, and a branch whose history nobody can explain. Give them five worktrees without ownership and merge rules and you get the same mess, only distributed across five directories.

Git worktrees are worth the discipline when they make two decisions explicit: which agent owns each change, and in what order those changes enter the product. A worktree is the physical boundary. The task graph and merge queue are the operating rules. You need both.

The Git manual describes a linked worktree as another working tree attached to the same repository. Each one can check out a different branch, while Git keeps the repository history and objects shared. That is exactly the right level of separation for coding agents: separate files and local state, one source of truth for commits.

## Agents need separate checkouts, not shared politeness

An agent working in a shared checkout can change more than the files named in its task. It can regenerate a client, update a lockfile, rewrite a formatter configuration, leave an unstaged migration, or start a process that writes to a local database. Even an agent that behaves perfectly can collide with a second agent that runs tests at the wrong moment.

Telling agents to "be careful" is not a control. A shared checkout has one index and one working directory. The index is where Git records the next commit, so one actor can stage another actor's changes without noticing. That is not an AI-specific defect. Humans have been doing it to each other for years.

A worktree gives every agent its own directory, checked-out branch, index, and HEAD. Git still prevents the dangerous case of checking out one branch in multiple worktrees by default. Do not bypass that safeguard. If two agents need to change the same branch, they are not independent workers. They need a handoff.

The practical distinction is easy to miss:

- A branch is a line of commits.
- A worktree is a directory where one branch is checked out.
- An agent assignment is a bounded responsibility with a branch, acceptance test, and disposal rule.

Most teams create branches and call the job done. That leaves each agent free to keep changing the same local files, ports, generated assets, and test fixtures. Worktrees make the checkout part of the assignment concrete.

Use one primary checkout as the control room. Keep the default branch there. Put agent worktrees beside it, not inside it, so a recursive search, editor workspace, or cleanup script cannot accidentally treat one worktree as part of another.

```text
repos/
  billing-api/                 # primary checkout and integration control
  billing-api-wt/
    agent-auth-refresh/
    agent-invoice-export/
    agent-test-repair/
    integrate-release-184/
```

That naming has a mundane benefit: an operator can tell what each directory is for without inspecting Git. When five jobs run overnight, mundane beats clever.

## A task boundary must be smaller than a vague feature

A worktree cannot make overlapping work merge cleanly. It only stops overlap from corrupting the checkout before you see it. The job still needs a boundary that can survive contact with other branches.

"Implement invoice exports" is not an agent task. It is a feature area with unknown seams. A better split might assign one agent the export domain contract and migration, another the CSV writer behind that contract, and a third the authorization tests after the contract settles. Those tasks are not identical in risk, and they should not start or merge in the same way.

Before creating worktrees, write each assignment with five fields:

1. The branch name and parent commit.
2. The files or module the agent owns.
3. The interfaces it may change.
4. The commands that prove the change works.
5. The branch or task that must land before it.

The fourth field catches a common failure. Teams ask an agent to "add tests" but never say whether its test suite must pass alone, after a dependency branch lands, or only in the combined product. The agent then reports success from its own worktree, while the eventual merge fails because its expected API never existed on the integration branch.

The fifth field matters more than task completion time. If agent B needs a type introduced by agent A, then A belongs ahead of B in the merge queue even if B finishes first. Trying to hide this dependency with a fast rebase creates busywork. B repeatedly absorbs moving changes and spends its context resolving conflicts that an integrator should have handled once.

Keep ownership strict. If an agent discovers it must edit a file owned by another active task, it should stop and report the dependency. Do not reward it for silently making the edit. That behavior looks productive until two valid changes overwrite each other in a conflict resolution nobody reviews closely.

## Create worktrees from a named baseline

Create every agent worktree from an explicit baseline branch or commit. Do not let agents start from whatever happens to be checked out in a developer's directory. A named baseline makes it possible to answer the first question that matters during review: what did this task actually change?

Assume `main` is current, the repository root is `~/repos/billing-api`, and worktrees live under `~/repos/billing-api-wt`. An operator can create a worktree and branch like this:

```bash
cd ~/repos/billing-api
git fetch origin
git switch main
git pull --ff-only
git worktree add -b agent/auth-refresh \
  ../billing-api-wt/agent-auth-refresh main
```

`git worktree add -b` creates the branch and checks it out in the new directory. Git documents this as a first-class operation, rather than a workaround involving clones or copied folders. A linked worktree keeps separate administrative data so Git can tell the working directories apart.

Do not use `git checkout -b` inside a copied directory. Copied repositories cost disk space, drift in remotes and hooks, and hide whether the branches really belong to the same working set. More importantly, they invite agents to use whatever stale clone happened to exist.

For a disposable investigation, create a detached worktree instead:

```bash
cd ~/repos/billing-api
git worktree add -d ../billing-api-wt/repro-payment-timeout main
```

A detached worktree is appropriate for reproducing a production bug, comparing performance, or testing a risky code-generation command. It is not appropriate for a task expected to merge. An agent can create commits in detached HEAD, but those commits have no branch name carrying them forward. That is a poor place to leave work you expect another person to find tomorrow.

Record the baseline SHA in the task record when the work begins. The branch name tells you intent. The SHA tells you the exact starting state. When an agent says a test passed, you can then distinguish "passed before the dependency landed" from "passed against the branch we intend to ship."

## The merge queue follows dependencies, not completion time

A merge order is a dependency graph written in a form people can operate. It is not a FIFO queue of agent messages.

Start by sorting work into three classes. Foundation work changes schemas, public types, feature flags, shared configuration, or generated contracts. Dependent work consumes those changes. Independent work touches a separate module or test area and can merge whenever it passes its own checks.

For a release with an API contract, a service implementation, a UI change, and regression tests, the likely sequence is:

1. Merge the contract or migration branch.
2. Merge the service implementation after updating it against that result.
3. Merge the UI branch after it sees the final contract.
4. Merge regression tests and operational documentation when they match the delivered behavior.

That sequence will sometimes feel slower than letting every agent continuously rebase. It is faster in total. Repeated rebases distribute integration work across every agent, produce different conflict resolutions in different directories, and invalidate test results each time the base moves. One controlled integration point keeps the conflict decision visible.

Use the integration branch to make the order executable:

```bash
cd ~/repos/billing-api
git worktree add -b integrate/release-184 \
  ../billing-api-wt/integrate-release-184 main

cd ../billing-api-wt/integrate-release-184
git merge --no-ff agent/auth-refresh
git merge --no-ff agent/invoice-export
```

If your repository policy uses squash merges or a hosted pull request system, keep that policy. The point is not the merge flag. The point is that one branch receives approved work in a declared order and runs the combined checks after every meaningful addition.

Git's merge documentation explains that a fast-forward can simply move the branch pointer, while `--no-ff` creates a merge commit even when Git could fast-forward. Teams may prefer either history shape. Do not confuse a visible merge commit with integration discipline. The tests, review, and order make an integration branch trustworthy.

## One integration worktree owns cross-branch conflicts

Resolve cross-branch conflicts in an integration worktree, not in the agent worktree that happened to merge second. This is the rule that stops parallel work from tangling active branches.

Suppose the authentication agent adds `actor_id` to an audit-event constructor. The invoice export agent, started earlier, also creates audit events with the old constructor. Both branches can pass independently. When you merge them, Git reports a conflict or compilation failure. That is not proof either agent failed. It is proof the combined system has a decision to make.

The integrator should make that decision where the combined branch lives. They can ask the export agent for a follow-up commit, apply a narrow adjustment themselves, or reject one branch if the underlying contract was wrong. What they should not do is send both agents back to rebase blindly until green. That replaces an owned design decision with a race.

Use a small integration log in the task tracker or release note. It needs only the candidate branch, baseline SHA, merge order, test command, result, and owner of any conflict. That record gives you a useful answer when a regression appears: which combination first introduced it?

Run checks that can catch integration defects, not merely the checks each agent already ran. Typical examples are a full type check, migration validation from an empty database, contract tests, a production build, and the narrow end-to-end path affected by the release. Do not make every agent run the slowest suite after every edit. Run it at the point where branches become one product.

If a merge breaks, preserve the evidence. Keep the integration branch long enough to inspect the failed combination, or reset it deliberately after recording the last good commit. Do not delete the worktrees and reconstruct the state from chat messages. The branch graph is a better incident record than memory.

## Worktrees isolate files, not every shared resource

A worktree gives agents separate files, but it does not automatically give them separate services, caches, credentials, or external state. This is where teams declare victory too early.

Each worktree commonly needs its own dependency install because generated files and package metadata may differ by branch. Sharing a dependency cache can be fine when the package manager supports it. Sharing a mutable `node_modules`, virtual environment, build directory, or generated client output across branches is not fine. You will eventually test code against artifacts produced by another branch.

Reserve distinct local resources before agents start. Give every worktree its own application port, test database name, temporary directory, and local environment file. A simple convention is enough:

```bash
# .env.local, not committed
APP_PORT=4312
TEST_DATABASE_URL=postgres://localhost/billing_agent_auth_refresh
TMPDIR=/tmp/billing-agent-auth-refresh
```

Never place production credentials in an agent worktree. An autonomous process can read files in its directory, echo environment values in logs, or include a secret in a diagnostic patch. Use limited development credentials, short-lived tokens where your infrastructure supports them, and explicit allowlists for destructive commands.

Git also shares repository objects and references across linked worktrees. That is useful because commits made by one agent are immediately available to the integration worktree. It also means a worktree is not a security boundary between untrusted actors. If an agent should not access a repository's history, secrets, or remotes, it should not receive a worktree from that repository.

Submodules deserve special caution. Their state can add another checked-out repository below the main one, and Git restricts moving worktrees that contain submodules. Avoid assigning submodule updates to a general feature agent unless the task explicitly owns the version change and its validation.

## Removing abandoned work requires a decision first

An abandoned worktree is not merely a directory that has not changed recently. It may contain an unfinished diagnosis, local test data, uncommitted changes, or a branch another active task depends on. Clean it as an operator, not with a blind nightly delete script.

Start with an inventory:

```bash
cd ~/repos/billing-api
git worktree list -v
git status --short --branch
```

Run `git status` inside the candidate worktree as well. The first command tells you which worktrees Git knows about, which branch each has checked out, and whether Git considers a missing path prunable. The second tells you whether the current checkout is dirty. Git's manual documents `git worktree list -v` as the view that exposes extra state such as locked or prunable worktrees.

Then make one of three decisions.

- If the work is complete and merged, remove the clean worktree and later delete the merged branch according to your branch policy.
- If the work has useful commits but no owner, preserve the branch, write a short handoff note, and remove only the directory if it is clean.
- If the work has no value, inspect uncommitted changes once, then discard it deliberately.

Remove a clean worktree through Git:

```bash
cd ~/repos/billing-api
git worktree remove ../billing-api-wt/agent-test-repair
```

Git refuses to remove a dirty worktree unless you force it. That friction is intentional. Use `-f` only after preserving a patch, committing a salvage branch, or deciding the work is disposable. Do not let an automation job force-remove directories because they are older than an arbitrary number of days.

If someone already deleted a worktree directory in the file system, Git can retain stale metadata. Inspect first, then prune:

```bash
cd ~/repos/billing-api
git worktree prune -n -v
git worktree prune -v
```

The dry run is not ceremony. It tells you exactly which missing worktree records Git will remove. Git describes `prune` as cleanup for administrative records whose working tree paths are missing, and recommends `remove` for normal retirement. Those commands solve different problems.

Lock a worktree only when a real operator needs to protect it from accidental removal, such as a long-running release investigation. A lock is not a substitute for ownership. If the task has no owner, write down why it exists or retire it.

## A small operator contract beats elaborate agent orchestration

You do not need a complicated multi-agent framework to get value from worktrees. You need an operator contract that every agent and reviewer follows.

An assignment begins with a fresh worktree and branch, a written boundary, a known baseline, and a validation command. The agent commits coherent changes and reports the commit SHA, changed interfaces, test result, and any dependency discovered during the work. It does not merge its own branch into the release branch.

The integrator decides when the branch enters the queue. Before merging, they update the integration branch, inspect the diff against the intended baseline, run the combined checks, and resolve only the conflicts they own. If a conflict changes business behavior or a public interface, they send it back to the task owner with a precise request.

This contract makes agent throughput measurable. You can count completed branches, branches that needed rework after integration, conflicts caused by overlapping ownership, and time spent waiting on prerequisites. Those signals tell you whether adding agents helps. A dashboard that counts generated lines of code does not.

At AppMaster.io, reducing a much larger engineering operation to a small AI-augmented team required exactly this kind of operational discipline: clear ownership, controlled integration, and systems that reveal failure early. The tool choice changes, but the cost of ambiguous handoffs does not.

Start with two agents and one integration worktree on a release that has genuinely separate tasks. If that run produces clean handoffs and a comprehensible merge history, add another independent branch next time. If it produces constant conflicts, do not add more agents. Fix the task boundaries first.

## The first useful rule is to stop merging from agent desks

The operational change that pays off fastest is simple: agents work in their own worktrees, and only the integration worktree receives cross-branch merges. That single rule prevents most accidental staging, checkout disruption, and invisible conflict resolution.

Keep the primary checkout boring. Keep active agent directories obvious. Keep merge order written down before work begins. Retire finished worktrees through Git, inspect missing paths before pruning, and preserve anything that may matter during a later incident.

Parallel coding can increase delivery speed, but it increases the number of partial truths in your repository. Worktrees keep those partial truths separate long enough for an integrator to turn them into one tested change.
