Git worktrees let you check out multiple branches of the same repo simultaneously, each in its own directory, without cloning the repo multiple times. They're perfect for running AI coding agents in parallel — each agent gets its own worktree so they can work on separate branches without stepping on each other, while sharing the same git history and object store.
The built-in git worktree commands are verbose and require managing full paths. aw wraps them with short, ergonomic commands: aw feat-login creates a worktree and cd's into it immediately, aw with no args takes you back to the main repo, and aw rm lets you interactively clean up when you're done.
- Git
- One of: bash, zsh, fish, or PowerShell
Download a pre-built binary from the latest release, extract it, and place it somewhere on your PATH. Then add the shell wrapper from the Setup section.
Linux / macOS:
# Download and extract (replace TARGET with your platform)
# Targets: x86_64-unknown-linux-gnu, aarch64-unknown-linux-gnu,
# x86_64-apple-darwin, aarch64-apple-darwin
curl -L https://github.com/PNRxA/aw/releases/latest/download/aw-TARGET.tar.gz | tar xz
sudo mv aw /usr/local/bin/Windows:
Download aw-x86_64-pc-windows-msvc.zip from the latest release, extract aw.exe, and move it to a directory on your PATH.
Alternatively, you can build from source.
Add the shell wrapper to your shell config so aw can cd into worktrees:
bash (add to ~/.bashrc):
eval "$(aw init bash)"zsh (add to ~/.zshrc):
eval "$(aw init zsh)"fish (add to ~/.config/fish/config.fish):
aw init fish | sourcePowerShell (add to $PROFILE):
aw init powershell | Invoke-ExpressionWithout the shell wrapper, commands that change directories will print the path instead.
A subprocess can't change its parent shell's working directory. To work around this, aw init bash prints a small shell function named aw that wraps the binary. eval "$(…)" evaluates that output, defining the function in your current shell.
When you run aw add feat-login, the wrapper calls the real aw binary and reads its stdout line by line. The wrapper intercepts lines with special prefixes and acts on them in your shell:
| Prefix | Action |
|---|---|
__AW_CD__:/some/path |
Runs cd /some/path in your shell |
__AW_EXEC_ARG__:arg |
Collects an argument for a pending command |
__AW_EXEC_RUN__ |
Executes the collected arguments as a command |
All other lines are printed normally.
The __AW_CD__ protocol is the only way a CLI tool can change your shell's directory. The __AW_EXEC__ protocol works similarly — when you run aw feat-login claude, the binary emits __AW_EXEC_ARG__:claude followed by __AW_EXEC_RUN__, and the wrapper executes claude in the new worktree directory. This ensures the command runs as a direct child of your shell rather than as a subprocess of aw, which matters for interactive programs that need proper terminal access.
| Command | Description |
|---|---|
aw |
Return to the main worktree (or show help) |
aw <name> [CMD...] |
Create worktree and cd into it, optionally run CMD |
aw add <name> [--base REF] [CMD...] |
Create worktree with optional base ref, optionally run CMD |
aw list / aw ls |
List all worktrees with indices |
aw go [name|#] |
Switch to a worktree by name, index, or interactively |
aw rm [name|#] [-f] |
Remove a worktree by name, index, or interactively |
aw merge [name|#] [--into name|#] |
Merge a worktree's branch into the main worktree (or --into target) |
aw path [name|#] |
Print a worktree's absolute path by name, index, or interactively |
aw prune |
Clean up stale worktree entries |
aw init <shell> |
Print shell wrapper (bash, zsh, fish, powershell) |
aw version / aw -v |
Print version |
aw help |
Show help |
awRunning aw with no arguments from inside a non-main worktree cd's you back to the main worktree. From the main worktree it shows help.
Runs git worktree list --porcelain to find the main worktree path.
aw feat-login # shorthand for 'aw add feat-login'
aw add feat-login
aw add feat-login --base developRunning aw feat-login inside /code/myproject creates a new worktree at /code/myproject-feat-login with a branch named feat-login, then cd's into it.
Slashes in names become dashes in directory names (feature/foo becomes myproject-feature-foo).
Runs git worktree add -b <branch> <path> (or git worktree add -b <branch> <path> <base> with --base).
aw feat-login claude # create worktree and launch claude
aw feat-login npm install # create worktree and install deps
aw add feat-login --base develop npm installAnything after the worktree name (and optional --base REF) is run as a command in the new worktree directory. The exit code reflects the command's result. Use -- to separate flags from the command if needed: aw feat-login -- --some-tool.
aw list # or 'aw ls'Runs git worktree list --porcelain.
Output includes an index and marks the current worktree with *:
* 0 myproject [main]
1 myproject-feat-login [feat-login]
2 myproject-bugfix-42 [bugfix-42]
aw go feat-login # switch by name
aw go 1 # switch by index (from 'aw list')
aw go # interactive: shows worktrees, prompts for indexRuns git worktree list --porcelain to resolve the target, then cd's into it.
aw rm feat-login # remove by name
aw rm 1 # remove by index (from 'aw list')
aw rm # interactive: shows worktrees, prompts for index
aw rm feat-login -f # remove even with uncommitted changesRuns git status --porcelain to check for uncommitted changes (unless --force), then git worktree remove <path> (or git worktree remove --force <path>).
aw merge feat-login # merge feat-login into main worktree
aw merge 1 # merge worktree at index 1 into main worktree
aw merge feat-login --into dev # merge feat-login into the 'dev' worktree
aw merge # interactive: prompts for source worktreeMerges the source worktree's branch into the target worktree (default: main worktree at index 0). On success, prompts to remove the source worktree and delete its branch. In interactive mode, if you're currently inside a worktree it is pre-selected as the default — just press Enter to confirm.
Runs git status --porcelain on both worktrees to check for uncommitted changes, then git merge <branch> --no-edit from the target worktree. On cleanup: git worktree remove <path> and git branch -d <branch>.
aw merge runs several checks before performing the merge to prevent data loss:
- Self-merge — refuses to merge a worktree into itself.
- Detached HEAD / bare — refuses if either the source or target worktree has no branch (detached HEAD or bare repository).
- Dirty target — refuses if the target worktree has uncommitted changes. You must commit or stash them first. There is no override for this.
- Dirty source — warns if the source worktree has uncommitted changes (these won't be included in the merge) and asks for confirmation before continuing.
- Merge conflicts — if the merge produces conflicts,
awcd's you into the target worktree and prints instructions for manual resolution (git add+git commit).
Post-merge cleanup uses git branch -d (safe delete), which refuses to delete a branch that isn't fully merged, so no work can be silently lost.
aw path feat-login # print path by name
aw path 1 # print path by index (from 'aw list')
aw path # interactive: shows worktrees, prompts for indexRuns git worktree list --porcelain to resolve the target and prints its path.
Useful for scripting, e.g. cp file $(aw path feat-login)/.
aw pruneRuns git worktree prune -v.
aw help- Remove the
eval "$(aw init …)"/aw init fish | source/aw init powershell | Invoke-Expressionline from your shell config (~/.bashrc,~/.zshrc,~/.config/fish/config.fish, or$PROFILE). - Remove the binary (wherever you placed it during install, e.g.
/usr/local/bin/aw):sudo rm /usr/local/bin/aw
- Any worktrees you created with
aware regular git worktrees. Clean them up withgit worktree remove <path>or just delete the directories and rungit worktree prunefrom the main repo.
Worktrees are created as siblings of the repo root:
/code/
myproject/ # main worktree
myproject-feat-login/
myproject-bugfix-42/
On Windows the layout is the same, just with Windows paths (e.g. C:\code\myproject\).
Requires Cargo (Rust toolchain).
cargo install --path .Cargo installs binaries to ~/.cargo/bin (or %USERPROFILE%\.cargo\bin on Windows). Make sure this directory is on your PATH.
To uninstall:
cargo uninstall awReleases are automated with GitHub Actions. To publish a new release:
- Update the version in
Cargo.toml - Commit the version bump:
git add Cargo.toml git commit -m "v0.2.0" - Tag the commit and push:
git tag v0.2.0 git push origin main v0.2.0
This triggers the release workflow which builds binaries for all supported platforms (Linux x86_64/aarch64, macOS x86_64/aarch64, Windows x86_64), generates SHA256 checksums, and creates a GitHub Release with the artifacts attached.