Guide · git worktrees
Clean up the git worktrees your coding agents left behind
Updated · Scott Havird
short answer
In each repository, run git worktree list. For every worktree you no longer use, make sure git status is clean and its commits are merged or pushed, then run git worktree remove <path> (without --force) and git worktree prune. To check every repository at once, lysosome worktrees ~/code does those checks read-only and lists what is safe to remove.
Why agent worktrees pile up
A git worktree is a second working folder attached to the same repository, checked out on its own branch. Coding agents use them so several tasks can run in parallel without touching your main checkout: Claude Code's --worktree mode, Cursor's parallel agents and most multi-agent setups create one per task.
Nothing removes them when the task is done. The worktree itself is a full copy of the tracked files, and the bigger cost is what gets built inside it: each one usually gets its own node_modules, Rust target/ or Python .venv. pnpm hard-links packages from a shared store, so its installs cost less, but npm, Yarn, Cargo and virtualenvs make a full copy per worktree. A few weeks of agent work can leave dozens of multi-gigabyte folders nobody will open again.
Where they live
| Created by | Usual location |
|---|---|
Claude Code (claude --worktree) | <repo>/.claude/worktrees/<name> |
| Cursor parallel agents | ~/.cursor/worktrees/<repo>/<name> |
git worktree add, scripts, other agents | Anywhere you or the tool chose, often a sibling folder or ~/.worktrees |
The main repository keeps a record of each one in .git/worktrees/. If a worktree folder is deleted by hand, that record stays behind until git worktree prune clears it.
Check sizes and state by hand
List the worktrees of one repository and see how much space they take:
git -C ~/code/shop worktree list
du -sh ~/code/shop/.claude/worktrees/* | sort -h
To find linked worktrees across many repositories, look for .git files: a linked worktree has a small .git file pointing back at its repository instead of a .git folder. Submodules have one too, so check each hit.
find ~/code ~/.cursor/worktrees -maxdepth 6 -name .git -type f 2>/dev/null
Before removing a worktree, check three things inside it:
cd ~/code/shop/.claude/worktrees/checkout-v2
git status --short # uncommitted or untracked files?
git log --oneline @{u}..HEAD # commits that aren't on the upstream branch
git -C ~/code/shop branch --merged main # is the branch merged into main?
If @{u} fails, the branch has no upstream, so its commits may exist only in this worktree. git worktree list also marks worktrees that are locked (someone asked git to keep it) or prunable (the folder is already gone).
Squash merges are the common trap: after a squash-merged pull request, git still reports the branch's commits as unmerged, because the merged commit has a different hash. Check the pull request itself, for example with gh pr list --state merged --head <branch>.
Remove them safely by hand
git -C ~/code/shop worktree remove ~/code/shop/.claude/worktrees/checkout-v2
git -C ~/code/shop worktree prune
git -C ~/code/shop branch -d feat/checkout-v2 # optional; -d refuses unmerged branches
- Use
git worktree remove, notrm -rf. It refuses to remove a worktree with modified or untracked files, and it updates the repository's records. - Avoid
--forceunless you have checked the worktree yourself. It skips exactly the safety checks you want. - Ignored files go with it.
git worktree removedeletes git-ignored files too, including.envfiles and local databases. Copy out anything you want to keep first. - The branch stays. Removing a worktree never deletes its branch, so
git worktree add <path> <branch>brings it back. - Stale records are harmless.
git worktree prune --dry-run --verboseshows records that point at folders that no longer exist.
Want to keep a worktree but get the space back? Delete only its build and dependency folders (node_modules, target, .venv); the next install or build recreates them. The build artifacts guide covers how to do that safely.
How lysosome does it
The open-source lysosome CLI runs the same checks for every worktree under the folders you give it, without changing anything:
lysosome worktrees ~/code ~/.cursor/worktrees # read-only report
lysosome worktrees --days 14 # call anything idle 14+ days dormant (default 30)
lysosome clean ~/code --worktrees # dry run: what would be removed
lysosome clean ~/code --worktrees --yes # remove the REMOVABLE ones
Each worktree gets a status and a reason:
- REMOVABLE: idle for 30+ days, clean, not locked, and every commit merged into the default branch or pushed. With the GitHub CLI (
gh) signed in, a branch whose pull request was squash-merged at exactly the worktree'sHEADcounts too (--no-ghturns that lookup off). - REVIEW: uncommitted changes, untracked files, a lock, or commits that exist nowhere else. The reason is printed underneath, and these are never removed.
- ORPHAN: the parent repository was moved or deleted. Reported, never cleaned.
- ACTIVE: used recently. Hidden unless you pass
--all.
clean analyzes each worktree again right before acting and skips it if anything changed. It runs git worktree remove without --force, then git worktree prune, keeps the branch, moves ignored files that aren't build output (like .env) to the Trash first, and writes every action to a log. To keep the worktrees and free only their dependencies and build output, use lysosome clean ~/code --strip-artifacts --tier regenerable_expensive.
If you use Claude Code, the lysosome plugin (/plugin install lysosome --marketplace lysosome-dev/cli) checks a worktree read-only when its session ends and tells the next session what is safe to reclaim. The Mac app goes further: it ties each worktree to the Claude Code or Cursor session that made it, so you can tell a live task from a forgotten one.