All articles
/Updated Mar 3, 2026/16 min read/Vibehackers Team

Git Worktrees: From Running Multiple Agents to Real Multi-Agent Development

You're running multiple AI agents on the same codebase, but you're still the one coordinating every commit and branch switch. Git worktrees let you stop babysitting and start dispatching — here's how.

#git#multi-agent#claude-code#cursor#workflow#tutorial

incident.io estimated a task would take two hours. It took ten minutes. Not because their AI agent was unusually fast — because they ran five of them, truly in parallel, each one autonomous from start to finish. Each agent had its own branch, its own directory, its own ability to commit and push and open a PR without waiting for anyone.

That’s not how most of us run multiple agents today.

Two terminals, one directory

Most multi-agent setups look like this: two Claude Code sessions open, both pointed at the same project directory. Terminal one is building an API endpoint. Terminal two is writing tests. You keep an eye on both, make sure they’re working on different files. When terminal one finishes, you commit its work. Then terminal two. You coordinate.

This works. But you’re doing something that doesn’t scale: you’re the synchronization layer. You’re mentally partitioning the codebase, sequencing commits, tracking which agent is in which part of the code. With two agents it’s manageable. With three it gets stressful. With five — the number incident.io runs daily — it’s impossible.

The difference between “running multiple agents” and actual multi-agent development is whether the agents can operate autonomously. Can each one own its workstream end-to-end — edit, commit, push, PR — without you coordinating anything? In a shared directory, the answer is no. Everything is mixed together: files, staging area, branch. You can’t commit one agent’s work without accidentally including the other’s.

Loading tweet...

What if each agent had its own directory?

The obvious solution: give each agent its own copy of the codebase. One directory per agent. They can’t collide because they’re in completely separate folders, each on their own branch.

The first instinct is git clone. Clone the repo three times, point each agent at a different clone. This works. But the more you use it, the more it nags at you.

The problem with clones isn’t disk space or network bandwidth — storage is cheap, connections are fast. The problem is they’re disconnected universes.

You commit something in Clone A. Can Clone B see it? No. You have to push to remote from A, then fetch from remote in B. There’s no direct link between them. Every clone talks to the world through the remote, never to each other.

You create a branch in Clone A. Clone B doesn’t know it exists until you push it. You change a config file in Clone A. Clone B has the old config. You add an env variable in Clone A. Clone B doesn’t have it. After a week you have three clones that have subtly drifted apart, and you spend twenty minutes debugging something that works fine — just not in the clone you’re currently in.

It’s not a technical problem. It’s a cognitive one. Three clones of the same repo means three contexts to keep in sync, and none of them do it automatically.

There’s a better primitive for this. It’s built into git, and it solves exactly the problem clones create.

Git worktree

The command is git worktree add:

~/my-project
$git worktree add ../my-project-feature -b feature/new-api
Preparing worktree (new branch 'feature/new-api')
HEAD is now at a1b2c3d Latest commit on main

This created two things: a new directory at ../my-project-feature, and a new branch called feature/new-api. The new directory is a full checkout of your repo on that branch. You can cd into it, edit files, run your dev server, do anything you normally do.

~/my-project
$ls ../
my-project/
my-project-feature/

Two directories. You can open each in its own terminal, its own editor. Both are fully functional. But here’s where it gets interesting. Look inside the new directory:

~/my-project-feature
$cat .git
gitdir: /Users/you/my-project/.git/worktrees/my-project-feature

That’s not a .git directory. It’s a tiny .git file — one line, pointing back to the original repository’s .git folder. The new directory doesn’t contain a copy of the git database. It doesn’t have its own commit history. It doesn’t have its own branches.

It shares everything with the original. This feature shipped in Git 2.5 back in 2015, and flew so far under the radar that even Guido van Rossum only discovered it years later:

Loading tweet...

What’s shared and what isn’t

This is the part that matters.

When you create a worktree, it shares the entire git object database with the original. Every commit, every branch, every tag — one copy, shared across all worktrees. A commit made in any worktree is instantly visible from every other worktree. No pushing, no fetching, no syncing. Because there’s nothing to sync — it’s the same database.

~/my-project-feature
$git log --oneline -1 main
f4e5d6c A commit made in the other directory just now

