Abstract
I use Git worktrees to run coding agents in parallel, with a branch and directory for each task. I’ll show how I organize and create worktrees, review the agents’ changes and pull requests, and clean up when I’m done.
Why I use worktrees
Most projects have several open issues at once. Working on them in a single directory means stashing changes and switching branches. If two agents use that directory, they can overwrite each other’s files.
I use Git worktrees to give each task its own workspace.
How Git worktrees work
With git worktree, you can check out several branches from one repository.
Each worktree has its own files, staging area, and HEAD, while sharing Git
objects and branch references. A linked worktree’s .git file points back to
the shared repository. See the Git worktree documentation.
Organize the directories
I keep each project in a parent directory with this layout:
project-name/
main/ # Clone on the main branch; contains .git/
feat/
login/ # Worktree on feat/login
fix/
timeout/ # Worktree on fix/timeout
refactor/
parser/ # Worktree on refactor/parser
pull/
123/ # Worktree for pull request 123
I use the main worktree, main/, to update the base branch and coordinate
tasks. I group task worktrees under feat/, fix/, and refactor/, with a
child directory for each task. Pull request checkouts go in pull/<number>/.
The parent project-name/ sits outside the repository. The group directories
are ordinary folders; each task directory contains a complete checkout. Add
children as you need them, such as feat/export/ or fix/cache/.
Prepare the project
The commands below use a shell on Linux or macOS, with main as the base
branch and origin as the remote. Replace the project name and repository URL.
Start in the directory where you keep your projects:
mkdir -p project-name
cd project-name
mkdir -p feat fix refactor pull
git clone --branch main https://github.com/OWNER/REPO.git main
git -C main fetch origin
Run the remaining commands from project-name/. The git -C option selects
the checkout, so you don’t need to change directories between commands.
Create task worktrees manually
Give each task a branch and a worktree:
# Feature
git -C main worktree add --no-track -b feat/login ../feat/login origin/main
# Bug fix
git -C main worktree add --no-track -b fix/timeout ../fix/timeout origin/main
# Refactor
git -C main worktree add --no-track -b refactor/parser ../refactor/parser origin/main
The -b option names the new branch. The next argument is the worktree path,
relative to main/. Each branch starts at the commit from origin/main.
I use --no-track to avoid setting origin/main as the task branch’s upstream.
I set the upstream when I push the branch. See Git’s
branch documentation.
If you already have a local feat/login branch, use:
git -C main worktree add ../feat/login feat/login
Git normally allows a branch to be checked out in only one worktree, so give each agent a different branch. To list the worktrees, run:
git -C main worktree list
Create a pull request worktree
To review GitHub pull request 123, fetch its head and create a worktree:
git -C main fetch origin pull/123/head
git -C main worktree add --detach ../pull/123 FETCH_HEAD
Run these commands together because FETCH_HEAD refers to the latest fetch.
GitHub documents the pull request ref in its
local review guide.
The worktree has a detached HEAD at the fetched revision. You can review and
test that revision, but the checkout won’t update when the author pushes more
commits.
Create a branch first if you need to commit changes:
git -C pull/123 switch -c review/pr-123
Agent orchestration
I use one coordinator and assign an implementation agent to each task worktree. I choose a model with stronger reasoning for the coordinator. Smaller models can handle focused tasks.
The coordinator assigns tasks, reviews diffs, and prepares pull requests. Implementation agents work in their assigned directories.
Specify the directory, branch, and expected behavior in each agent’s task:
Work in <workspace>/project-name/feat/login on branch feat/login.
Read the repository instructions before editing.
Add login form validation. Keep the change focused on that behavior.
Run the relevant tests.
Report the changed files, test results, and remaining questions.
Replace <workspace> with the absolute path and start the agent in that
directory. Agree on the expected behavior and who will commit the changes.
Choose tasks with few dependencies. If two tasks change the same interface, agree on it before starting. Separate directories prevent file overwrites, but you can still get merge conflicts.
Set up each environment
A new worktree contains the checked-out files, but it doesn’t copy ignored
files from main/. Follow the project’s setup instructions to install
dependencies and prepare local configuration in each worktree.
Worktrees share the host’s resources. Keep build output and virtual environments in their task directories. Give parallel servers different ports, and use separate test databases when tasks change database state.
Review and integrate the result
Review the local edits before asking the agent to commit:
git -C feat/login status --short
git -C feat/login diff
git -C feat/login diff --cached
Once the changes are committed, compare the branch with the base branch:
git -C main fetch origin
git -C main diff origin/main...feat/login
Read the diff to check that the change solves the assigned problem. Review
the tests too, then run the relevant tests in feat/login/.
When you’re satisfied with the branch, push it and open a pull request:
git -C feat/login push -u origin feat/login
Integrate one task at a time. If a merge affects another task, update that task’s branch and rerun its tests.
Remove finished worktrees
After merging the pull request, update main/ and check for local work you
still need to keep. Then remove the task worktree:
git -C main pull --ff-only
git -C feat/login status --short
git -C main worktree remove ../feat/login
git -C main branch -d feat/login
worktree remove refuses to remove a dirty checkout. Delete the branch
separately. See the
worktree removal rules.
With branch -d, Git checks the branch’s upstream if one is set, or HEAD
otherwise. After a squash merge, this check can consider the original commits
unmerged. If Git refuses to delete the branch, keep it until you’ve verified
that its changes were integrated. The
branch deletion rules
explain that check.
When you’ve finished reviewing a pull request, remove its worktree:
git -C main worktree remove ../pull/123
The downsides
Worktrees avoid repeated clones of Git history, but each checkout still takes disk space for source files, dependencies, and build output. Agents running in parallel also compete for CPU and memory.
Agents can miss instructions or skip checks, and each parallel task adds another diff to review. I start with two independent tasks and add more when I can keep up with the results.
I treat the agent’s output as a draft. I use the test results and the diff to decide whether it’s ready to merge.
A note on tmux
I use tmux with one session per project and one window per task. From
project-name/, create the session and a window for the feature:
tmux new-session -d -s project-name -n main -c "$PWD/main"
tmux new-window -t project-name -n login -c "$PWD/feat/login"
tmux attach-session -t project-name
Each window starts in its task’s checkout. I name the windows after the tasks so I can find the right agent quickly.