Skip to content
RodrigoEspinosaPublic

About

Fast git worktree manager for the terminal: fuzzy-switch with fzf, check out PRs, auto-copy .env/node_modules, and clean up merged branches.

Topics

Resources

Contributing

Stars

1 star

Watchers

0 watching

Forks

Latest commit

 

History

54 Commits

Folders and files

Repository files navigation

wt

CI Lint Release License: MIT

Git worktree manager powered by fzf.

Quickly list, switch to, create, or remove git worktrees with fuzzy search.

wt demo

Install

Homebrew

brew install RodrigoEspinosa/tap/wt

From source

git clone https://github.com/RodrigoEspinosa/wt.git
cd wt
make install

This installs wt to /usr/local/bin by default. Override with PREFIX:

make install PREFIX=$HOME/.local

Uninstall

brew uninstall wt            # Homebrew
make uninstall               # from source

Requirements

  • git 2.5 or newer (for worktree support)
  • fzf
  • jq (only for the herdr integration)

Quick start

  1. Install wt (see above).
  2. Add shell integration to your rc file:
# zsh
eval "$(wt init zsh)"

# bash
eval "$(wt init bash)"

# fish
wt init fish | source
  1. Run wt inside any git repository.

Usage

wt                    # Interactive fuzzy picker
wt my-feature         # Switch to branch worktree (creates if needed)
wt my-feature main    # Create the branch from main if it doesn't exist
wt pr 123             # Check out GitHub PR #123 in a worktree
wt clean              # Review worktrees (size, age, safety) and pick ones to remove
wt clean --list       # Just print that report
wt clean --prune --yes # Prune remotes, then remove every safe worktree non-interactively
wt -d my-feature      # Remove a worktree
wt -l                 # List worktrees (marks current `*`, dirty `!`, ahead/behind)

Inside the interactive picker: enter switches to the highlighted worktree, ctrl-n creates a worktree from whatever you've typed, and ctrl-d / del removes the highlighted one.

Commands

Command Description
wt Launch fzf to fuzzy-pick a worktree
wt <branch> [<start-point>] Switch to the worktree for <branch>, creating it if it doesn't exist. A new branch starts at <start-point> (commit, branch, or tag) if given
wt pr <number> Fetch a GitHub pull request and check it out in a worktree
wt clean [--list] [--prune] [--yes] Review every worktree's disk usage, last activity, and removal safety, and multi-select which to remove. --list prints the report only; --prune fetches/prunes remotes first; --yes removes every safe worktree without prompting
wt -d <branch> Remove the worktree for <branch>, deleting the local branch too if it is fully merged
wt -l List all worktrees, marking the current one (*), uncommitted changes (!), and ahead/behind upstream
wt init <shell> Print shell integration for bash, zsh, or fish
wt -h Show help
wt -v Show version

Worktree hooks (.wtconfig)

A fresh worktree is a clean checkout — it's missing the untracked files that make a repo usable (.env, node_modules, build caches). Drop a .wtconfig at the repo root and wt will prepare each newly created worktree for you:

# .wtconfig
copy = .env                # copy a file/dir from the primary worktree
copy = .env.local
link = node_modules        # symlink instead of copy (good for big dirs)
postCreate = pnpm install  # shell command run inside the new worktree
  • copy / link / postCreate may each appear multiple times; they run in that order.
  • baseDir = <path> sets where this repo's worktrees live (relative paths resolve against the repo root, ~ expands to your home). WT_BASE_DIR overrides it.
  • copy and link sources are resolved against the primary worktree; missing sources are skipped with a notice.
  • postCreate runs with the new worktree as the working directory and can read $WT_BRANCH, $WT_PATH, and $WT_SOURCE.
  • All hook output goes to stderr, so the shell wrapper still cds you in cleanly. Set WT_NO_HOOKS=1 to skip hooks for one run.

Because hooks leave untracked files behind, wt -d and wt clean force past untracked files when removing a worktree but still refuse to delete one with uncommitted changes to tracked files.

wt pr

wt pr <number> fetches refs/pull/<number>/head from origin (or your first remote) into a local branch and checks it out in a worktree — perfect for reviewing a PR without disturbing your current work. If gh is installed, the worktree is named after the PR's head branch; otherwise it falls back to pr-<number>.

wt clean

