---
title: "How to clean up the git worktrees your AI coding agents leave behind · Cork"
description: "Claude Code, Codex, Cursor and Conductor create git worktrees faster than anyone removes them. How to find them, tell a finished one from someone's next task, remove it without losing work, prune stale records, and keep the pile from coming back."
canonical: "https://corkcli.com/guides/clean-up-git-worktrees-ai-coding-agents"
last_updated: "2026-10-16"
---

# How to clean up the git worktrees your AI coding agents leave behind

Agent tools create a git worktree per session or per parallel agent, and they create them faster than anyone removes them. Each one is a full set of working files, often with its own dependencies and build output, and each holds a branch that git will not let you delete. This guide covers finding them, telling a finished worktree from someone's next task, removing it without losing work, and keeping the pile from coming back.

Page: https://corkcli.com/guides/clean-up-git-worktrees-ai-coding-agents

## Where agent tools put their worktrees

The folders differ by tool, and some live outside the repository, which is why they are easy to forget. The usual places:

| Tool | Usual folder |
|---|---|
| Claude Code | `.claude/worktrees/` inside the repo |
| Codex | `.codex/worktrees/` inside the repo, or `~/.codex/worktrees/` for the Codex app |
| Cursor (parallel agents) | `~/.cursor/worktrees/` |
| Conductor | `~/conductor/workspaces/` |

These are each tool's usual defaults as of October 2026; a tool can change its folder between versions, or let you set it, so treat the table as where to look first, not as the rule. Folders under your home directory can hold worktrees from many repositories. Do not clean them by looking at the folder; ask each repository, because git keeps the authoritative list.

## Step 1: list every worktree of the repo

Run this from the main checkout or any of its worktrees:

```sh
git worktree list
git worktree list --porcelain   # one field per line, for scripts
```

The main checkout comes first, then every linked worktree with its path, commit and branch. A worktree marked `locked` was locked on purpose; a worktree marked `prunable` is one whose folder no longer exists (see step 4).

## Step 2: decide which ones are finished

A worktree is safe to remove when all of these are true:

- **An agent tool made it.** It sits in one of the folders above. A worktree a person made by hand somewhere else is theirs to remove.
- **It is not locked.** The porcelain listing shows a `locked` line, with the reason if one was given.
- **Its work is merged.** `git merge-base --is-ancestor <branch> origin/main` exits 0 when every commit on the branch is already in `origin/main`. Squash and rebase merges create new commits, so a branch merged that way still looks unmerged here; check the pull request instead (with the GitHub CLI, `gh pr list --head <branch> --state merged`).
- **It has no changes.** `git -C <path> status --porcelain` prints nothing when there is nothing staged, unstaged or untracked.
- **Nobody is using it.** No agent session is still open in it, and it was not created a minute ago for a task about to start. A brand new worktree has no changes and nothing unmerged, so it passes the checks above; how recently it was touched is what tells it apart.

This script runs the git checks for every linked worktree and prints the candidates. It removes nothing, so you can read the list before acting on it. Pass a different base branch as the first argument if yours is not `origin/main`.

```sh
#!/bin/sh
# Print linked worktrees that look finished: unlocked, no changes,
# HEAD already merged into the base branch. Removes nothing.
base="${1:-origin/main}"
git fetch --quiet origin
git worktree list --porcelain | sed -n 's/^worktree //p' | tail -n +2 |
while IFS= read -r wt; do
  if [ ! -d "$wt" ]; then echo "missing:  $wt (git worktree prune)"; continue; fi
  [ -e "$(git -C "$wt" rev-parse --absolute-git-dir)/locked" ] && continue
  [ -n "$(git -C "$wt" status --porcelain)" ] && continue
  git -C "$wt" merge-base --is-ancestor HEAD "$base" || continue
  echo "finished: $wt ($(git -C "$wt" branch --show-current), last commit $(git -C "$wt" log -1 --format=%cr))"
done
```

It prints each candidate's branch and the age of its last commit. A fresh worktree shows the age of the commit it was branched from, so the age alone is not proof that nobody is working there.

## Step 3: remove the finished ones

```sh
git worktree remove .claude/worktrees/search
git branch -d worktree-search      # optional: delete the merged branch too
```

`git worktree remove` refuses a worktree with modified tracked files or untracked files. Treat that refusal as information, not an obstacle: look at what is there before reaching for `--force`.

Two things it will not warn you about. Ignored files (`.env`, `node_modules`, build output) are not changes, so they are deleted with the folder. If an agent might have edited a local config file only in that worktree, check first:

```sh
git -C .claude/worktrees/search status --porcelain --ignored
```

And removing a worktree keeps its branch. That is what makes removal safe to undo, and it is also why branches pile up after worktrees do. `git branch -d` deletes only a fully merged branch (merged into its upstream, or into the branch you have checked out if it has no upstream), so it is the safe choice; `-D` deletes regardless. If git refuses to delete a branch because it is checked out, some other worktree still has it: find that one with `git worktree list`.

### Bringing one back

As long as the branch exists, a removed worktree is one command away (dependencies and ignored files have to be set up again):

