| title | Git worktrees | |||||
|---|---|---|---|---|---|---|
| navTitle | Worktrees | |||||
| description | Use git worktrees for parallel work on multiple branches, each in its own container. | |||||
| keywords |
|
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.
- Moat installed
- A git repository (
moat wtalso requires amoat.yamlat the repository root)
Start a run on a new branch:
moat wt dark-modeThis 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 moat.yaml.
moat wt <branch> does three things:
- Creates the branch from HEAD if it doesn't already exist
- Creates a git worktree — a separate checkout of the branch at
~/.moat/worktrees/<repo-id>/<branch> - Starts a run using the command from
moat.yamlwith 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.
moat wt reads moat.yaml from the repository root and starts a run in the worktree:
# Start the command defined in moat.yaml on the dark-mode branch
moat wt dark-mode
# Run a specific command instead of the moat.yaml default
moat wt dark-mode -- make testThis requires a moat.yaml in the repository root.
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"
# Start Gemini on a refactor branch
moat gemini --worktree cleanupThe --worktree flag creates the branch and worktree the same way as moat wt, but starts the specified agent instead of reading moat.yaml. --wt is accepted as a shorthand alias.
Run names are generated automatically:
- If
moat.yamlhas anamefield (or--nameis passed), the run name is{name}-{branch} - Otherwise, the run name is
{branch}
Override with --name:
moat wt dark-mode --name my-custom-nameStart runs on multiple branches simultaneously, each in its own terminal:
# Terminal 1
moat wt feature/auth
# Terminal 2
moat wt feature/dark-mode
# Terminal 3
moat wt fix/login-bugEach 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-bugIf 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)
Follow with 'moat logs -f run_a1b2c3d4e5f6' or stop with 'moat stop run_a1b2c3d4e5f6'
The --worktree flag on agent commands behaves the same way.
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-modemoat 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).
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-modemoat wt reads moat.yaml from the repository root. If the worktree directory also contains a moat.yaml, the worktree's copy takes precedence.
Branch-specific configuration works as follows:
- Start with
moat.yamlin your main branch - The worktree inherits it when the branch is created
- Modify the worktree's
moat.yamlfor branch-specific settings (extra grants, different dependencies)
-
Configure
moat.yaml:name: my-agent grants: - anthropic - github dependencies: - node@22 - git - claude-code
-
Start runs on multiple branches:
moat wt feature/auth moat wt feature/dark-mode
-
Monitor progress:
moat wt list moat logs run_a1b2c3d4e5f6 moat logs -f run_a1b2c3d4e5f6
-
When finished, clean up worktree directories:
moat wt clean
-
Branches remain in git. Merge as usual:
git merge feature/auth git merge feature/dark-mode
moat wt requires a moat.yaml in the repository root. Create one, or use moat claude --worktree / moat codex --worktree / moat gemini --worktree which work without moat.yaml.
Run moat wt from within a git repository. Worktrees are a git feature and require a git repo.
Another run is already active on that branch. Follow its logs or stop it first:
moat logs -f <run-id>
moat stop <run-id>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.
If your repository uses git submodules or subtrees, worktrees do not automatically initialize them. Add initialization to your moat.yaml command or pre_run hook:
hooks:
pre_run: git submodule update --init --recursiveSubmodule HEAD pointers in a worktree are frozen at the commit recorded in the parent repo at branch creation time. Run git submodule update --remote inside the worktree to pick up newer commits.
To give an agent access to multiple independent repos at once, pass their parent directory as the workspace:
moat run ~/devThis mounts ~/dev at /workspace, giving the agent access to all repos underneath. However, moat wt does not work here because the parent directory is not a git repository — there is no branch to create a worktree from.
For worktree-style isolation across independent repos, one approach is a wrapper git repository above the child repos (with the repos in .gitignore) and a pre_run hook that clones or checks out matching branches. This requires project-specific scripting and is beyond the scope of this guide.
- Running Claude Code — Use
--worktreewith Claude Code - Running Codex — Use
--worktreewith Codex - Running Gemini — Use
--worktreewith Gemini