That commit appeared without any git pull. It was made in ~/my-project, but it’s visible here because both directories are reading from the same .git.

What’s not shared: the files on disk, the staging area, and which branch is checked out. Each worktree has its own working directory, its own index, its own HEAD. They’re independent workspaces that happen to share a backend.

Think of it this way. A git repo is a database. Normally you interact with it through one working directory — one set of files, one staging area, one branch. A worktree is a second interface to the same database. A second set of files, a second staging area, a second branch. As many interfaces as you want, all reading from and writing to the same store.

The problem worktrees already solved

Before AI agents entered the picture, developers had this problem for decades. You’re deep in a feature branch — halfway through a refactor, files changed everywhere, nothing compiles. Then Slack lights up: production bug, needs a hotfix now.

What do you do?

Option 1: git stash. You stash your work-in-progress, switch to main, make the fix, commit, push, switch back, git stash pop. Sounds clean. In practice, stash is a footgun. Stash doesn’t track which branch you were on. It doesn’t save untracked files unless you remember --include-untracked. Pop a stash onto the wrong branch and you’re untangling merge conflicts in code you weren’t even working on. People have lost days of work to stash mishaps — GitHub Desktop had a bug that silently wiped stashed changes, and VS Code users have reported stashes vanishing after updates.

Option 2: WIP commit. You commit half-broken code with a message like wip don't look at this, switch branches, fix the bug, switch back, then try to remember what state you were in. Your git history looks like a crime scene.

Option 3: Just context-switch. Discard your mental state, handle the hotfix, come back an hour later and spend twenty minutes remembering where you were. This is what most people actually do. It’s expensive in ways that don’t show up on any dashboard.

Worktrees make all three options unnecessary. You keep your feature branch exactly as it is — mid-refactor, broken tests, whatever — and open a second worktree on main. Fix the bug there. Commit, push. Close the worktree. Your feature branch never noticed anything happened. No stashing, no WIP commits, no context-switching.

The same applies to code review. Someone opens a PR and you want to actually run their code, not just read the diff. Without worktrees: stash your work, switch branches, npm install (because they added a dependency), run the code, switch back, npm install again (because your branch has different dependencies), pop stash. With worktrees: open their branch in a second directory, run it there. Your work is untouched.

Worktrees have been in git since 2015. For eleven years, they’ve been solving these exact problems — and most developers never knew.

Why this isn’t just a party trick

OK so git has this feature. Two directories, shared database. Cute.

Let me bring it back to the thing that was nagging you earlier.

Remember the coordination work? Making sure agents don’t edit the same files, sequencing commits, switching branches between agents? That coordination exists because all your agents share one set of files on disk. One staging area. One branch. There’s only one “workspace,” and every agent is operating inside it simultaneously.

With worktrees, each agent gets its own workspace. Its own files, its own staging area, its own branch. Agent 1 can freely edit package.json, commit, and push — while Agent 2 is doing the exact same thing in its own worktree, on its own branch. They can’t collide because they’re not touching the same files. Not “they probably won’t collide” — they structurally cannot.

And here’s what that unlocks: you can let go.

You can tell an agent “build this feature, commit when you’re done, push the branch, open a PR.” And then forget about it. You don’t need to watch it. You don’t need to coordinate it with other agents. You don’t need to stage its files separately. It owns its workstream from start to finish.

That’s the difference between “running multiple agents” and “multi-agent development.” It’s not a difference in tools. It’s a difference in how much autonomy you can give each agent. And autonomy requires isolation.

What the tools already do

This isn’t theory. The major AI coding tools have already built worktree support into their core workflow.

Claude Code

When you run Claude Code with the --worktree flag, you pass it a name — any name you choose. Claude creates two things: a directory and a branch.

~
$claude --worktree billing-api

This creates a worktree directory at .claude/worktrees/billing-api/ and a branch called worktree-billing-api. Claude then starts a session inside that directory, isolated from your main codebase.

Why .claude/worktrees/? It’s a convention. The .claude/ directory is already in your .gitignore (it stores Claude’s project-level config), so worktrees created inside it don’t clutter your project root. You could put worktrees anywhere — ../billing-api, /tmp/billing-api, wherever. Claude just picks a sensible default.

When Claude finishes and you close the session: if it made no changes, the worktree and branch are cleaned up automatically. If it committed work, Claude asks if you want to keep or remove the worktree. The branch (with its commits) stays either way — your work is safe.

Boris Cherny, who built Claude Code, calls worktrees his “number one productivity tip” — he runs three to five simultaneously:

Loading tweet...

Running multiple agents is just opening multiple terminals:

Terminal 1
$claude --worktree auth-fix

Terminal 2
$claude --worktree billing-api

Terminal 3
$claude --worktree test-coverage

Three agents, three worktrees, three branches. Each agent can edit files, install packages, run tests, commit, push, and open PRs — all without you coordinating anything.

Cursor

Cursor 2.0 shipped parallel agents in October 2025, powered by git worktrees under the hood. Each of Cursor’s up to eight parallel agents operates in its own worktree. You can even spin up multiple agents on the same task and compare their outputs.

Loading tweet...

VS Code + Copilot

VS Code 1.107 added automatic worktree isolation for background agents. When a Copilot background agent starts working, VS Code silently creates a worktree for it.

OpenAI Codex

Codex added built-in worktree support too:

Loading tweet...

Every major AI coding tool converged on the same primitive. That’s not coincidence — it’s because worktrees solve the right problem at the right layer.

A real workflow, step by step

Let’s walk through a full multi-agent session. You have a feature to ship — a notification system — and you want to break it into three parallel workstreams.

Launch three agents, each in its own worktree:

~/my-project
$claude --worktree notif-api
$claude --worktree notif-ui
$claude --worktree notif-tests

Behind the scenes, this creates:

~/my-project
$git worktree list
/Users/you/my-project abc1234 [main]
/Users/you/my-project/.claude/worktrees/notif-api abc1234 [worktree-notif-api]
/Users/you/my-project/.claude/worktrees/notif-ui abc1234 [worktree-notif-ui]
/Users/you/my-project/.claude/worktrees/notif-tests abc1234 [worktree-notif-tests]

Each agent works independently. They install their own dependencies, make their own commits, run their own tests. You go get coffee.

When they’re done, each branch has a clean set of commits. You merge:

~/my-project
$git merge worktree-notif-api
$git merge worktree-notif-ui
$git merge worktree-notif-tests

Or better — each agent pushes its branch and opens a PR. You review and merge through GitHub. Same workflow you use with human teammates.

Cleanup is automatic if you used claude --worktree. If you created worktrees manually:

~/my-project
$git worktree remove .claude/worktrees/notif-api
$git worktree remove .claude/worktrees/notif-ui
$git worktree remove .claude/worktrees/notif-tests

Teams at incident.io run this workflow daily — four to five agents in parallel. A task estimated at 2 hours done in 10 minutes. Not because the agents are faster. Because five autonomous agents is a different kind of leverage than one agent you’re babysitting.

Things to know

A few constraints and gotchas worth knowing before you start.

Each worktree needs its own npm install. The git database is shared, but node_modules, build caches, .next — all per-directory. Each worktree is a full working directory, so it needs its own dependencies installed. For a typical Node.js project that’s 200-500MB per worktree.

One branch per worktree. Git enforces this — you can’t check out main in two worktrees at the same time. If you try:

~/my-project
$git worktree add ../another main
fatal: 'main' is already checked out at '/Users/you/my-project'

This is intentional. Two worktrees modifying the same branch would corrupt state. The constraint forces each workstream onto its own branch — which is what you want anyway.

Port conflicts. If agents try to run dev servers, they’ll fight over the same port. Give each worktree a different port in its .env, or let your framework pick one automatically.

Merge conflicts happen at merge time, not runtime. If two agents both modify package.json on different branches, you’ll resolve the conflict when you merge — same as with any branching workflow. The difference is you’re resolving it once, cleanly, instead of discovering it mid-session when both agents are still running.

Don’t rm -rf a worktree directory. Use git worktree remove. If you do delete it manually, git worktree prune cleans up the stale metadata.

Where to put worktrees. Claude Code defaults to .claude/worktrees/ — already gitignored, since .claude/ stores project config. Some teams prefer .worktrees/ at the project root — one gitignore entry, tool-agnostic, works with Cursor or Cline or whatever you’re running next month. Others use sibling directories (../my-project-feature), the traditional git approach that doesn’t need any gitignore at all. Pick one and be consistent. When your team runs five agents daily, everyone should know where to look.

