wt remove
Remove worktree; delete branch if merged. Defaults to the current worktree.
Examples
Section titled “Examples”Remove current worktree:
wt remove◎ Running pre-remove project:cleanup flyctl scale count 0Scaling app to 0 machines◎ Removing api worktree & branch in background (same commit as main, _)○ Switched to worktree for main @ ~/repoRemove specific worktrees / branches:
wt remove feature-branchwt remove old-feature another-branchKeep the branch:
wt remove --no-delete-branch feature-branchForce-delete an unmerged branch:
wt remove -D experimentalBranch cleanup
Section titled “Branch cleanup”By default, branches are deleted when they would add no changes to the default branch if merged. This works with both unchanged git histories, and squash-merge or rebase workflows where commit history differs but file changes match.
Worktrunk checks six conditions (in order of cost):
- Same commit — Branch HEAD equals the default branch. Shows
_inwt list. - Ancestor — Branch is in target’s history (fast-forward or rebase case). Shows
⊂. - No added changes — Three-dot diff (
target...branch) is empty. Shows⊂. - Trees match — Branch tree SHA equals target tree SHA. Shows
⊂. - Merge adds nothing — Simulated merge produces the same tree as target. Handles squash-merged branches where target has advanced with changes to different files. Shows
⊂. - Patch-id match — Branch’s entire diff matches a single squash-merge commit on target. Fallback for when the simulated merge conflicts because target later modified the same files the branch touched. Shows
⊂.
The default-branch walk is capped so a single check stays fast; a squash merge with hundreds of commits landed since the merge point falls outside the cap and needs -D to remove.
The ‘same commit’ check uses the local default branch; for other checks, ‘target’ means the default branch, or its upstream (e.g., origin/main) when strictly ahead.
Branches matching these conditions and with empty working trees are dimmed in wt list as safe to delete.
Those six ask whether deleting loses work. A branch checked out in a second worktree (only reachable via git worktree add --force) fails a different test: deleting the ref would leave that worktree unable to resolve HEAD, which is why git branch -d refuses the same delete. Such a branch is retained whatever -D asks, and the surviving checkout is named.
Force flags
Section titled “Force flags”Worktrunk has two force flags for different situations:
| Flag | Scope | When to use |
|---|---|---|
--force (-f) | Worktree | Worktree has uncommitted changes |
--force-delete (-D) | Branch | Branch has unmerged commits |
wt remove feature --force # Remove dirty worktreewt remove feature -D # Delete unmerged branchwt remove feature --force -D # BothUse --no-delete-branch to keep the branch regardless of merge status.
Background removal
Section titled “Background removal”Removal runs in the background by default — the command returns immediately. The worktree is renamed into .git/wt/trash/ (instant same-filesystem rename), git metadata is pruned, the branch is deleted, and a detached rm -rf finishes cleanup. Cross-filesystem worktrees fall back to git worktree remove. Logs: .git/wt/logs/{branch}/internal/remove.log. Use --foreground to run in the foreground.
After each wt remove, entries in .git/wt/trash/ older than 24 hours are swept by a detached rm -rf — eventual cleanup for directories orphaned when a previous background removal was interrupted (SIGKILL, reboot, disk full).
Reaping processes
Section titled “Reaping processes”
--reap terminates processes left running in the worktree before it is removed — a post-start dev server, a file watcher, a language server — freeing the ports and file handles they hold. Processes are discovered by working directory: any process whose current directory is at or under the worktree path (SIGTERM, then SIGKILL for survivors).
wt remove --reap feature◎ Reaping 2 processes under feature worktree ┃ 51234 node ┃ 51240 esbuild✓ Reaped 2 processes◎ Removing feature worktree & branch in background (same commit as main, _)To avoid killing work the user did not mean to kill, two guards keep --reap conservative:
- Interactive processes are spared. A process holding a controlling terminal — an interactive shell, or a terminal editor such as
vimwith unsaved buffers — is never reaped. Only detached processes remain candidates. - Discovery is by working directory only. A process that started in the worktree and later changed directory, or a daemon that reparented to
init, no longer reports a directory under the worktree and is not found. To reliably reap those, launch them withwt step tether, which kills the whole process group when the worktree is removed.
Reaping runs before the worktree directory is touched, so it is independent of foreground/background removal and the --force flag. Unix only; on Windows --reap is rejected.
JSON output
Section titled “JSON output”--format=json prints one object per removal to stdout: {kind, branch, path, branch_outcome, branch_checked_out_at} for a worktree, with pruned in place of path for a branch-only removal.
branch_outcome names what happened to the branch, so a caller can tell a deletion the removal declined from one it was never asked to make:
| Value | Meaning |
|---|---|
deleted | The branch is gone |
deferred | Handed to the detached background process, whose result this run never sees. --foreground never reports it |
not_attempted | No deletion was tried: a detached worktree, a sibling checkout, or --no-delete-branch |
retained_unmerged | Declined: the branch was not integrated into the target |
retained_checked_out | Declined: the final topology read found a live worktree with it checked out |
retained_raced | Refused by the compare-and-swap — the branch moved between the integration check and the delete. Re-read the ref and retry |
retained_failed | The delete command itself failed |
pre-remove hooks run before the worktree is deleted (with access to worktree files). post-remove hooks run after removal. See wt hook for configuration.
Detached HEAD worktrees
Section titled “Detached HEAD worktrees”Detached worktrees have no branch name. Pass the worktree path instead: wt remove /path/to/worktree.
See also
Section titled “See also”Command reference
Section titled “Command reference”wt remove - Remove worktree; delete branch if mergedDefaults to the current worktree.Usage: wt remove [OPTIONS] [BRANCHES]...Arguments:[BRANCHES]...Branch name or worktree path Options:--no-delete-branchKeep branch after removal-D, --force-deleteDelete unmerged branches--foregroundRun removal in foreground (block until complete)--reapKill processes started in the worktree Before removal, terminate processes whose working directory is under the worktree — devservers, watchers, language servers. Processes holding a controlling terminal (interactiveshells, terminal editors) are left alone. Unix only.-f, --forceForce worktree removalRemove a dirty worktree, including staged, modified, and untracked files. Without thisflag, removal fails if the worktree has any uncommitted changes.-h, --helpPrint help (see a summary with '-h')Automation:--no-hooksSkip hooks--format <FORMAT>Output formatJSON prints structured result to stdout after removal completes.Global Options:-C <path>Working directory for this command--config <path>User config file path--config-set <toml>Override config with inline TOML, e.g. --config-set list.full=true (repeatable)-v, --verbose...Verbose output (-v: info logs + hook/alias template variables on stderr; -vv: also debuglogs and raw subprocess output written to .git/wt/logs/). Set WORKTRUNK_VERBOSE=0|1|2 toapply the same level everywhere — including shell completion, which no flag can reach-y, --yesSkip approval prompts