Skip to content

fix(core): recover worktree removal blocked by cached locations - #51281

Open
Zhangyinglun wants to merge 1 commit into
anomalyco:v2from
Zhangyinglun:fix-worktree-remove
Open

Zhangyinglun wants to merge 1 commit into
anomalyco:v2from
Zhangyinglun:fix-worktree-remove

Conversation

@Zhangyinglun

Copy link
Copy Markdown

Issue for this PR

Closes #51172

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

On Windows, removing a worktree that has been used as a location fails halfway. A local MCP server started with the worktree as its cwd keeps the directory in use, so git worktree remove deletes the checkout and .git/worktrees/<id> but not the root. The row stays, and every retry fails with Worktree directory unavailable.

  • Worktree.remove still tries the strategy first. If that fails for a reason force wouldn't fix, it invalidates the cached local locations at or inside the worktree (the same thing debug.location.evict does; closing an idle location stops its MCP servers) and retries once. If the retry also fails, the original error is returned and the retry error is logged. Refusals that need force (git or plugin forceRequired) don't touch any location, and on Linux/macOS the first attempt succeeds, so nothing changes there.
  • The git strategy treats an empty directory as the leftover of a partial removal and deletes it, since a real checkout always has .git. Non-empty directories are left alone. This also lets a retry succeed once some other process that held the directory has exited.

Known limits, same as before this change:

  • An active session's location is only detached by invalidation, so it can keep holding the directory until the session ends. I asked in the issue whether removal should reject or interrupt active sessions.
  • A locked file inside the worktree (for example an MCP server running from the worktree's .venv) still leaves a non-empty leftover.
  • A plugin removing the worktree it is loaded in is not handled.
  • The release-and-retry also runs for failures that releasing can't fix (for example a locked worktree). Happy to limit it to Windows if you prefer.

How did you verify your code works?

  • New tests in packages/core/test/worktree.test.ts, which fail before this change and pass after:

    • locations inside the worktree are released only after a failed first attempt, then the removal is retried;
    • a dirty git worktree and a plugin forceRequired refusal release nothing;
    • an empty leftover (outside and inside the repo) is cleared on retry;
    • a non-empty leftover is kept.

    bun run test test/worktree.test.ts in packages/core: 41 pass. location-layer, location-activity and git tests also pass.

  • On Windows 11 (git 2.55), I ran the issue's reproduction script against serve from this branch with a stub stdio MCP server. Before: DELETE /api/worktree → 400 Permission denied, an empty directory and the row are left, and the retry → 400 Worktree directory unavailable. After: 204, and the directory and row are gone. With an external process holding the directory, the first delete still reports git's error, and a retry after that process exits → 204.

  • The script in the issue uses the old mcp: { <name>: ... } config. Current v2 needs mcp.servers, otherwise no MCP server starts and the bug doesn't show.

Screenshots / recordings

Not a UI change.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

On Windows a cached location can keep a local MCP server running with the worktree as its cwd, so git deletes the checkout but not its root and every retry fails. Release the locations inside the worktree after a failure that force would not fix and retry once, and let the git strategy clear the empty leftover of a partial removal.

Closes anomalyco#51172
@github-actions

Copy link
Copy Markdown
Contributor

Thanks for your contribution!

This PR doesn't have a linked issue. All PRs must reference an existing issue.

Please:

  1. Open an issue describing the bug/feature (if one doesn't exist)
  2. Add Fixes #<number> or Closes #<number> to this PR description

See CONTRIBUTING.md for details.

@github-actions

Copy link
Copy Markdown
Contributor

The following comment was made by an LLM, it may be inaccurate:

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant