A fast, simple Git worktree helper written in Go
57
stars
287
commits
Go
primary language
Aug 29, 2026
updated
A fast, simple Git worktree helper written in Go. Inspired by haacked/dotfiles/tree-me.

global, sibling-repo, parent-branches, and morewt clone acquires a repo under repo_root (<host>/<owner>/<repo>/<branch>), ready to inspectwt pr command (uses gh CLI) — checks out the PR's actual branch namewt mr command (uses glab CLI) — checks out the MR's actual branch name.env and friends in [files] or a committed .worktreeinclude; copied with a reflink on APFS/Btrfs/XFS, so even node_modules costs metadata rather than diskwt cleanup --stale)NO_COLOR=1 and auto-strips colors when pipedwt status --ci shows pipeline status (✓/✗/●) per branch via gh or glab CLI.wt.toml config — override global settings (strategy, hooks, etc.) on a per-repository basisgit config support — keep personal settings in .git/config or ~/.gitconfig, with no extra file to gitignorebrew install timvw/tap/wt # or: go install github.com/timvw/wt@latest
wt init # configure shell integration
See docs/installation.md for all platforms (Scoop, WinGet, Linux packages, from source).
# Acquire the main repository, ready to inspect. Placed under repo_root as
# <repo_root>/<host>/<owner>/<repo>/<default-branch>, left on its default branch
# (that trailing segment makes the clone a normal worktree slot, so wt create
# later puts siblings next to it).
wt clone timvw/wt # owner/repo resolved via gh/glab
wt clone git@github.com:me/dotfiles.git # full URL used as-is
wt clone acme/api ~/src/api # explicit destination
Two settings control placement: repo_root (default ~/dev/repos) and
repo_pattern. Grouping levels like "work" vs "personal" are yours to define —
put an env var in the pattern with a :- default so it works even when unset:
repo_pattern = "{.repoRoot}/{.env.WT_CATEGORY:-personal}/{.repo.Owner}/{.repo.Name}/{.branch}"
WT_CATEGORY=work wt clone acme/api # ~/dev/repos/work/acme/api/main
wt clone timvw/wt # ~/dev/repos/personal/timvw/wt/main (default)
To set the category for a whole tree of repos instead of per command, add a
[[context]] rule:
[[context]]
when_path = "~/dev/repos/work"
env = { WT_CATEGORY = "work" }
Every wt command operating on a repo under that path then resolves work,
including wt create from a worktree in a different tree. The same rule can go
in ~/.gitconfig instead, if you would rather not keep a config file:
git config --global wt.context.work.whenpath "~/dev/repos/work"
git config --global --add wt.context.work.env "WT_CATEGORY=work"
See Setting the category per directory.
# Checkout existing branch in new worktree
wt co feature-branch
wt co # interactive: fuzzy-search from available branches
# Create new branch in worktree (defaults to main/master as base)
wt create my-feature
wt create my-feature develop # specify base branch
# Switch to a worktree that already exists (never creates one)
wt cd feature-branch
wt cd # interactive: fuzzy-search from existing worktrees
wt sw # alias for wt cd
Unlike wt co, the wt cd list contains only branches that already have a worktree — including the main checkout — so it stays short in repositories with many branches.

# Checkout GitHub PR (requires gh CLI)
wt pr 123 # looks up branch for PR #123
wt pr https://github.com/org/repo/pull/123 # GitHub PR URL
wt pr # interactive: fuzzy-search from open PRs
# Checkout GitLab MR (requires glab CLI)
wt mr 123 # looks up branch for MR !123
wt mr https://gitlab.com/org/repo/-/merge_requests/123 # GitLab MR URL
wt mr # interactive: fuzzy-search from open MRs
A new worktree has everything git tracks and nothing else — no .env, no
.envrc, no editor state. Declare what a usable checkout needs and wt puts it
there on create, checkout, pr and mr:
# ~/.config/wt/config.toml, or a repo's .wt.toml
[files]
copy = [".env", ".claude/settings.local.json"]
link = ["node_modules"]
wt copy # re-run for the current worktree
wt copy feature-branch --dry-run # show what would happen, change nothing
wt copy feature-branch --force # overwrite files already there
wt create feature --no-copy # skip it just this once
Or commit a .worktreeinclude at the repo root — same gitignore syntax, one
pattern per line — so every contributor gets working worktrees without
configuring anything. Only untracked, git-ignored files are ever candidates; a
tracked file is already in the worktree and is never touched. See
Files.

wt ls # list all worktrees
wt rm old-branch # remove a worktree
wt rm # interactive: fuzzy-search worktree to remove
wt migrate # migrate worktrees to configured paths
wt migrate --force # force when target path exists
wt cleanup --stale # detect stale worktrees (deleted remotes, inactive commits)
wt cleanup --stale --stale-days 7 # custom inactivity threshold (default: 30 days)
wt prune # clean up stale worktree admin files
wt version # show version
wt examples # show practical examples
wt --help # show help

wt info # show active strategy, pattern, variables
wt config show # show effective config with sources
wt config init # create a default config file
wt config path # print the config file path
# Place a .wt.toml in a repo root to override global config for that repo
# Or keep it out of the working tree entirely, in git config:
git config --local wt.strategy sibling-repo # this repo only
git config --global wt.root ~/projects/worktrees
wt runs no hook you have not approved — a repo's committed .wt.toml and your own config
file alike. It asks once, and remembers the answer until the commands change:
wt trust # approve every hook that applies here
wt trust --list # show every approval on this machine
wt untrust # revoke this repository's approval
wt untrust --path /removed/repo # revoke stale approvals for a removed repository
wt untrust --global # revoke your config file's approval
Editing a hook command — or a git pull that adds one — asks again; editing anything else in the
file does not. Non-interactive runs (scripts, CI, --format json) decline, unless you opt out with
WT_HOOKS_APPROVE_ALL=1. Whitelist a whole tree with [trust] prefix = ["~/src/mine"], or set
hooks_policy = "prompt-all" to confirm every hook every time.
See Hook trust.
On case-insensitive filesystems such as the default macOS APFS setup, mixed-case branch
prefixes can produce confusing worktree paths. For example, Feature/foo and
feature/bar both need a first-level directory that macOS treats as the same name.
Set separator = "-" to flatten branch paths (Feature/foo -> Feature-foo) and
avoid that class of collision. See Configuration.

wt status # color-coded overview of all worktrees
wt status --ci # include CI/CD pipeline status (requires gh or glab)
Shows dirty/clean state, ahead/behind counts, and highlights the current worktree. With --ci, each branch shows ✓ (pass), ✗ (fail), or ● (pending) for its latest CI pipeline. Colors are automatically stripped when piping; set NO_COLOR=1 to disable.

When you run wt co, wt cd, wt rm, wt pr, or wt mr without arguments, you'll get an interactive selection menu. Typing filters the results with fuzzy matching, so you can quickly find the branch or worktree you're looking for.
wt co selects from every local and remote branch (creating a worktree when needed), while wt cd selects only from worktrees that already exist.
--format json)Most commands support machine-readable JSON output:
wt --format json version
wt --format json info
wt --format json config show
wt --format json list
wt --format json examples
In json mode, shell integration does not auto-navigate. For commands that normally prompt interactively, pass explicit arguments when using --format json.
Install the Claude Code plugin to teach Claude how to work with wt-managed worktrees:
claude plugin marketplace add timvw/wt
claude plugin install wt@wt --scope local
Once installed, Claude understands wt commands, worktree strategies, and hooks — so you can ask it to create worktrees, set up hooks for copying .env files or running npm install / uv sync, and follow worktree-based workflows automatically.
See docs/examples.md for hooks that launch Claude Code in tmux per worktree.
| Topic | Description |
|---|---|
| Configuration | Config file, git config, strategies, patterns, separator, hooks, per-repo .wt.toml |
| Examples | Claude Code + tmux, multi-repo workflows, environment variables |
| Installation | All platforms, shell integration, building from source |
| Development | Building, testing, running from source |
| Claude Code Plugin | Plugin that teaches Claude Code how to work with wt |
The tool wraps Git's native worktree commands with a convenient interface and organized directory structure:
MIT
Based on tree-me by Phil Haack.
Go
99.1%
A fast, simple Git worktree helper written in Go
57
stars
287
commits
Go
primary language
Aug 29, 2026
updated
A fast, simple Git worktree helper written in Go. Inspired by haacked/dotfiles/tree-me.

