WTR is a small tool to manage multiple git worktrees. It removes the need to remember absolute paths and standardises a layout so that every worktree of a project lives under a single base directory.
With mise
mise use -g github:abogoyavlensky/wtr@latestOr pin a version in .mise.toml:
[tools]
"github:abogoyavlensky/wtr" = "latest"Then run mise install.
With Homebrew
brew install abogoyavlensky/tap/wtrWorks on macOS and Linux, Intel and ARM. The command taps the repository automatically. Upgrade with:
brew upgrade wtrDownload the archive for your platform from the
releases page, extract it,
and put wtr on your PATH:
VERSION=0.1.0
OS=$(uname -s | tr '[:upper:]' '[:lower:]') # linux | darwin
ARCH=$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/')
curl -sSL -o wtr.tar.gz \
"https://github.com/abogoyavlensky/wtr/releases/download/v${VERSION}/wtr_${VERSION}_${OS}_${ARCH}.tar.gz"
tar -xzf wtr.tar.gz
mv wtr ~/.local/bin/Releases are created by pushing a v* tag; each one ships binaries for
linux/amd64, linux/arm64, darwin/amd64 and darwin/arm64 with a
checksums.txt.
A usual workflow: one task, one worktree, one branch.
# Start a task in its own worktree and jump straight into a shell there
$ wtr create --sh feature-x
Created worktree at /Users/andrew/Projects/worktrees/wtr/feature-x
Branch: feature-x
# ...work in the worktree: run an agent, edit, commit. `exit` returns you.
# Meanwhile, from the main worktree: run one-off commands without cd
$ wtr run feature-x npm test
# See every worktree of the project at a glance
$ wtr list
# Check out the branch from the main dir, where deps and env already live
$ wtr switch feature-x
# ...build, test, poke around, then go back
$ wtr switch master
# Once the work is merged, clean up the worktree and its branch
$ wtr remove feature-xEach command is described in detail below.
Lists every worktree of the current repository with a short commit and the
branch label. The current worktree is marked with *.
$ wtr list
PATH COMMIT BRANCH
* /Users/andrew/Projects/wtr 3270c3d master
/Users/andrew/Projects/worktrees/wtr/feat-x 8a1b2c3 feat-x
Creates a new worktree at <base-dir>/<project>/<name> on a new branch named
<name>. The base directory is read from ~/.config/wtr/config.toml. On the
first run, the config is created with a sensible default.
# First run also writes ~/.config/wtr/config.toml
$ wtr create feature-x
Created config at ~/.config/wtr/config.toml with base_dir = /Users/andrew/Projects/worktrees
Created worktree at /Users/andrew/Projects/worktrees/wtr/feature-x
Branch: feature-x
# Create from a specific ref
$ wtr create hotfix --from main
Created worktree at /Users/andrew/Projects/worktrees/wtr/hotfix
Branch: hotfix (from main)
# Override base directory for one invocation
$ wtr --base-dir /tmp/scratch create throwaway
Created worktree at /tmp/scratch/wtr/throwaway
Branch: throwaway
# Create and jump straight into a shell in the new worktree
$ wtr create --sh feature-y
Created worktree at /Users/andrew/Projects/worktrees/wtr/feature-y
Branch: feature-y
The branch name is taken from <name> as-is — no prefix is added. To use a
namespaced branch, pass it explicitly: wtr create feature/bar.
--sh behaves like wtr run feature-y immediately after the create: it opens
an interactive shell ($SHELL, falling back to /bin/sh) in the new worktree,
runs the same best-effort mise trust preflight, and exit returns you with the
shell's exit code. As with run, an interactive shell needs the built binary —
under lgx run the dev runner buffers stdio.
Runs a command in the named worktree, with stdio streamed and the command's
exit code propagated. With no command, it opens an interactive shell ($SHELL,
falling back to /bin/sh) in the worktree — the binary-native way to "be in"
a worktree without changing your parent shell. master or main target the
main worktree. If the command name is not an executable on PATH, wtr runs
it through your interactive shell, so zsh functions from ~/.zshrc work.
# Run a one-off command in a worktree
$ wtr run feature-x npm test
# Flags after <name> flow to the command — no `--` needed
$ wtr run feature-x git status -s
# A literal `--` inside the command is passed through untouched
$ wtr run feature-x git checkout -- file.txt
# Run a zsh function defined in ~/.zshrc
$ wtr run feature-x lmcx
# Open a shell in the worktree; `exit` returns you
$ wtr run feature-x
# Operate on the main worktree
$ wtr run master git pullThe name is resolved against git worktree list, so only existing worktrees
match (no config needed). Namespaced names work too: wtr run feature/bar.
A worktree literally named master or main is shadowed by the main-worktree
alias.
For shell-backed modes (wtr run feature-x and shell-function fallback), wtr
tries mise trust --yes --all --cd <worktree> first when mise is available.
This prevents a mise trust prompt from blocking shell startup in the child
process.
Note: an interactive shell needs the built binary (./bin/wtr run …). Under
lgx run the dev runner buffers stdio, so a nested shell won't be interactive.
Points your main worktree at another worktree's branch, in detached HEAD, so you can read, build, and run that branch's code from the main project dir — where your environment, dependencies, and tooling already live — without disturbing the feature worktree. Git refuses a normal checkout of a branch that's already checked out elsewhere; detaching sidesteps that.
# Look at the feature-x branch from your main dir
$ wtr switch feature-x
Switched main worktree to 'feature-x' (detached at 8a1b2c3). Run 'wtr switch master' to return.
# ...build, test, poke around, then go back
$ wtr switch master
Switched main worktree to mastermaster or main re-attach the main worktree to that branch as usual; the
return hint names whichever branch the main worktree was on. As with run, the
name resolves against git worktree list (so only existing worktrees match,
namespaced names like feature/bar work, and a worktree literally named
master/main is shadowed by the alias).
Note: this reflects the branch's committed state. Uncommitted changes still living in the feature worktree won't appear — git can't share a working tree across worktrees.
Read-only: prints the config file path and its content.
$ wtr config
Config: /Users/andrew/.config/wtr/config.toml
base_dir = "/Users/andrew/Projects/worktrees"When the config file doesn't exist yet (it's only written on the first
wtr create), it prints the path along with the default base_dir that the
first create would use:
$ wtr config
Config: /Users/andrew/.config/wtr/config.toml (not created yet)
Default base_dir: /Users/andrew/Projects/worktrees
Run 'wtr create <name>' to initialize it.The content is shown verbatim (no parsing), and the command always reports the
on-disk file — it ignores --base-dir.
Removes the named worktree and then deletes its branch.
$ wtr remove feature-x
Removed worktree /Users/andrew/Projects/worktrees/wtr/feature-x and branch feature-x.
$ wtr remove throwaway --force
Removed worktree /Users/andrew/Projects/worktrees/wtr/throwaway and branch throwaway.By default, remove lets git protect your work. Git refuses to remove a dirty
worktree, and wtr stops before touching the branch. If the worktree is clean
but the branch has commits that are not merged, wtr removes the worktree,
keeps the branch, and prints a note with git's reason.
Use --force only for throwaway work. It passes --force to
git worktree remove and deletes the branch with git branch -D.
wtr remove master and wtr remove main are always refused because they target
the main worktree. Names resolve against git worktree list, like run and
switch, so namespaced worktrees such as feature/bar work.
Prints the completion script for bash, zsh or fish. See
Shell completions for how to install it.
Config file: ~/.config/wtr/config.toml
base_dir = "/Users/andrew/Projects/worktrees"base_dirmust be an absolute path.- On the first run,
wtrwrites a config that points at aworktreesdirectory sibling to the main worktree.
wtr completion <shell> prints a completion script for bash, zsh or fish.
Completions are dynamic: besides subcommands and flags, they suggest the
worktree names of the current repository for run, switch and remove.
Bash — add to ~/.bashrc:
source <(wtr completion bash)Zsh — either source it the same way in ~/.zshrc (after compinit), or
drop it on your fpath:
mkdir -p ~/.zfunc
wtr completion zsh > ~/.zfunc/_wtrand make sure ~/.zshrc contains fpath+=~/.zfunc before compinit runs.
Fish:
wtr completion fish > ~/.config/fish/completions/wtr.fishwtr is a let-go script bundled to a
native binary via lgx.
# Run from source (development)
lgx run -- list
# Bundle to a binary
lgx build
./bin/wtr listlgx test