single Go binary · zero deps

Your branches,
everywhere at once.

wt makes git worktrees ergonomic — fork your uncommitted state into a fresh worktree, keep long-lived branches permanently checked out, and jump between them in a keystroke.

$ curl -fsSL https://wt.glevski.com/install.sh | sh
~/app — zsh
➜ wt fork -c fix/login
wt: carried over: 1 staged, 2 unstaged file(s)
wt: created worktree 'fix-login'
➜ fix-login wt peek origin/main
wt: peek ready at ~/worktrees/app/peek-a41f09c
➜ wt finish -d -b
wt: removed 'fix-login', branch deleted — back home

Stop stashing. Start forking.

wt fork
WIP travels with you

Staged, unstaged and untracked changes carry into a fresh worktree — split preserved, original untouched.

wt base
main, always on screen

Permanent view-only worktrees for long-lived branches; one command fast-forwards them all.

wt deps
node_modules follows, atomically

Heavy paths hardlink in the background after you jump — near-zero disk, and a half-built dir never appears.

wt peek
Snapshot any revision

A disposable, branchless export of any sha, tag or remote branch — safe even mid-rebase.

wt <tab>
Completion out of the box

Subcommands, worktree names, branches and your own aliases complete in zsh and bash — no plugin, it comes with the shell function.

wt global
Every repo, from anywhere

A machine-wide registry: list, inspect and jump into any project's worktrees from any directory.

Pay for node_modules once.

Declare a heavy directory once. Every new worktree gets it hardlinked from the one you forked: the same files on disk, with no install and no copy.

$ wt deps add node_modules
509 MB
one project's node_modules
2.9 MB
what a linked fork adds
1.5 GB+
saved across four worktrees
ext4 Hardlinks need no APFS or btrfs. They share on plain ext4, the default on most Linux machines, containers and CI runners.
wt df Shared bytes are charged once, to the first worktree holding them. Plain du counts these four trees as 2.0 GB; the disk holds 523 MB.
wt deps eject A tool that edits files in place inside a dep writes through to every linked worktree. Eject gives one worktree private copies; link shares again, purge reclaims the space.

Hardlinks can't cross filesystems, so wt says so upfront and copies instead. The reference covers wt deps and wt df in full.

~/shop — zsh
➜ wt df
NAME DEPS SIZE DEPS-SIZE SHARED
shop - 512M 509M 509M
search-ui linked 4.8M - 509M
checkout-fix linked 2.9M - 509M
staging linked 2.9M - 509M
TOTAL 523M (1.5G saved by sharing)

main and staging, one jump away.

Promote a long-lived branch to a base: a permanent, view-only worktree named after it. When staging breaks while you're mid-feature, you jump in and look, with nothing stashed and no throwaway checkout.

$ wt base add staging
wt ch staging Jump in to inspect or run the code. When looking turns into real work, wt fork -c carries your tweaks into a properly named worktree.
wt base update Fetches and fast-forwards every base to its upstream. Fast-forward only, so it can never lose anything: dirty, drifted or diverged bases are skipped with a note.
wt base reset Back to pristine: the base's own branch checked out again and hard-synced to the upstream tip.
drift wt can't stop git from switching a base's branch, so it tells you: a red branch cell with a ! in wt list.

A base is a launch pad, not a build directory: it gets your ignored files (.env and friends) but not declared deps, unless you pass --deps. The reference covers wt base in full.

~/app — zsh
➜ wt base add staging
wt: added base 'staging' at ~/worktrees/app/staging
➜ wt base update
wt: fetching origin…
wt: main: updated to 4b9a96f (was 163a116)
wt: staging: already up to date (7af004a)
➜ wt ch staging
➜ staging wt fork -c hotfix/cart
wt: created worktree 'hotfix-cart'

Checkpoint long work without committing it.

Complex changes take many rounds, and committing each one is overkill. A snapshot is an intermediate commit that stays out of your history: recorded natively in git, kept off the branch, and never sent to the remote.

$ wt snap
wt snap Records the working state, tracked edits and untracked files alike, as snapshot 1, 2, 3… An unchanged tree records nothing, and a real commit starts a fresh series.
wt snap diff See what the latest round or session added to work you haven't committed. Plain git diff lumps everything since the last commit together; this shows only what changed since the last snapshot, or between any two.
your pager It is git diff underneath, so your pager, colors and tools like delta work as they always do.
wt snap rev Every snapshot is a plain git commit, readable by any git command. wt peek $(wt snap rev 2) opens iteration 2 without touching your worktree.
never pushed Off the branch, invisible to git log and to other worktrees, and carried by no ordinary push. The one exception is git push --mirror, which copies every ref: purge first if you mirror.

Snapshots belong to the worktree and are removed with it; wt rm says how many would go. Needs wt 0.0.19 or newer. The README has the full command list.

~/worktrees/app/auth-refresh — zsh
➜ wt snap first pass
wt: snapshot 1 recorded (3 files +42 −7)
➜ wt snap retry on 401
wt: snapshot 2 recorded (2 files +18 −4)
➜ wt snap ls
N AGE FILES MESSAGE CREATED
2 now 2 files +18 −4 retry on 401 2026-10-03 15:12
1 41m 3 files +42 −7 first pass 2026-10-03 14:31
➜ wt snap diff 2 # iteration 2, in your pager

Every project, from wherever you stand.

Register a project once and it joins a machine-wide registry. From any directory, even outside a repo, you can list its worktrees, check their status, read their logs and jump straight in.

