Skip to content

worktree remove: delete git worktrees

Remove one or more worktrees.

Terminal window
worktree remove [branch]
worktree remove [branch] --force
worktree remove <branch> --force --json
Terminal window
worktree remove feature/add-bulk-actions
worktree remove feature/add-bulk-actions --force
worktree rm feature/add-bulk-actions

The alias rm is available for remove.

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.

If an agent session is working in the worktree you name, the prompt opens with that fact and then says whatever it would have said anyway:

An agent session (feature-one-1f) is working in this worktree. Are you sure you want to remove this worktree?

remove warns here and does not refuse: it names one worktree that you chose. A session that is only idle does not count, so nothing is added for it, and the run prints exactly what it printed before sessions were looked up.

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, but it does not stay silent about an agent. If a session was working in the worktree, remove prints a warning on stderr after removing it, and every --json run (which needs --force) carries the same text in the document’s warnings:

Removed feature/add-bulk-actions while an agent session (feature-one-1f) was working in it.

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, and a worktree an agent session is working in is listed under the active branches with its Agent: detail beside it. A session that is only idle leaves the worktree where it was. Selecting an active branch asks the usual “not safe to delete” question, and --force skips it and warns as above.

A worktree on a detached HEAD is listed as (detached HEAD), and every one carries that same label, so a name cannot tell them apart. Name the worktree by its path instead:

Terminal window
worktree remove /abs/repo.worktrees/scratch --force

An argument is read as a path only when it is written as one — absolute, ., .., or starting with ./ or ../ — and as a branch name otherwise; remove "(detached HEAD)" reaches only the first. A path that names no worktree exits 2 with No worktree to remove at "<path>"., and so does the worktree you are standing in: the current worktree is never removed, detached or not. A detached worktree has no branch, so remove and cleanup remove its directory and git’s record of it without running git branch -D.

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. With rex, it kills the Rex session instead, but only one whose every shell is in that worktree; see Rex Sessions.

With opener set to auto, remove asks every installed tool, not only the one it would open in today: a worktree Herdr opened still has its space closed when you run remove from a plain terminal, or from inside Rex. Both lookups run at once, a tool that is not installed is not asked, and one tool failing to answer is a warning that does not stop the other from closing what it holds.

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, editor or none, nothing here applies and no Herdr or Rex process is started. A fixed herdr or rex asks that tool only. See Herdr Spaces.

--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.

Terminal window
worktree remove feature/add-bulk-actions --force --json
{"removed":[{"branch":"feature/add-bulk-actions","path":"/abs/repo.worktrees/feature/add-bulk-actions"}],"herdrSpacesClosed":["w5"],"rexSessionsClosed":[],"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 neither herdr nor auto, or nothing was open
rexSessionsClosed the Rex session ids that were killed; [] when opener is neither rex nor auto, or nothing was open
warnings what went wrong without stopping the run, such as a Herdr space or Rex session 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.