```sh
git worktree add .claude/worktrees/search worktree-search
```

## Step 4: prune records of folders deleted by hand

If someone deleted a worktree folder with `rm -rf` or a file manager, git still has a record of it, the listing marks it prunable, and its branch still counts as checked out. Clear those records:

```sh
git worktree prune --dry-run --verbose   # show what would go
git worktree prune
```

A locked worktree is never pruned, which is the point of locking one that lives on a removable drive or a network share.

## Keep the pile from coming back

- **Remove at landing.** Make `git worktree remove` the last step of the routine that merges an agent's branch, so a worktree lives exactly as long as its work.
- **Lock the ones you mean to keep.** `git worktree lock --reason "release branch" <path>` protects a long-lived worktree from pruning and removal, and tells anyone cleaning up why it is there.
- **Keep repo-local worktree folders out of git.** Add `.claude/worktrees/` (or your tool's folder) to `.gitignore` so the main checkout does not show them as untracked files.
- **Create fewer.** Many worktrees exist only to keep two agents from overwriting each other, not because the work needs its own branch. [Do you need git worktrees to run AI coding agents in parallel?](https://corkcli.com/guides/do-you-need-git-worktrees-for-ai-coding-agents) sorts the two.

Agent tools also clean up some of their own worktrees: on exit, Claude Code removes a clean unnamed worktree and asks about one with changes or commits, and subagent worktrees made with `isolation: worktree` are removed when the subagent made no changes. Worktrees whose work was committed and merged later are the ones that stay.

## How Cork does it for you

[Cork](https://corkcli.com) keeps Claude Code, Codex and Cursor agents from overwriting each other on one codebase, so in a checkout with Cork an agent does not need a worktree just to stay out of another agent's way. For the worktrees that exist anyway, it runs the checklist above at session start, in the background so the session does not wait, and re-checks every condition at the moment of removal. It only removes a worktree that:

- an agent tool made, in one of the folders in the table above (never one a person made by hand elsewhere),
- is not locked,
- is on a branch, or a detached commit, already merged into the main checkout's branch or the remote's default branch,
- has no changes, staged, unstaged or untracked, and
- nobody has touched for a day, since a fresh one is someone's next task.

It uses plain `git worktree remove`, which refuses a dirty worktree by itself, keeps every branch, and tells the next session what it removed and that `git worktree add <folder> <branch>` brings one back. If your tool keeps worktrees somewhere else, add the folder under `agent_worktree_dirs` in `.cork/config.json` or in `CORK_AGENT_WORKTREE_DIRS` (colon-separated). To turn the tidying off, set `{ "tidy_worktrees": false }` in `.cork/config.json`, or `CORK_TIDY_WORKTREES=0` in the environment.

Cork, free and local, covers one machine with no account:

```sh
curl -fsSL https://corkcli.com/install.sh | sh
```

## Common questions

### Does removing a git worktree delete its branch?

No. `git worktree remove` deletes the folder and git's record of it; the branch and its commits stay in the repository. Delete a merged branch separately with `git branch -d`, which refuses a branch that is not merged. To bring a removed worktree back, run `git worktree add <folder> <branch>`.

### What is the difference between git worktree remove and deleting the folder?

`git worktree remove` checks that the worktree is clean, refuses if it has modified tracked files or untracked files, and removes both the folder and the administrative record under `.git/worktrees`. Deleting the folder by hand skips the check and leaves the record behind, so `git worktree list` shows the entry as prunable until you run `git worktree prune`.

### Why can't I delete a branch after an agent finished with it?

git refuses to delete a branch that is checked out in any worktree, including a linked one you forgot about. Find it with `git worktree list`, remove that worktree, then delete the branch.

### Can removing a worktree lose my .env or other local files?

Yes. Ignored files, such as `.env`, `node_modules` or build output, do not count as changes, so `git worktree remove` deletes them with the folder without asking. Run `git -C <path> status --porcelain --ignored` first if the worktree might hold a local file you edited only there.

### How do I remove a locked worktree?

Someone locked it on purpose, so check why first: `git worktree list --porcelain` prints the lock reason. If it is safe, run `git worktree unlock <path>`, then `git worktree remove <path>`.

### Can Cork clean up agent worktrees for me?

Yes. At session start Cork removes, in the background, worktrees that an agent tool created in its usual folder, that are unlocked, merged into the main checkout's branch or the remote's default branch, have no changes (staged, unstaged or untracked) and have sat untouched for a day. It uses plain `git worktree remove`, keeps every branch, and tells the next session what it removed and how to bring one back. It never touches a worktree a person made somewhere else. Turn it off with `"tidy_worktrees": false` in `.cork/config.json` or `CORK_TIDY_WORKTREES=0`.

## Related

- [Git worktrees for parallel AI coding agents: setup and limits](https://corkcli.com/blog/git-worktrees-parallel-ai-coding-agents)
- [Cork vs git worktrees](https://corkcli.com/blog/cork-vs-git-worktrees)
- [Install Cork](https://corkcli.com/install)
