# `wtr` - Git [w]ork[t]ree [r]outer `wtr` is a small tool to manage multiple git worktrees. Working from the main repo, it frees you from remembering absolute paths, runs a command in any worktree, and checks a worktree's state in detached HEAD. ## Installation ### With [Homebrew](https://brew.sh) Works on macOS and Linux: ```sh brew install abogoyavlensky/tap/wtr ``` ### With [mise](https://mise.jdx.dev) ```sh mise use -g github:abogoyavlensky/wtr@latest ``` Or pin a version in `.mise.toml`: ```toml [tools] "github:abogoyavlensky/wtr" = "latest" ``` ### Manual Download the archive for your platform from the [releases page](https://github.com/abogoyavlensky/wtr/releases), extract it, and put `wtr` on your `PATH`: ```sh 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`. ## Quickstart A usual workflow: one task, one worktree, one branch. ```bash # 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 # ...or run `wtr` with no command to pick one interactively and jump in $ wtr # 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-x ``` Each command is described in detail below. ## Commands | Command | Description | | --- | --- | | [`wtr`](#wtr-no-command) | Open an interactive dashboard of the repo's worktrees. | | [`wtr list`](#wtr-list) | List all worktrees of the current repo, marking the current one. | | [`wtr create `](#wtr-create-name) | Create a worktree on a new branch; `--sh` jumps into a shell there. | | [`wtr run [command...]`](#wtr-run-name-command) | Run a command (or open a shell) in a worktree from the main repo. | | [`wtr switch `](#wtr-switch-name) | Point the main worktree at another branch in detached HEAD. | | [`wtr config`](#wtr-config) | Print the config file path and its contents. | | [`wtr remove `](#wtr-remove-name) | Remove a worktree and delete its branch. | | [`wtr completion `](#wtr-completion-shell) | Print the shell completion script (bash/zsh/fish). | ### `wtr` (no command) Running `wtr` with no command opens an interactive dashboard of the repo's worktrees. Move with `↑`/`↓`. `enter` opens a shell in the highlighted worktree (like `wtr run `) and leaves the dashboard. `c` prompts for a name and creates a new worktree (like `wtr create `), then returns with the cursor on it — press `enter` to jump in. `s` switches the main worktree to the highlighted one, detached (like `wtr switch `); `d` removes it and its branch after a confirmation (like `wtr remove `, a safe delete — unmerged branches are kept). `c`, `s`, and `d` act in place and keep the dashboard open, reporting the result on a status line — and `d` drops the removed worktree from the list, so you can manage several in a row. `q` or `esc` quits. ``` $ wtr Worktrees › master new-feat ↑/↓ navigate enter select c create s switch d remove q quit ``` With no terminal to draw on — output piped or redirected — `wtr` prints the static worktree list instead, the same as `wtr list`. `wtr --help` and `wtr help` still print the command help. Like `wtr run`, the interactive shell needs the built binary (`./bin/wtr`); under `lgx run` the dev runner buffers stdio. ### `wtr list` 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 ``` ### `wtr create ` Creates a new worktree at `//` on a new branch named ``. 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 `` 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. ### `wtr run [command...]` 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. ```bash # Run a one-off command in a worktree $ wtr run feature-x npm test # Flags after 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 pull ``` The 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 ` 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. ### `wtr switch ` 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. ```bash # 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 master ``` `master` 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. ### `wtr config` Read-only: prints the config file path and its content. ```bash $ 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: ```bash $ wtr config Config: /Users/andrew/.config/wtr/config.toml (not created yet) Default base_dir: /Users/andrew/Projects/worktrees Run 'wtr create ' to initialize it. ``` The content is shown verbatim (no parsing), and the command always reports the on-disk file — it ignores `--base-dir`. ### `wtr remove ` Removes the named worktree and then deletes its branch. ```bash $ 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, and shell completion omits them for `remove` for the same reason. Names resolve against `git worktree list`, like `run` and `switch`, so namespaced worktrees such as `feature/bar` work. ### `wtr completion ` Prints the completion script for `bash`, `zsh` or `fish`. See [Shell completions](#shell-completions) for how to install it. ## Configuration Config file: `~/.config/wtr/config.toml` ```toml base_dir = "/Users/andrew/Projects/worktrees" ``` - `base_dir` must be an absolute path. - On the first run, `wtr` writes a config that points at a `worktrees` directory sibling to the main worktree. ### Shell completions `wtr completion ` 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`: ```sh source <(wtr completion bash) ``` Zsh — either source it the same way in `~/.zshrc` (after `compinit`), or drop it on your `fpath`: ```sh mkdir -p ~/.zfunc wtr completion zsh > ~/.zfunc/_wtr ``` and make sure `~/.zshrc` contains `fpath+=~/.zfunc` before `compinit` runs. Fish: ```sh wtr completion fish > ~/.config/fish/completions/wtr.fish ``` ## Build `wtr` is a [let-go](https://github.com/nooga/let-go) script bundled to a native binary via [lgx](https://github.com/abogoyavlensky/lgx). ```bash # Run from source (development) lgx run -- list # Bundle to a binary lgx build ./bin/wtr list ``` ## Tests ```bash lgx test ```