wt removes the friction from git worktrees. It ships as a CLI and a
desktop app (GUI) built on the same core, so both give the same
guarantees. Every worktree of a repository lives in one predictable
place — a sibling directory named <repo>.worktrees/ — and creating,
listing, switching, renaming and removing worktrees is painless:
Worktrees are one of git's best features and one of its least used, because
three things routinely trip people up. wt is designed around them:
-
"I can't delete this worktree — it has changes."
wt removeshows you exactly which files have uncommitted changes and gives you two explicit ways out: stash them (saved in the repo's stash, recoverable any time withgit stash pop) or discard them permanently. No more mystery--force. -
"If I delete the worktree, do I lose the branch?" No — and
wtsays so at every step. Removing a worktree never touches the branch; it remains in the repository and can be checked out from anywhere. Deleting the branch is a separate, clearly-labeled opt-in step. -
"Where even are my worktrees?" A worktree is (almost) a full copy of your checkout, so
wtshows them in relation to the main one and enforces that they all live in<repo>.worktrees/. Worktrees created behind its back with rawgit worktree addare detected the next time you runwtand offered a one-keystroke move into place.
brew install didley/tap/wtThe cask installs wt.app and the wt command-line tool together. The
app is signed with a Developer ID and notarized by Apple, so it launches
without a Gatekeeper warning.
The GUI is distributed as a Flatpak. Until the Flathub listing is live,
grab wt.flatpak from the latest release:
flatpak install ./wt.flatpak # runtime deps come from Flathub(Once the Flathub submission lands this becomes
flatpak install flathub dev.didley.wt.)
If you just want the command-line tool (works with Linuxbrew):
brew install didley/tap/wt-cliOr with Go:
go install github.com/didley/wt/cmd/wt@latestThe wt cask and the wt-cli formula both install the same wt binary,
so use one or the other.
Lets wt switch / wt cd change your shell's directory (a child process
can't do that on its own), and adds tab completion for wt's commands and
flags. Add one line to your shell rc:
eval "$(wt setup bash)" # ~/.bashrc
eval "$(wt setup zsh)" # ~/.zshrc
wt setup fish | source # ~/.config/fish/config.fishRun wt with no arguments to list worktrees, then (in a terminal) pick
what to do next from an interactive menu. All commands are interactive
when run in a terminal and scriptable with flags. Pass -y/--yes
(available on every command) to skip confirmation prompts and fail instead
of prompting for missing input — the flag to use in scripts and CI.
Create one or more worktrees under <repo>.worktrees/.
wt add— interactive: a new branch (name + base ref), or one or more existing branches that have no worktree yet.wt add fix-login— non-interactive. If the branch exists it's checked out into the worktree; otherwise it's created from the repo's default branch (override with--from <ref>).wt add fix-login fix-signup— creates a worktree for each; a failure on one (e.g. an already-checked-out branch) doesn't stop the rest.
Branch names containing / get flattened directory names:
feature/search lives at my-app.worktrees/feature-search.
Show all worktrees with their branch, lock, and dirty state as a NAME BRANCH LOCK STATE table (a locked worktree shows 🔒 in the LOCK column;
its reason is only shown with --verbose, since reasons can be long).
Worktrees living outside <repo>.worktrees/ are flagged with a trailing
* and a wt organize hint. --verbose/-v adds full paths, directory
names and commit hashes. --porcelain prints stable, versioned
tab-separated output for scripts (bare --porcelain is shorthand for
--porcelain=v1; only v1 exists so far):
path<TAB>name<TAB>branch<TAB>main|linked|stray<TAB>state<TAB>locked|unlocked[:reason]<TAB>head.
Jump to a worktree — interactive picker, or by name/branch. With shell
integration installed it cd's your shell; without it, it prints the path
(compose it yourself: cd "$(wt switch fix-login)").
Remove a worktree. The branch is always kept — removal only deletes the checkout directory. If the worktree is dirty you'll see the changed files and choose to stash or discard them.
Flags for scripting:
| Flag | Effect |
|---|---|
--stash |
stash uncommitted changes before removing |
--discard |
permanently discard uncommitted changes |
--yes / -y |
skip confirmation prompts (global flag) |
--delete-branch |
also delete the branch (refused if unmerged) |
--force-delete-branch |
also delete the branch, even if unmerged |
A stash created by wt lives in the repository, not the worktree, so it
survives the removal — recover it from anywhere with git stash pop.
Locked worktrees are refused unless you confirm the override (or pass
--yes) — see wt lock below.
Lock a worktree to protect it from wt remove and wt prune (and their
git equivalents) — handy for one on removable media, or one you want to
leave untouched mid-review. Locking never affects the branch or its
commits. --reason "<text>" records why; it shows up in wt list and
git worktree list.
Rename the worktree directory. The branch keeps its name unless you pass
--branch.
Health-check the convention:
- worktrees living outside
<repo>.worktrees/are listed and moved into place (each move confirmed;--fixapplies everything unattended) - stale entries whose directories were deleted manually are pruned
The same check also runs automatically before every wt command, so
worktrees created with raw git worktree add are caught the next time you
use wt — no background watcher needed.
Print the shell wrapper function and tab completions (see
Shell integration). Run it directly in a
terminal (not piped into eval/source) to pick which piece(s) you want
via an interactive prompt; piped or scripted usage always emits both.
The desktop app (gui/, Wails v2) shares internal/core with the CLI —
same behaviors, same safety copy:
- worktree cards with dirty status, lock status, and expandable changed-file lists
- create / rename / remove dialogs: the branch is always kept, dirty trees get the explicit stash-or-discard choice, locked trees need an explicit override
- lock / unlock worktrees, with an optional reason
- a banner with a one-click move for worktrees living outside
.worktrees/
Install it via the macOS cask or the Linux Flatpak. To build it from source (including on Fedora Atomic with a distrobox), see gui/README.md.
Does deleting a worktree delete my branch?
No. A worktree is just a checkout directory; the branch lives in the
repository. wt remove reminds you of this every time, and only deletes a
branch if you explicitly ask (a prompt, or --delete-branch).
Where did my stashed changes go?
Into the regular git stash of the repository: git stash list from any
worktree shows them, git stash pop restores them. wt labels them
wt: removed worktree "<name>" so they're easy to spot.
Can I still use git worktree directly?
Yes. wt is a thin layer over git worktree — anything it creates is a
normal worktree. If you add one outside <repo>.worktrees/, wt will
notice next time it runs and offer to move it.
Bare repositories?
Not supported (yet): the .worktrees convention anchors on a main
checkout.
Tasks are run with just
(dnf/apt/brew install just):
just --list # list all recipes
just build # CLI -> ./wt
just runCli -h # run the CLI via `go run`, forwarding any args
just gui # desktop app -> gui/wt-gui (needs GTK3/WebKitGTK
# headers on Linux; on Fedora Atomic run inside a
# distrobox, see gui/README.md)
just check # CLI + GUI tests (against real git repos in temp
# dirs) + vet, the same gate CI applies
just flatpak # build + install the Flatpak for the current userReleases: push a v* tag. CI runs the test suite (Ubuntu + macOS) and GUI
builds; the release workflow re-runs tests, then
- goreleaser builds linux/darwin × amd64/arm64 CLI binaries, publishes the
GitHub Release and updates the
wt-cliformula indidley/homebrew-tap(needs aTAP_GITHUB_TOKENrepository secret with write access) - a macOS job builds the universal
wt.app+ CLI, attacheswt_<version>_darwin_universal.zipto the release and updates thewtcask in the tap - a Linux job builds the Flatpak and attaches
wt.flatpakto the release
MIT