worktree remove
Remove one or more worktrees.
Usage
worktree remove [branch]
worktree remove [branch] --force
worktree remove <branch> --force --jsonExamples
worktree remove feature/add-bulk-actions
worktree remove feature/add-bulk-actions --force
worktree rm feature/add-bulk-actionsThe alias rm is available for remove.
The confirmation prompt
Naming a branch removes that worktree after one confirmation. The prompt says what you stand to lose, rather than asking a bare “are you sure”:
This branch is 1 commit ahead so you might lose some work. Are you sure you want to remove this worktree?This branch has 3 uncommitted changes so you might lose some work. Are you sure you want to remove this worktree?Commits count here even when the branch was never pushed. Where there is no upstream the
count is taken against origin/HEAD, falling back to
defaultSourceBranch. If neither resolves the count cannot be taken
at all, and the prompt says that instead of implying there is nothing to weigh:
Unpushed commits could not be counted for this branch (no comparison base; set defaultSourceBranch or run git remote set-head origin --auto), so it may carry work that exists nowhere else. Are you sure you want to remove this worktree?A worktree with nothing at stake gets the plain question — and a branch whose commits are all already applied in the comparison base is one of those, however many of them there are. It is counted as ahead, because a squash merge and a rebase both give the same work new commit identities, but there is nothing in it to lose. Warning about those commits would teach you that this prompt does not mean anything, which is how the one that does gets waved through.
Removing has no default, so a non-interactive run needs -f
(--force) and a branch name: without -f it exits 2 with worktree: no default for confirmation to remove <branch>; pass -f, and with no branch it names <branchName> -f rather than showing the picker.
--force skips the prompt entirely. Unlike cleanup, remove
names one worktree that you chose, so --force here answers only the confirmation you
would otherwise have been given.
Running remove with no branch shows a picker instead, grouped into branches that are safe
to delete and branches that are still active. Each entry carries the same detail
list shows, so unpushed commits are visible before you select
anything.
Herdr spaces
With opener set to herdr, removing a worktree also closes the Herdr space
that was built around it, so the space does not outlive the checkout.
Only a worktree that was actually removed has its space closed. A branch that was not found, a
confirmation you declined and a removal that failed all leave the space alone — their checkouts are
still on disk. A failed close is a warning and the command still exits 0: by then the worktree is
already gone, so there is nothing to retry.
With opener unset or set to editor, nothing here applies and no Herdr process is started. See
Herdr Spaces.
JSON output
--json prints exactly one JSON document on stdout, on one line, and implies a
non-interactive run: nothing is asked, so it needs a branch name
and -f.
worktree remove feature/add-bulk-actions --force --json{"removed":[{"branch":"feature/add-bulk-actions","path":"/abs/repo.worktrees/feature/add-bulk-actions"}],"herdrSpacesClosed":["w5"],"warnings":[]}| Key | Value |
|---|---|
removed | the worktrees that were removed, each with its branch and path |
herdrSpacesClosed | the Herdr workspace ids that were closed; [] when opener is not herdr or nothing was open |
warnings | what went wrong without stopping the run, such as a Herdr space that would not close |
The spinner lines and warnings go to stderr. A failure prints {"error":{"code","message"}} on stdout instead
and exits non-zero; the codes are listed under branch. A named branch
that does not exist is not_found, a missing -f is missing_value, and a removal that did not happen is
failed — with --json, “nothing removed” is never reported as a success. When git refused the removal, its
reason is in the message (Could not remove the worktree <branch>: fatal: …); without --json the same line
goes to stderr and the exit is 1.