wt
wt is a single-binary CLI + TUI for managing Git worktrees and their GitHub
pull requests: create a branch and worktree in one step, jump between them, check
out PRs into isolated directories, and clean up when work merges. Git is the
source of truth — worktrees you create or remove with plain git show up
automatically.
Getting Started
1. Install
Homebrew (recommended)
This pulls a prebuilt binary from the getkono/homebrew-tap tap (macOS arm64/x86_64 and Linux arm64/x86_64).
Cargo (crates.io)
The crate is published as kono-wt (the bare
wt name was already taken); the installed binary is still wt.
From source
You need the Rust toolchain (rustup), git ≥ 2.20 on your
PATH, and — only for PR commands — the gh CLI.
# or, from a checkout:
Make sure ~/.cargo/bin is on your PATH. Then enable shell integration
(below) — that single step also gives you the best tab completion.
2. Enable shell integration (required for navigation)
A program can't change its parent shell's working directory, so on its own wt
can only print where to go. The shell wrapper closes that gap: it captures the
path and cds you in. Source it from your shell rc once:
# ~/.zshrc or ~/.bashrc
# fish (~/.config/fish/config.fish)
|
# PowerShell ($PROFILE)
| |
Without it, switch, new, pr, and the TUI just print a path instead of
moving you. Supported shells: bash, zsh, fish, powershell, elvish. On anything
else, wt switch --print-path lets you build your own cd alias.
This is also the recommended way to get tab completion. The shell-init snippet
installs dynamic completions that suggest live values — real worktree names,
branches, and PR numbers (via wt __complete) — not just the static command and
flag list. Because you need to source it for navigation anyway, it's the single
step that sets up everything; there's no separate completions install. (A static,
values-unaware script is still available via wt completions <shell> if you want
to manage it yourself.)
3. Authenticate gh (only for PR commands)
Everything except wt pr works fully offline. If gh is missing or
unauthenticated, only the PR commands fail (with an actionable message); the rest
keep working.
4. Open it
Run wt with no arguments in any repository to launch the TUI dashboard, then
press ? for the full keymap — creating, switching, removing, checking out PRs,
sorting, and filtering are all discoverable from there. For example:
Run wt --help (or wt <command> --help) for the complete command surface.
Key features to know
These are the things worth knowing up front; the rest is discoverable from
--help and the TUI.
-
See every branch, not just worktrees. The TUI lists your worktrees first, then — dimmed beneath them — any local branch that has no worktree, each with how far it is ahead/behind its base. Select one and press
Enterto create a worktree for it and switch in (it asks first). A branch left behind after you remove its worktree stays visible here instead of vanishing. -
Pick options on pop-up fields. TUI fields with known choices offer an inline dropdown instead of blind typing. The new-worktree branch/base fields suggest existing local and remote branches to fork from or check out — type to filter,
↑/↓to pick,Enterto accept, or just type a brand-new name. The PR compose form's model and effort fields list their choices the same way. -
Where worktrees are created. New worktrees follow a configurable path template. The default keeps them beside the repo, out of it, and prefixes each worktree directory with the repo name so it's obvious which repo you're in:
{repo_parent}/{repo}.worktrees/{repo}-{branch_slug}. Change it withwt config set path_template …. Common alternatives: a subdir inside the repo,{repo_root}/.worktrees/{branch_slug}(add it to.gitignore), or a central store,{home}/worktrees/{repo}/{branch_slug}. Worktrees you made by hand anywhere are still listed and managed. -
Auto-copy ignored files into new worktrees. Git-ignored files like
.envdon't follow a new worktree. List glob patterns undercopyto bring them along onwt new, e.g.copy = [".env", ".env.local"]. -
Run a command after creating a worktree.
hooks.post_create(e.g.npm install,direnv allow) runs inside the new worktree;hooks.pre_removeruns before removal. Hooks receiveWT_WORKTREE_PATH,WT_BRANCH,WT_REPO_ROOT, and friends in their environment. -
Configuration lives in two places. A per-repo
.wt.tomlat the repo root and a global user config, managed withwt config get|set|list|edit(--globalfor the user config); precedence is flags > repo > global.wt initis an optional convenience that scaffolds a starter.wt.tomland, for a subdir store, offers to add it to.gitignore. -
Theme the TUI. Pick a built-in palette and tweak individual colors under
[ui.theme]:presetselects the base (one-dark(default) orsolarized), and the named slots (accent,green,red,yellow,orange,cyan,magenta,gray,selection_bg,chip_fg) override it. Colors are#rrggbbhex, a named color (e.g.cyan,light-blue), or a 0–255 ANSI index. Like every setting, themes merge across layers (a global base palette, per-repo accents), e.g.[] = "solarized" = "#ff8800" -
Removal protects your work.
wt removeandwt prunerefuse to drop a worktree with uncommitted or unpushed changes unless you pass--force. -
Drop the worktree you're in.
wt dropremoves the worktree containing the current directory (from any depth), keeps the branch, andcds you back to the main worktree. It refuses the primary worktree and honors the same--forceguard. -
Bulk-clean stale branches.
wt prune --mergedremoves worktrees whose branch is merged into the default branch, andwt prune --goneremoves worktrees whose upstream was deleted (plus any missing worktrees). Both also delete matching local branches that no longer have a worktree — so a repo left with a pile of merged feature branches gets cleaned up too. Preview with--dry-run. A--gonebranch that isn't also merged may hold unmerged commits, so it is skipped unless you pass--force. The current and default branches are never touched.
Development
Prerequisites
- Rust (rustup) — toolchain (pinned via
rust-toolchain.toml) - mise — tool manager + task runner
- hk — git hooks manager
Run mise install once to fetch the pinned dev tools (hk, pkl,
cargo-llvm-cov, cargo-mutants).
| Command | Description |
|---|---|
mise tasks |
List available tasks |
cargo run |
Run the application |
mise run install |
Build and install wt to ~/.cargo/bin |
mise run test |
Run tests |
mise run format |
Format code |
mise run lint |
Lint with Clippy (warnings as errors) |
mise run lint-fix |
Lint and auto-fix |
mise run coverage |
Run tests with coverage (min 80%) |
After cloning, run mise install to fetch the dev tools, then hk install
once to activate the git hooks.
Tech Stack
- Runtime: Rust (edition 2024)
- Formatter: rustfmt
- Linter: Clippy
- Task runner: mise
- Git hooks: hk
- Key Dependencies: tokio, eyre + color-eyre, tracing + tracing-subscriber, thiserror
Architecture
The logic lives in the library crate (src/lib.rs) so it is unit-testable and
measured by coverage. The binary (src/main.rs) is a thin entry point that
wires up error reporting and tracing, then delegates to the library; it is
excluded from coverage.
Git Hooks
This project uses hk, configured in hk.pkl.
Pre-commit hooks auto-fix formatting and linting on staged Rust files.
Pre-push hooks run format checks, Clippy, tests, and the coverage gate.
CI/CD
GitHub Actions runs format checks, linting, tests, and coverage on pushes to
main and pull requests.
Code Coverage
This project uses cargo-llvm-cov
for LLVM-based code coverage. CI enforces a minimum of 80% line coverage and
uploads the report as a CI artifact.
License
MIT — see LICENSE for details.