Skip to content

Repository files navigation

wt — git worktrees, ergonomically

coverage

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:

Screenshots

wt-screenshot wt-cli-screenshot

Why

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:

  1. "I can't delete this worktree — it has changes." wt remove shows 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 with git stash pop) or discard them permanently. No more mystery --force.

  2. "If I delete the worktree, do I lose the branch?" No — and wt says 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.

  3. "Where even are my worktrees?" A worktree is (almost) a full copy of your checkout, so wt shows them in relation to the main one and enforces that they all live in <repo>.worktrees/. Worktrees created behind its back with raw git worktree add are detected the next time you run wt and offered a one-keystroke move into place.

Install

macOS — desktop app + CLI

brew install didley/tap/wt

The 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.

Linux — desktop app

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.)

CLI only — macOS and Linux

If you just want the command-line tool (works with Linuxbrew):

brew install didley/tap/wt-cli

Or with Go:

go install github.com/didley/wt/cmd/wt@latest

The wt cask and the wt-cli formula both install the same wt binary, so use one or the other.

Shell integration (recommended)

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.fish

CLI usage

Run 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.

wt add [branch...]

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.

wt list (alias: ls)

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.

wt switch [worktree] (alias: cd)

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)").

wt remove [worktree] (aliases: rm, delete)

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.

wt lock [worktree] / wt unlock [worktree]

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.

wt rename <worktree> <new-name>

Rename the worktree directory. The branch keeps its name unless you pass --branch.

wt organize

Health-check the convention:

  • worktrees living outside <repo>.worktrees/ are listed and moved into place (each move confirmed; --fix applies 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.

wt setup <bash|zsh|fish>

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 GUI

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.

FAQ

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.

Development

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 user

Releases: 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-cli formula in didley/homebrew-tap (needs a TAP_GITHUB_TOKEN repository secret with write access)
  • a macOS job builds the universal wt.app + CLI, attaches wt_<version>_darwin_universal.zip to the release and updates the wt cask in the tap
  • a Linux job builds the Flatpak and attaches wt.flatpak to the release

License

MIT

About

Ergonomic git worktrees CLI & GUI

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages