A dead worktree is one that is safe to delete because all meaningful work is either gone or already preserved in the remote. There are exactly two conditions that make a worktree dead:
The main worktree (the one at the repo root) is never a candidate for deletion.
Run this in the repo root (wherever the user is working):
git fetch --prune
This removes stale remote-tracking refs (e.g. origin/feature-x that no longer exist on the
remote). Without this step, condition 2 cannot be evaluated correctly.
git worktree list --porcelain
Parse the output to get each worktree’s:
worktree — absolute path to the worktree directorybranch — the branch checked out in that worktree (e.g. refs/heads/feature-x)HEAD — current commit SHASkip the first entry — that is always the main worktree.
For every non-main worktree, run the checks below. A worktree is dead if either condition is true.
First, check the working tree:
git -C <worktree-path> status --porcelain
If the output is not empty, the worktree has uncommitted changes — stop here and do NOT mark it dead under this condition.
If the working tree is clean, determine whether unpushed commits exist. The approach depends on whether the branch has an upstream configured:
Case A — upstream is configured:
git -C <worktree-path> config --get branch.<branch-name>.merge
If this returns a value, an upstream exists. Check for unpushed commits:
git -C <worktree-path> log @{u}..HEAD --oneline
If this returns nothing, there are no unpushed commits. Mark as dead under condition 1.
Case B — no upstream configured:
Find the default branch reference. Try these in order until one succeeds:
# Option 1: use origin/HEAD if it resolves
git -C <worktree-path> rev-parse --verify refs/remotes/origin/HEAD 2>/dev/null
# If resolved, get the branch name:
git -C <worktree-path> symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's|refs/remotes/||'
# Option 2: check for origin/main
git -C <worktree-path> rev-parse --verify refs/remotes/origin/main 2>/dev/null
# Option 3: check for origin/master
git -C <worktree-path> rev-parse --verify refs/remotes/origin/master 2>/dev/null
Use the first one that resolves as DEFAULT_REF. If none resolves, skip this worktree — cannot
safely evaluate it.
Now check for commits ahead of the default:
git -C <worktree-path> log ${DEFAULT_REF}..HEAD --oneline
If there are any commits ahead of the default branch, skip this worktree — it has unshared work. Do NOT mark it dead.
If the output is empty (no commits ahead of default), mark as dead under condition 1.
After git fetch --prune (Step 1), any remote-tracking ref that no longer exists on the remote
has already been removed locally. So no second network call is needed — check the local ref store.
First, confirm the branch was configured to track origin:
git -C <worktree-path> config --get branch.<branch-name>.remote
If this returns origin, the branch was tracking a remote at some point. Now check if the
remote-tracking ref still exists locally:
git -C <worktree-path> rev-parse --verify refs/remotes/origin/<branch-name> 2>/dev/null
If this returns empty output (non-zero exit), the remote branch is gone. Mark as dead under condition 2.
Also handle the case where the worktree is in detached HEAD state — git worktree list
--porcelain emits detached instead of a branch: line. For detached HEAD worktrees, skip
condition 2 entirely. Apply condition 1 only (check working tree cleanliness and commits ahead of
default branch).
Display a table like this:
Dead worktrees found:
PATH BRANCH REASON
/path/to/worktree-a feature/foo No changes, no upstream commits
/path/to/worktree-b fix/bar Branch deleted from origin
/path/to/worktree-c chore/baz Both conditions met
If no dead worktrees are found, tell the user and stop.
Do not delete anything without explicit user confirmation.
Ask: “Shall I delete all of these, or do you want to pick specific ones?”
Wait for the user’s answer. Never auto-delete.
For each confirmed worktree, run:
git worktree remove <worktree-path>
If the worktree has uncommitted changes that somehow slipped through (shouldn’t happen, but
git worktree remove will refuse by default), report the error and do NOT use --force without
asking the user explicitly.
After deletion, run:
git worktree prune
to clean up any leftover administrative files.
Tell the user which worktrees were deleted and which (if any) were skipped due to errors.
git worktree list.--force on git worktree remove without explicit user approval.git fetch --prune fails (e.g. no network), warn the user that condition 2 cannot be
reliably evaluated and offer to proceed with condition 1 only.