global, sibling-repo, parent-branches, and morewt clone acquires a repo under repo_root (<host>/<owner>/<repo>/<branch>), ready to inspectwt pr command (uses gh CLI) — checks out the PR's actual branch namewt mr command (uses glab CLI) — checks out the MR's actual branch name.env and friends in [files] or a committed .worktreeinclude; copied with a reflink on APFS/Btrfs/XFS, so even node_modules costs metadata rather than diskwt cleanup --stale)NO_COLOR=1 and auto-strips colors when pipedwt status --ci shows pipeline status (✓/✗/●) per branch via gh or glab CLI.wt.toml config — override global settings (strategy, hooks, etc.) on a per-repository basisgit config support — keep personal settings in .git/config or ~/.gitconfig, with no extra file to gitignorebrew install timvw/tap/wt # or: go install github.com/timvw/wt@latest
wt init # configure shell integration
See docs/installation.md for all platforms (Scoop, WinGet, Linux packages, from source).
# Acquire the main repository, ready to inspect. Placed under repo_root as
# <repo_root>/<host>/<owner>/<repo>/<default-branch>, left on its default branch
# (that trailing segment makes the clone a normal worktree slot, so wt create
# later puts siblings next to it).
wt clone timvw/wt # owner/repo resolved via gh/glab
wt clone git@github.com:me/dotfiles.git # full URL used as-is
wt clone acme/api ~/src/api # explicit destination
Two settings control placement: repo_root (default ~/dev/repos) and
repo_pattern. Grouping levels like "work" vs "personal" are yours to define —
put an env var in the pattern with a :- default so it works even when unset:
repo_pattern = "{.repoRoot}/{.env.WT_CATEGORY:-personal}/{.repo.Owner}/{.repo.Name}/{.branch}"
WT_CATEGORY=work wt clone acme/api # ~/dev/repos/work/acme/api/main
wt clone timvw/wt # ~/dev/repos/personal/timvw/wt/main (default)
To set the category for a whole tree of repos instead of per command, add a
[[context]] rule:
[[context]]
when_path = "~/dev/repos/work"
env = { WT_CATEGORY = "work" }
Every wt command operating on a repo under that path then resolves work,
including wt create from a worktree in a different tree. The same rule can go
in ~/.gitconfig instead, if you would rather not keep a config file:
git config --global wt.context.work.whenpath "~/dev/repos/work"
git config --global --add wt.context.work.env "WT_CATEGORY=work"
See Setting the category per directory.
# Checkout existing branch in new worktree
wt co feature-branch
wt co # interactive: fuzzy-search from available branches
# Create new branch in worktree (defaults to main/master as base)
wt create my-feature
wt create my-feature develop # specify base branch
# Switch to a worktree that already exists (never creates one)
wt cd feature-branch
wt cd # interactive: fuzzy-search from existing worktrees
wt sw # alias for wt cd
Unlike wt co, the wt cd list contains only branches that already have a worktree — including the main checkout — so it stays short in repositories with many branches.

# Checkout GitHub PR (requires gh CLI)
wt pr 123 # looks up branch for PR #123
wt pr https://github.com/org/repo/pull/123 # GitHub PR URL
wt pr # interactive: fuzzy-search from open PRs
# Checkout GitLab MR (requires glab CLI)
wt mr 123 # looks up branch for MR !123
wt mr https://gitlab.com/org/repo/-/merge_requests/123 # GitLab MR URL
wt mr # interactive: fuzzy-search from open MRs
A new worktree has everything git tracks and nothing else — no .env, no
.envrc, no editor state. Declare what a usable checkout needs and wt puts it
there on create, checkout, pr and mr:
# ~/.config/wt/config.toml, or a repo's .wt.toml
[files]
copy = [".env", ".claude/settings.local.json"]
link = ["node_modules"]
wt copy # re-run for the current worktree
wt copy feature-branch --dry-run # show what would happen, change nothing
wt copy feature-branch --force # overwrite files already there
wt create feature --no-copy # skip it just this once
Or commit a .worktreeinclude at the repo root — same gitignore syntax, one
pattern per line — so every contributor gets working worktrees without
configuring anything. Only untracked, git-ignored files are ever candidates; a
tracked file is already in the worktree and is never touched. See
Files.

