A CLI wrapper around git worktree.
Inspired by wtp.
- Installation
- Configuration
- Hooks: how they work
- Hook reference
- Symlink direction (the easy-to-get-wrong one)
- Other defaults
- Usage
- Shell Completion
- Commands
pipx install .
# or
pip install .
# or
make installCreate .wt.toml in your project root, or run wt init to generate a default:
wt init # create default .wt.tomlwt init always writes to the main worktree root — even when invoked from inside a linked worktree.
base_dir = ".worktrees"
# copy a file or dir FROM the main worktree INTO the new worktree
# from : path in the MAIN worktree (required)
# to : path in the NEW worktree (optional; defaults to `from`)
[[hooks.post_create]]
type = "copy"
from = ".env"
to = ".env"
# create a symlink INSIDE the new worktree that POINTS TO a path in main
# from : the TARGET the link points to (lives in the MAIN worktree, required)
# to : WHERE the symlink itself is created (lives in the NEW worktree,
# optional; defaults to `from`)
[[hooks.post_create]]
type = "symlink"
from = ".bin"
to = ".bin"
# run a shell command INSIDE the new worktree (non-interactive, no TTY)
# command : shell string (required)
# work_dir : subdir of the NEW worktree to run in (optional; default ".")
# env : extra env vars added on top of your shell env (optional)
[[hooks.post_create]]
type = "command"
command = "npm install"
work_dir = "."
env = { NODE_ENV = "development" }base_dir(required) — relative or absolute path where worktrees are created.
post_create hooks run after wt add creates the new worktree, in the order listed in your config. Each [[hooks.post_create]] table is one hook. (Other lifecycles like pre_create or post_delete may be added in the future.) The new worktree already exists when post_create hooks run, so:
fromalways resolves against the main worktreetoalways resolves against the new worktreecommand/work_diralways run inside the new worktree
If a hook fails, wt add aborts; a missing or nonexistent from raises a config error; and a command that exits non-zero raises a hook error showing its stdout/stderr.
| Type | Field | Required? | Default | Worktree it refers to | Meaning |
|---|---|---|---|---|---|
copy |
from |
yes | — | main | file/dir to copy |
copy |
to |
no | from |
new | destination path |
symlink |
from |
yes | — | main | target the symlink points to |
symlink |
to |
no | from |
new | path where the symlink is created |
command |
command |
yes | — | new | shell string to run |
command |
work_dir |
no | "." |
new | subdir to run the command in |
command |
env |
no | — | new | table of extra env vars, overlaid on your shell env |
For symlink, from is what the link points to, not where the link lives. The link itself is created at to inside the new worktree.
new_worktree/.bin ──symlink──▶ main_worktree/.bin
(to) (from)
Mnemonic: to is where the link lives, from is what it points to.
Omit to to give the symlink the same name in the new worktree as it has in main.
todefaults tofromfor bothcopyandsymlink— omit it to keep the same path in the new worktree.work_dirdefaults to"."forcommand— the command runs at the new worktree's root.envis added on top of your existing shell environment, not a replacement for it.
wt init # create default .wt.toml
wt add feature/auth # existing branch
wt add -b feature/new-feature # new branch
wt add -b hotfix/urgent abc1234 # new branch from commit
wt add -b feature/test origin/main # new branch tracking remote
wt add -b feature/new-feature --exec "npm test" # run command after hooks
wt add feature/remote-only # auto-track remote branch
wt remove feature/auth # remove worktree
wt remove --force feature/auth # force remove
wt remove --with-branch feature/auth # remove + delete branch
wt remove --with-branch --force-branch feature/auth # force delete branch
wt list # list worktrees
wt list --format porcelain # script-friendly list output
wt cd # go to main worktree
wt cd @ # go to main worktree (shorthand)
wt cd feature/auth # go to linked worktree by name
wt shell-init bash # print Bash shell init script
wt -v add -b feature/feature # verbose
wt -vv add -b feature/feature # debug
Important
wt cd needs the shell init loaded — see Shell Completion below.
Without it, wt cd only prints the path; it cannot change your shell's cwd.
Default output is script-friendly: add and remove print only the absolute path of the created or removed worktree.
wt ships a Bash init script that provides both completion and a wt cd directory-changing hook. To load them in the current shell:
source <(wt shell-init bash)The init script suggests commands, aliases, flags, list --format values, Git branches/tags/start points, removable worktree names, and defines the wt() function so wt cd <name> changes your shell's working directory.
Important
wt cd requires shell setup. A child process cannot change its parent shell's directory, so wt cd only changes directories after the init script above is loaded (which wraps wt in a shell function). Without it, wt cd just prints the resolved path — it will not change your cwd. Add source <(wt shell-init bash) to your ~/.bashrc to make it permanent.
| Command | Description |
|---|---|
init |
Create a default .wt.toml configuration file |
add |
Create a worktree from an existing or new branch |
remove / rm |
Remove a worktree, optionally deleting its branch |
list / ls |
List worktrees |
cd |
Change to a worktree by name (requires shell init) |
shell-init |
Print shell init script for Bash (completion + cd hook) |
