How to clean up git worktrees without losing work

A checklist for deleting old git worktrees safely: the six kinds of work that hide in them, why git branch --merged misses squash merges, and how to check each one.

Short answer: before removing a worktree, check it for six kinds of unlanded work: uncommitted changes, untracked files, unpushed commits, branches with no upstream, stashes, and interrupted rebases or merges. Then confirm the branch is really in main. git branch --merged alone isn’t enough, because squash and rebase merges create new commits.

Why old worktrees are risky to delete

Worktrees are cheap to create, so they accumulate, especially when AI coding agents each get their own. A week later some branches were squash-merged on GitHub, some have commits that only exist on your Mac, and some contain changes an agent made but nobody committed. git worktree remove --force or rm -rf deletes uncommitted and untracked files permanently: they never reached git’s object database, so there is no reflog to recover them from.

The six kinds of unlanded work

WhatHow to check
Uncommitted changesgit status --porcelain shows lines starting with M, A, D
Untracked filesLines starting with ?? in the same output
Commits not pushedgit log @{upstream}..HEAD --oneline
Branch with no upstreamgit rev-parse --abbrev-ref @{upstream} fails
Stashesgit stash list (shared by all worktrees of a repository)
Interrupted rebase, merge or cherry-pickgit status mentions it, or rebase-merge, MERGE_HEAD or CHERRY_PICK_HEAD exists in the worktree’s git dir

Also look for ignored files you care about, such as .env. They don’t show up in any of the checks above and are not in any commit.

Is the branch already in main?

A worktree with no local-only work can still hold a branch whose changes never landed. How you check depends on how the branch was merged.

Regular merge or fast-forward

git merge-base --is-ancestor feat/search main && echo merged

If the branch tip is an ancestor of main, every commit is in main. This is what git branch --merged main reports.

Rebase merge or cherry-pick

git cherry main feat/search

The commits in main have different hashes, so the ancestry check fails. git cherry compares patch IDs instead: lines starting with - are commits whose changes already exist in main, + means not yet.

Squash merge

The hardest case, and the default on many GitHub repositories. All commits become one new commit, so neither ancestry nor per-commit patch IDs match. Compare the branch’s total diff against the squash commit instead, or ask GitHub:

gh pr list --state merged --head feat/search

A merged PR for that branch is strong evidence, as long as the branch hasn’t gained commits since the merge.

A safe cleanup routine

  1. Make sure no agent or editor is still working in the directory.
  2. Run the six checks above and handle anything they find: commit, push, or decide it’s disposable.
  3. Confirm the branch is in main with the method that matches how it was merged.
  4. Back up ignored files you need, like .env.
  5. git worktree remove <path> (errors explained in how to remove a git worktree) without --force, so git stops you if something is left.
  6. Delete the branch with git branch -d, or -D after a squash merge since git can’t see that it was merged.
  7. Run git worktree prune for any folders that were deleted by hand.

Doing this for every worktree, automatically

That routine is fine for one worktree and tedious for twenty. Orchard runs these checks continuously for every worktree on your Mac. It decides whether work is in main with a three-layer check (commit ancestry, patch and squash comparison cross-checked against the PR on GitHub, and a comparison of uncommitted changes) and shows the evidence for each verdict. Cleanup snapshots uncommitted changes, untracked files and the branch tip first and keeps them for 7 days, so a cleanup can be undone with one click. Worktrees with a running agent or an interrupted rebase are never cleaned up.

New to worktrees? Start with git worktree explained.

FAQ

Why doesn’t git branch --merged show my squash-merged branch?

A squash merge creates one brand-new commit on main, so your branch’s commits are never ancestors of main. git branch --merged only checks ancestry. Check the merged pull request, or compare the branch’s total diff against main, instead.

Can I recover files after git worktree remove --force?

Committed work survives in the branch and reflog. Uncommitted and untracked files do not: they were never stored in git, so they are gone unless you made a backup or snapshot before removing the worktree.

What does git worktree prune do?

It deletes git’s bookkeeping for worktrees whose directories no longer exist, for example after rm -rf. It doesn’t touch existing worktrees, branches or commits, and it frees the branch to be checked out elsewhere.