wt ls # list all worktrees
wt rm old-branch # remove a worktree
wt rm # interactive: fuzzy-search worktree to remove
wt migrate # migrate worktrees to configured paths
wt migrate --force # force when target path exists
wt cleanup --stale # detect stale worktrees (deleted remotes, inactive commits)
wt cleanup --stale --stale-days 7 # custom inactivity threshold (default: 30 days)
wt prune # clean up stale worktree admin files
wt version # show version
wt examples # show practical examples
wt --help # show help

wt info # show active strategy, pattern, variables
wt config show # show effective config with sources
wt config init # create a default config file
wt config path # print the config file path
# Place a .wt.toml in a repo root to override global config for that repo
# Or keep it out of the working tree entirely, in git config:
git config --local wt.strategy sibling-repo # this repo only
git config --global wt.root ~/projects/worktrees
wt runs no hook you have not approved — a repo's committed .wt.toml and your own config
file alike. It asks once, and remembers the answer until the commands change:
wt trust # approve every hook that applies here
wt trust --list # show every approval on this machine
wt untrust # revoke this repository's approval
wt untrust --path /removed/repo # revoke stale approvals for a removed repository
wt untrust --global # revoke your config file's approval
Editing a hook command — or a git pull that adds one — asks again; editing anything else in the
file does not. Non-interactive runs (scripts, CI, --format json) decline, unless you opt out with
WT_HOOKS_APPROVE_ALL=1. Whitelist a whole tree with [trust] prefix = ["~/src/mine"], or set
hooks_policy = "prompt-all" to confirm every hook every time.
See Hook trust.
On case-insensitive filesystems such as the default macOS APFS setup, mixed-case branch
prefixes can produce confusing worktree paths. For example, Feature/foo and
feature/bar both need a first-level directory that macOS treats as the same name.
Set separator = "-" to flatten branch paths (Feature/foo -> Feature-foo) and
avoid that class of collision. See Configuration.

wt status # color-coded overview of all worktrees
wt status --ci # include CI/CD pipeline status (requires gh or glab)
Shows dirty/clean state, ahead/behind counts, and highlights the current worktree. With --ci, each branch shows ✓ (pass), ✗ (fail), or ● (pending) for its latest CI pipeline. Colors are automatically stripped when piping; set NO_COLOR=1 to disable.

When you run wt co, wt cd, wt rm, wt pr, or wt mr without arguments, you'll get an interactive selection menu. Typing filters the results with fuzzy matching, so you can quickly find the branch or worktree you're looking for.
wt co selects from every local and remote branch (creating a worktree when needed), while wt cd selects only from worktrees that already exist.
--format json)Most commands support machine-readable JSON output:
wt --format json version
wt --format json info
wt --format json config show
wt --format json list
wt --format json examples
In json mode, shell integration does not auto-navigate. For commands that normally prompt interactively, pass explicit arguments when using --format json.
Install the Claude Code plugin to teach Claude how to work with wt-managed worktrees:
claude plugin marketplace add timvw/wt
claude plugin install wt@wt --scope local
Once installed, Claude understands wt commands, worktree strategies, and hooks — so you can ask it to create worktrees, set up hooks for copying .env files or running npm install / uv sync, and follow worktree-based workflows automatically.
See docs/examples.md for hooks that launch Claude Code in tmux per worktree.
| Topic | Description |
|---|---|
| Configuration | Config file, git config, strategies, patterns, separator, hooks, per-repo .wt.toml |
| Examples | Claude Code + tmux, multi-repo workflows, environment variables |
| Installation | All platforms, shell integration, building from source |
| Development | Building, testing, running from source |
| Claude Code Plugin | Plugin that teaches Claude Code how to work with wt |
The tool wraps Git's native worktree commands with a convenient interface and organized directory structure:
MIT
Based on tree-me by Phil Haack.
Go
99.1%