Agents can leave a lot of worktrees behind. wt clean lists every worktree except the primary checkout, with its disk usage, time since the last git activity in it, and a verdict on whether it can be removed. Safe ones come first, largest first:

4 worktree(s) · 1.3G on disk · 2 safe to remove (1.2G)
safe        1.2G   3d  agent/fix-login               merged into main
safe           -    -  agent/old-spike               directory already deleted
idle         48M   5h  agent/refactor-api            not merged (branch kept)
dirty        52M  12m  agent/new-parser              3 uncommitted change(s)
Verdict Meaning Removable
safe Merged into the default branch or upstream gone, with nothing uncommitted; or the directory is already deleted yes
idle Not merged, nothing uncommitted. The worktree goes; the branch and its commits stay yes
untracked Has untracked (not gitignored) files that removal would delete yes
dirty Uncommitted changes to tracked files no
orphan Detached HEAD with commits no branch or tag holds no
locked Locked with git worktree lock no
busy A herdr agent is working or waiting on input in it no
current The worktree you're running wt from no

In the picker, Tab marks a worktree and Ctrl-A marks everything shown, so type safe then Ctrl-A to select every safe one. Ctrl-/ switches the preview between the overview and diff views. Enter removes the marked worktrees and skips any that can't be removed. Gitignored files (node_modules, build output) never block removal, and a branch is deleted only when it's fully merged.

  • --list prints the report without the picker (this is also what you get without fzf).
  • --prune runs git fetch --all --prune first, so branches whose upstream was deleted on the remote are detected as gone.
  • --yes (alias --all) skips the picker and removes every safe worktree. Handy for scripts and CI.

Shell integration

wt prints the selected worktree path to stdout. A subprocess can't change the parent shell's working directory, so wt init <shell> outputs a thin wrapper function that captures the path and cds into it. It also provides tab completions for branches, worktrees, and options.

herdr

Inside a herdr pane, set WT_HERDR=workspace to have wt open each worktree as its own herdr workspace instead of cd-ing the current shell:

export WT_HERDR=workspace
  • wt <branch>, wt pr <n>, and the picker open (or focus, if already open) a workspace for the worktree, grouped under the repo in herdr's sidebar.
  • wt -d and wt clean close that worktree's workspace after removing it. They leave the workspace you're running wt from open, and refuse to remove a worktree while an agent in its workspace is still working or waiting on input.
  • The picker and wt -l show the state of each worktree's agent (● working, ◆ blocked, ✓ done); idle agents are left unmarked.
  • Requires jq to look up workspaces.
  • If herdr can't open the workspace, wt falls back to cd. Outside herdr (HERDR_ENV unset), the setting has no effect.

Keyboard shortcuts (fzf picker)

Key Action
Enter Select worktree (prints path)
Ctrl-N Create a worktree from the typed query
Ctrl-D / Del Delete the highlighted worktree
Tab Cycle the preview pane's view

The preview pane on the right has two views, switched with Tab:

  • overview: the working-tree status and the last 10 commits, plus the herdr agent's state when the herdr integration is on.
  • diff: the commits on this branch that aren't on the default branch (origin/<default> when it exists), their combined diffstat, and any uncommitted changes.

Environment variables

Variable Description Default
WT_BASE_DIR Parent directory for new worktrees <repo>/_worktrees
WT_NO_HOOKS When set, skip .wtconfig copy/link/postCreate hooks (unset)
WT_HERDR Set to workspace to open worktrees as herdr workspaces (unset)

When WT_BASE_DIR is not set, wt uses the baseDir key from .wtconfig if present, otherwise a _worktrees/ directory at the root of the repository. Branch names with slashes (e.g. rec/my-branch) are flattened to a single directory (_worktrees/rec-my-branch); the branch itself keeps its original name. If two branches would flatten to the same directory (e.g. rec/foo and rec-foo), wt refuses to create the second rather than dropping you in the wrong worktree.

Man page

A man page is included at doc/wt.1. make install installs it alongside the binary. To install just the man page:

# With make
make install-man

# Or manually
cp doc/wt.1 /usr/local/share/man/man1/

After installing, view it with man wt.

License

MIT

About

Fast git worktree manager for the terminal: fuzzy-switch with fzf, check out PRs, auto-copy .env/node_modules, and clean up merged branches.

Topics

Resources

Contributing

Stars

1 star

Watchers

0 watching

Forks

Releases

Contributors

Languages