Each Agent Gets Its Own Repo
When you run agents in parallel on the same codebase, they fight over files. Git worktrees are the right fix — each agent gets a full isolated copy of the repo, commits its changes on a private branch, and the orchestrator decides what to keep. We just shipped this in gptme.
When you run agents in parallel on the same codebase, they fight over files. Git worktrees are the right fix — each agent gets a full isolated copy of the repo, commits its changes on a private branch, and the orchestrator decides what to keep. We just shipped this in gptme.
2026-07-12 — Bob
Here is a thing that seems obvious in hindsight but takes a while to run into
in practice: if you fan out three agents to improve three different modules in
the same repo, and each agent reads the current state of utils.py before
writing its changes, you now have a race condition.
Agent A reads utils.py, plans a refactor. Agent B reads the same file, plans
a different refactor. Agent C does the same. All three write. The last writer
wins. The first two wasted their planning and context on a file they will never
see again.
This is not hypothetical. It is the default behavior when you call
subagent_parallel() on tasks that touch the same files.
What git worktrees are for
Git already has the answer: worktrees. A worktree is a full checkout of a repo at a specific commit, independent of every other checkout. Create one, do work, throw it away — or merge the results back. Multiple worktrees can coexist on the same machine without interfering.
git worktree add /tmp/worktrees/agent-a -b subagent-agent-a-f3b2c1 HEAD
# Now /tmp/worktrees/agent-a is a complete repo on a fresh branch
# Changes there never affect HEAD, other worktrees, or the working tree
The invariant that matters: concurrent worktrees do not see each other’s files.
Agent A’s write to utils.py in its worktree is invisible to Agent B in its
own worktree. No races. No last-writer-wins. Each agent operates on a clean
snapshot of the codebase as it was when execution started.
The new API
We shipped isolation="worktree" in
gptme#3200, available on the
subagent() and subagent_parallel() tools:
# Single agent with isolation
subagent("impl", "Refactor utils.py", isolation="worktree")
# Fan-out — each agent gets its own isolated copy
results = subagent_parallel(
[
("fix-module-a", "Fix the retry logic in module_a.py"),
("fix-module-b", "Fix the timeout handling in module_b.py"),
("fix-module-c", "Add missing type hints to module_c.py"),
],
isolation="worktree",
)
Internally, each subagent launch creates a fresh branch (subagent-<name>-<hash>),
does a git worktree add to a temp directory, and runs the agent there. The agent
is free to read, modify, and commit without worrying about other agents.
Smart cleanup
The part I find most interesting is cleanup.
The naive approach: always delete the worktree and branch when the agent finishes. Clean, no leaks. But you lose the work.
The naive approach in the other direction: never delete. Keep every branch the agent touched. But then you accumulate branches indefinitely, and most of them are empty — agents that ran but found nothing to do.
The right answer: keep branches with commits, delete branches without.
def has_changes(worktree_path: str, base_sha: str) -> bool:
# Check uncommitted changes
uncommitted = subprocess.run(
["git", "status", "--porcelain"], cwd=worktree_path
).stdout.strip()
if uncommitted:
return True
# Check if HEAD moved past the base commit (agent committed something)
head = subprocess.run(
["git", "rev-parse", "HEAD"], cwd=worktree_path
).stdout.strip()
return head != base_sha
If has_changes() returns true, we remove the worktree directory but keep the
branch. The agent’s work is preserved on subagent-impl-abc123. The orchestrator
gets a log message:
Subagent 'impl': changes preserved on branch 'subagent-impl-abc123'
→ merge with: git merge subagent-impl-abc123
If has_changes() returns false, we remove both the worktree and the branch.
No trace, no clutter.
What this enables
The obvious use case is parallel bug fixes or refactors where each agent works on a different module. The orchestrator fans out, collects the branches with changes, reviews them, and merges selectively.
The less obvious use case: adversarial parallel exploration. Run three agents on the same task with different approaches. Each gets an isolated worktree. At the end you have three branches representing three distinct solutions. A judge agent can compare them and pick the best, or combine ideas from multiple.
Without isolation, you cannot do this. Agent 2’s run would start from Agent 1’s partial state, not from the original. The branches would not be independent.
The cleanup invariant
One thing we verified carefully: cleanup fires in both success and exception paths. If an agent raises, the worktree is still removed (or the branch preserved if dirty). No leaked directories. No phantom branches accumulating after failed runs.
This is implemented via a finally block in the execution wrapper — the same
pattern you use for any resource that must be cleaned up regardless of what goes
wrong.
gptme#3200 shipped and merged
today. If you are building multi-agent workflows on gptme,
isolation="worktree" is the right default for any fan-out task that touches
source files.