$ wt link -r
wt global ls Every registered project with its worktree count and path. Add a project name for that project's full worktree table.
wt global status The status of any worktree, addressed as project/worktree, as if you ran it there. git-log works the same way, with its arguments passed through.
wt global ch Jump to a project's root, or straight into one of its worktrees. Names prefix-match and tab-complete.
the registry Plain global git config, one wt.project.<name> entry per project. Entries whose repo vanished or was renamed are pruned automatically, with a note.

Registration is always explicit: nothing joins the registry until you run wt link -r. The reference covers wt global and wt link in full.

~ — zsh
➜ shop wt link -r
wt: registered 'shop' for wt global
➜ ~ wt global ls
NAME WORKTREES PATH
blog 0 ~/code/blog
shop 3 ~/code/shop
tickets-app 5 ~/code/tickets-app
➜ ~ wt global ch tickets-app/dev
wt: → tickets-app/dev

Make it yours. It's git all the way down.

wt has no config file and no database of its own. Aliases, settings and the project registry live in git config, worktrees are ordinary git worktrees, and every command runs the real git binary. Customizing wt is customizing git.

$ wt alias add cr create -c
wt alias Git-style aliases: wt cr feature becomes wt create -c feature. They tab-complete like the command they expand to, and an alias of a jump command still moves you.
tab completion Works out of the box in zsh and bash: the same line that defines wt sets it up, with no plugin to install. Subcommands, worktree names, branches and your own aliases complete, computed live, so rm only offers what can be removed.
git config Every setting is a git config key, so any scope works: wt.root for where worktrees live, wt.deps for what to share, a per-repo alias in one line.
--json wt list --json and wt snap ls --json give scripts and editors a stable view. stdout carries only data; all narration goes to stderr.
worktree prompt A ready-made zsh prompt segment: project and branch, colored by the kind of worktree you stand in.
plain git A wt worktree is a completely normal checkout: commit, push and open PRs as usual. Stop using wt tomorrow and git worktree list still shows every one.

Builtins always win over an alias of the same name. The reference covers wt alias and every configuration key.

~/code/shop — zsh
➜ wt alias add cr create -c
wt: alias 'cr' = 'create -c' (global git config, wt.alias.cr)
➜ wt cr feature/search # wt create -c feature/search
wt: created worktree 'feature-search'
➜ feature-search git config --get wt.alias.cr
create -c
➜ feature-search git worktree list
~/code/shop 4b9a96f [main]
~/worktrees/shop/feature-search 4b9a96f [feature/search]

No bloat.

0 dependencies
One small binary

About 3 MB, built on the Go standard library alone. No runtime, no daemon, nothing else to install or keep updated.

0 network calls
No third-party APIs

wt contains no network code, no telemetry and no accounts. The only thing that reaches a network is git, talking to your own remotes.

0 API keys
Agent-ready, not AI-powered

No model calls and no prompts inside the binary. Agents drive wt through the same commands you type, and it works exactly the same without one.

git, natively
Just a git orchestrator

Every operation is a real git command you could run yourself. The one other program wt ever runs is tar, to unpack a peek.

Plugins and extensions are planned, so anything beyond the core stays opt-in instead of built in.

Up and running in three steps.

1
Get the binary

Homebrew, or the install script (checksum-verified, lands in ~/.local/bin). macOS and Linux, Intel and ARM.

HOMEBREW
$ brew install glevski/homebrew-tap/worktrees
INSTALL SCRIPT
$ curl -fsSL https://wt.glevski.com/install.sh | sh
2
Add the shell function

A child process can't cd its parent shell, so the binary — worktree — is wrapped by a tiny wt function that can. One line in your rc file defines it, tab completion included.

The install script adds that line for you, to ~/.zshrc or ~/.bashrc — open a new shell and go to step 3. Homebrew leaves your rc file alone, so add it yourself:

AFTER HOMEBREW
$ echo 'eval "$(worktree init zsh)"' >> ~/.zshrc

Also needed if you ran the install script with WT_NO_MODIFY_RC=1. On bash: worktree init bash in ~/.bashrc.

3
Link a repo

Once per repo. The name decides where its worktrees live — ~/worktrees/myproject/ — and keeps two repos both cloned as app/ apart.

$ cd ~/my-repo && wt link myproject

From here, wt fork -c drops you into a fresh worktree with your changes. The command reference has the rest.

Hand it to your agent.

wt ships agent skills, so Claude Code — or any agent that reads skills — drives worktrees the way you would: deps declared and hardlinked, paths read back, nothing deleted behind your back.

$ npx skills add glevski/wt
/wt new [branch] A new worktree the careful way — deps declared first, hardlinked, and ready before it reports the path.
/wt list · /wt df The tables, plus what stands out: dirty trees, drifted bases, deps that were copied instead of linked.
/wt-cleanup Removes the worktrees this session created, and only those. Anything with uncommitted changes is reported, not deleted.
plain words “Fix the login bug in a fresh worktree and run the tests there.” The skill loads itself whenever a task involves worktrees.

Guardrails are part of the skill: it never deletes a worktree on its own initiative, never answers a y/N prompt for you, and never touches worktrees it didn't create.

claude code
> /wt new fix/login
● worktree deps # declared before creating?
● worktree create -w -n fix-login fix/login
● worktree status fix-login # read the path back
path ~/worktrees/app/fix-login
deps linked
fix-login is ready — deps hardlinked, nothing copied.