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.