git worktree remove: cleaning up properly

Removing a worktree is one command. This page exists because most people delete the directory first, then discover git still believes it is there, and because the refusals are worth understanding rather than forcing past.

By the Continuum team. We build a workbench that runs Claude Code, Codex, and their peers, so the model rates quoted here are the ones our own cost analytics ship with.

The short version

git worktree remove <path> deletes the working directory and its administrative record together. It refuses when the worktree has modified or untracked files, and refuses twice as hard when the worktree is locked. It never deletes the branch. If you already deleted the directory by hand, remove still works on the path, and git worktree prune clears every stale record at once.

What you need to know
  • git worktree remove <path> is the correct way. Pass the path, never the branch name.
  • It refuses when dirty, and untracked files count. That refusal is protecting you.
  • A locked worktree needs --force twice, or unlock first.
  • Removing a worktree does not delete the branch or its commits.
  • Already deleted the directory? remove still works, and prune clears all of them.
  • Check the diff before you force. Uncommitted agent work is still work.

Removing one

Find it, look at it, remove it. The middle step is the one people skip and the one that saves them.

Output from git 2.54, August 2026.
# what exists
$ git worktree list
/Users/you/code/app        ad7da80 [main]
/Users/you/code/app-auth   ad7da80 [feature/auth]

# look before you delete
$ git -C ../app-auth status --short
 M src/auth.ts
?? src/auth.test.ts

# remove it
$ git worktree remove ../app-auth
fatal: '../app-auth' contains modified or untracked files, use --force to delete it

# then, if you are sure
$ git worktree remove --force ../app-auth

Every refusal, and what it means

Verbatim messages from git 2.54.

MessageMeansDo this
contains modified or untracked filesThe tree is dirtygit -C <path> status, then --force
cannot remove a locked working treeSomeone locked it deliberatelygit worktree unlock <path>, or --force --force
<path> is a main working treeYou aimed at the original cloneYou cannot remove it; delete the whole directory
<path> is not a working treeWrong path, or you passed a branch nameCopy the path from git worktree list
working trees containing submodules cannot be moved or removedPopulated submodules insideDeinit the submodules, or delete by hand and prune
The locked case in full. Note the second line tells you the answer.
$ git worktree lock ../app-usb --reason "on an external disk"
$ git worktree remove ../app-usb
fatal: cannot remove a locked working tree, lock reason: on an external disk
use 'remove -f -f' to override or unlock first

# the polite route
$ git worktree unlock ../app-usb
$ git worktree remove ../app-usb

The branch is not removed

Removing a worktree removes a working directory. The branch, and every commit on it, stays exactly where it was. This surprises people who expect remove to be an undo.

# worktree gone, branch still there
$ git worktree remove ../app-auth
$ git branch --list "feature/*"
  feature/auth

# delete the branch too, if it is merged
$ git branch -d feature/auth

# force delete an unmerged branch: this DOES orphan commits
$ git branch -D feature/auth

Removing every merged worktree at once

Once you routinely run several agents, cleanup becomes the chore. Drive it off the merge state rather than off memory.

Parses the porcelain form, so it does not break on long paths.
#!/usr/bin/env bash
# Remove worktrees whose branch is already merged into main.
set -euo pipefail

git worktree list --porcelain \
 | awk '/^worktree /{p=$2} /^branch /{print p, $2}' \
 | while read -r path ref; do
     branch="${ref#refs/heads/}"
     [ "$branch" = "main" ] && continue
     if git merge-base --is-ancestor "$branch" main 2>/dev/null; then
       echo "merged, removing: $branch"
       git worktree remove "$path" && git branch -d "$branch"
     fi
   done

If you forced it and want it back

Split this into two cases before you panic, because they have very different answers. Committed work is almost always recoverable. Uncommitted work is not. That asymmetry is the entire reason remove refuses on a dirty tree.

01

Read what git already told you

git branch -D prints the tip commit it just dropped. If the terminal is still open, the hard part is done.

$ git branch -D feature/auth
Deleted branch feature/auth (was 8ff611a).
02

Otherwise, find the dangling commit

Deleting a branch deletes its reflog too, so the commit becomes unreachable rather than gone. fsck lists exactly those.

$ git fsck --lost-found
dangling commit 8ff611a497ab01f62ecde2f78b0bea4f4ea88459

$ git log --oneline 8ff611a | head -3
8ff611a agent commit
45fc22d init
03

Give it a name again

$ git branch recovered 8ff611a
$ git worktree add ../app-recovered recovered

When you deleted the directory by hand

This is the common case, because rm -rf is the obvious thing to do. It leaves git holding a registration that points at nothing, and git worktree list starts annotating it as prunable.

$ rm -rf ../app-auth          # the tempting shortcut

$ git worktree list
/Users/you/code/app        ad7da80 [main]
/Users/you/code/app-auth   ad7da80 [feature/auth] prunable

# option A: remove still works on the path
$ git worktree remove ../app-auth

# option B: clear every stale record at once
$ git worktree prune --dry-run --verbose
Removing worktrees/app-auth: gitdir file points to non-existent location
$ git worktree prune

The repair commands, and which situation each is for.

SituationCommand
I want this worktree gonegit worktree remove <path>
I already deleted several directoriesgit worktree prune
I moved a worktree with mvgit worktree repair <new-path>
I moved the whole repositorygit worktree repair in the main worktree
I want to relocate one properlygit worktree move <from> <to>
A worktree lives on a disk that comes and goesgit worktree lock

Questions people ask

Run git worktree remove <path> from inside the repository. It deletes the directory and clears the administrative record together. Pass the path shown by git worktree list, not the branch name.

It has modified or untracked files. Run git -C <path> status --short to see exactly what, then use --force only once you are sure you do not want them. Untracked files count, so a stray log file is enough to trigger the refusal.

Either git worktree unlock <path> then remove it normally, or pass --force twice: git worktree remove -f -f <path>. Git tells you this in the second line of the error message.

No. The branch and all its commits remain. Delete it separately with git branch -d, or -D to force an unmerged branch, which orphans the commits until the reflog expires.

Either git worktree remove <path>, which still works on a missing directory, or git worktree prune to clear every stale record at once. Use prune --dry-run --verbose first if you want to see the list.

No. git worktree remove refuses with "is a main working tree". The main worktree is the original clone, so removing it means deleting the repository. Delete the directory yourself if that is really what you want.

Parse git worktree list --porcelain, check each branch with git merge-base --is-ancestor against main, and remove the ones already merged. Use plain remove and branch -d so anything with unfinished work refuses instead of disappearing.

If it was committed, yes. git branch -D prints the tip commit it dropped, and git fsck --lost-found lists dangling commits after the fact. Recreate a branch at that sha. Unreachable objects survive until git gc prunes them, which defaults to two weeks. Uncommitted changes were never objects and cannot be recovered.

Sources

Every figure above was read from these pages on August 2026. Vendors reprice without notice; if you find a stale number, tell us.

  1. git-worktree manual
  2. Pro Git book
Try it

Cleanup,
handled.

Continuum removes the worktree when a session ends and warns first if an agent left uncommitted work.

free app · your subscriptions · local-first