Beyond the basics

Once worktrees click, some interesting patterns emerge.

The race

Spin up multiple agents on the same task, each in its own worktree. Compare the results. Keep the best implementation. Cursor 2.0 was built around this idea.

Agent teams

Claude Code has an experimental feature where a lead agent spawns teammates, each in their own worktree, with shared task lists and async messaging:

~ — Agent Teams
$CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1 claude

The lead breaks down a task, teammates claim subtasks, each works in isolation, and the lead merges the results.

Conflict detection

Tools like clash can detect merge conflicts between worktrees before they happen — using read-only three-way merges to warn you when two agents are touching the same code. You can even wire it up as a Claude Code hook that checks before every file write.

Loading tweet...

Guardrails

Worktrees give agents isolation. They don’t give agents judgment.

Nothing in Claude Code prevents an agent from running git push --force, git reset --hard, or rm -rf in its worktree. The isolation means it won’t corrupt your main directory — but it can still destroy its own branch, push garbage to your remote, or wipe its own work. Cursor handles this differently: it forces a manual “Apply” step before any worktree changes touch your code. Claude Code trusts the agent by default.

The fix is hooks. Claude Code’s PreToolUse hook fires before every tool execution — every shell command, every file edit, every write. If the hook exits with code 2, the action is blocked.

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": ".claude/hooks/git-safety.sh"
          }
        ]
      }
    ]
  }
}

The script inspects the command about to run and blocks destructive patterns — git push --force, git reset --hard, git clean -f, git branch -D. Matt Pocock published a ready-made version: git-guardrails-claude-code. Trail of Bits has a more opinionated config that also blocks credential reads and enforces feature branches.

For worktree setup automation, WorktreeCreate hooks fire whenever --worktree is invoked. Teams use these to auto-copy .env files, run npm install, and assign deterministic port numbers so dev servers don’t collide. The claude-worktree-hooks project does all three — it hashes the branch name to pick a port in the 3100–9999 range, so each worktree gets its own port every time.

One thing that works in your favor: git hooks are shared across all worktrees. Every worktree points back to the same .git directory, so a single pre-push hook installed once protects every worktree automatically. No per-worktree configuration needed.

And for cross-worktree conflict detection, wire clash into the same system:

{
  "matcher": "Write|Edit",
  "hooks": [{ "type": "command", "command": "clash check" }]
}

Every file write checks for conflicts with other worktrees first. The agent gets warned before it creates a merge problem, not after.

The cheat sheet

For reference — every command you need:

Git Worktree Commands
$git worktree add <path> -b <new-branch>
# Create a new directory + new branch
$git worktree add <path> <existing-branch>
# Create a new directory for an existing branch
$git worktree list
# Show all worktrees and their branches
$git worktree remove <path>
# Clean up a worktree when you're done
$git worktree prune
# Remove metadata for worktrees that were manually deleted

Claude Code Shortcuts
$claude --worktree <name>
# Creates .claude/worktrees/<name>/ and branch worktree-<name>
$claude --worktree
# Same, with an auto-generated name like bright-running-fox
$claude --worktree <name> --tmux
# Opens the worktree session in a tmux pane

What changes

I want to end with what actually shifted for me.

For a long time I thought of multi-agent development as “running multiple agents.” Two terminals, same directory, careful not to collide. It worked. I was productive. I didn’t think I was missing anything.

Worktrees didn’t fix something that was broken. They showed me a workflow I didn’t know existed — one where I dispatch tasks and review PRs, and the coordination between agents is handled by git itself. Each agent gets a branch. Each branch gets a directory. Each directory is an isolated workspace. That’s it. Git already knows how to merge branches. It’s been doing it for twenty years.

The interesting part isn’t the technology. It’s the shift in what you do. You stop managing files and start managing outcomes. You stop being the synchronization layer and start being the decision-maker. Five agents, five worktrees, five PRs. You review, you merge, you ship.

Start with one:

~
$claude --worktree my-first-worktree

Then try two. You’ll feel the difference immediately.

Ready to level up your AI-assisted development workflow? Our step-by-step tutorial covers everything from context engineering to multi-agent orchestration.

Learn Vibe Coding →

Continue reading