Skip to main content
Guides

Git worktrees

This guide covers running commands in git worktrees — separate checkouts of your repository on different branches. Each worktree has its own working directory, so multiple runs work on separate branches simultaneously.

Prerequisites

  • Moat installed
  • A git repository (moat wt also requires an agent.yaml at the repository root)

Quick start

Start a run on a new branch:

moat wt dark-mode

This creates the dark-mode branch from HEAD (if it doesn’t exist), creates a worktree at ~/.moat/worktrees/<repo-id>/dark-mode, and starts the command defined in agent.yaml.

How it works

moat wt <branch> does three things:

  1. Creates the branch from HEAD if it doesn’t already exist
  2. Creates a git worktree — a separate checkout of the branch at ~/.moat/worktrees/<repo-id>/<branch>
  3. Starts a run using the command from agent.yaml with the worktree as its workspace

The repository is identified by its remote URL (or local path if no remote is configured). Each branch gets its own directory under that repo ID.

If the branch and worktree already exist, they are reused.

Two ways to use worktrees

The moat wt command

moat wt reads agent.yaml from the repository root and starts a run in the worktree:

# Start the command defined in agent.yaml on the dark-mode branch
moat wt dark-mode

# Run in background
moat wt dark-mode -d

# Run a specific command instead of the agent.yaml default
moat wt dark-mode -- make test

This requires an agent.yaml in the repository root.

The --worktree flag

The --worktree flag works on moat claude, moat codex, and moat gemini:

# Start Claude Code on the dark-mode branch
moat claude --worktree dark-mode

# Start Codex on a feature branch with a prompt
moat codex --worktree feature/auth -p "implement OAuth login" -d

# Start Gemini on a refactor branch
moat gemini --worktree cleanup

The --worktree flag creates the branch and worktree the same way as moat wt, but starts the specified agent instead of reading agent.yaml. --wt is accepted as a shorthand alias.

Run naming

Run names are generated automatically:

  • If agent.yaml has a name field (or --name is passed), the run name is {name}-{branch}
  • Otherwise, the run name is {branch}

Override with --name:

moat wt dark-mode --name my-custom-name

Parallel branches

Start runs on multiple branches simultaneously:

moat wt feature/auth -d
moat wt feature/dark-mode -d
moat wt fix/login-bug -d

Each run gets its own worktree, its own container, and its own branch. Branch names with slashes (like feature/auth) are supported.

Check on all worktree runs:

$ moat wt list

BRANCH              RUN NAME               STATUS   WORKTREE
feature/auth        my-agent-feature/auth  running  ~/.moat/worktrees/github.com/my-org/my-project/feature/auth
feature/dark-mode   my-agent-feature/...   running  ~/.moat/worktrees/github.com/my-org/my-project/feature/dark-mode
fix/login-bug       my-agent-fix/login-bug stopped  ~/.moat/worktrees/github.com/my-org/my-project/fix/login-bug

Active run detection

If a run is already active in a worktree, moat wt returns an error:

Error: a run is already active in worktree for branch "dark-mode": my-agent-dark-mode (run_a1b2c3d4e5f6)
Attach with 'moat attach run_a1b2c3d4e5f6' or stop with 'moat stop run_a1b2c3d4e5f6'

The --worktree flag on agent commands behaves the same way.

Cleaning up

Remove worktree directories for stopped runs:

# Clean all stopped worktrees for the current repo
moat wt clean

# Clean a specific branch's worktree
moat wt clean dark-mode

moat wt clean removes the worktree directory and runs git worktree prune. It never deletes branches — your work remains in git.

Active worktrees (with running containers) are skipped.

moat clean also removes worktree directories as part of its broader cleanup (stopped runs, unused images, orphaned networks).

Worktree storage

Worktrees are stored at:

~/.moat/worktrees/<repo-id>/<branch>

The <repo-id> is derived from the repository’s remote URL in host/owner/repo format. For example, github.com:my-org/my-project.git becomes github.com/my-org/my-project. Repositories without a remote use _local/<directory-name>.

Override the base path with MOAT_WORKTREE_BASE:

export MOAT_WORKTREE_BASE=/mnt/fast-disk/worktrees
moat wt dark-mode

Configuration

moat wt reads agent.yaml from the repository root. If the worktree directory also contains an agent.yaml, the worktree’s copy takes precedence.

Branch-specific configuration works as follows:

  1. Start with agent.yaml in your main branch
  2. The worktree inherits it when the branch is created
  3. Modify the worktree’s agent.yaml for branch-specific settings (extra grants, different dependencies)

Example: parallel feature development

  1. Configure agent.yaml:

    name: my-agent
    
    grants:
      - anthropic
      - github
    
    dependencies:
      - node@20
      - git
      - claude-code
  2. Start runs on multiple branches:

    moat wt feature/auth -d
    moat wt feature/dark-mode -d
  3. Monitor progress:

    moat wt list
    moat logs run_a1b2c3d4e5f6
    moat attach run_a1b2c3d4e5f6
  4. When finished, clean up worktree directories:

    moat wt clean
  5. Branches remain in git. Merge as usual:

    git merge feature/auth
    git merge feature/dark-mode

Troubleshooting

”agent.yaml not found”

moat wt requires an agent.yaml in the repository root. Create one, or use moat claude --worktree / moat codex --worktree / moat gemini --worktree which work without agent.yaml.

”not inside a git repository”

Run moat wt from within a git repository. Worktrees are a git feature and require a git repo.

”a run is already active in worktree”

Another run is already active on that branch. Either attach to it or stop it first:

moat attach <run-id>
moat stop <run-id>

Worktree persists after stop

moat stop stops the container but does not remove the worktree. Use moat wt clean to remove stopped worktree directories, or moat clean to remove all stopped resources including worktrees.