How Pi Web Handles Dirty Worktree Detection and Force Removal

Pi Web detects dirty worktrees by catching Git errors in lib/worktree.ts, returning HTTP 409 when uncommitted changes block removal, and allowing force deletion via a force query parameter that adds the --force flag to git worktree remove.

Managing Git worktrees safely requires handling uncommitted changes that block deletion. In the agegr/pi-web repository, dirty worktree detection is implemented through a combination of Git error parsing and structured API responses. This article examines how the application detects dirty states and provides a force-removal mechanism to discard changes when necessary.

Detecting Dirty Worktrees in lib/worktree.ts

The worktree management logic resides in lib/worktree.ts, where the removeWorktree() function orchestrates deletion attempts. When invoked, the function first validates the target through listWorktrees() to ensure it is not the main worktree and filters out prunable or missing entries (lines 38‑49).

The function then executes the standard Git command:

git worktree remove <path>

If the worktree contains uncommitted or untracked changes, Git aborts the operation and returns an error containing the word "dirty". The removeWorktree() wrapper catches this failure and processes the error through extractGitError() (lines 26‑30), which sanitizes the Git output before re‑throwing it as a standard Error object.

API Route Error Translation and the 409 Response

The HTTP layer in app/api/worktrees/route.ts bridges Git errors and client responses. When handling a DELETE request, the route invokes removeWorktree() with the force parameter set to false by default.

If the function throws an error containing "dirty", the route intercepts this specific case and responds with HTTP 409 Conflict and a JSON payload:

{ "dirty": true }

This structured response signals to the frontend that the worktree contains unsaved changes, triggering the display of a Force Remove button in the user interface.

Implementing Force Removal with the --force Flag

When the user opts to force removal, the client repeats the DELETE request with the query parameter force=1. The API route detects this flag and calls removeWorktree() with force=true, which modifies the Git command construction at line 19 of lib/worktree.ts.

With the force flag enabled, the function appends --force to the Git command:

git worktree remove --force <path>

This instructs Git to discard all uncommitted and untracked changes before removing the worktree directory. Upon successful execution, the route returns HTTP 200 OK, confirming the forced deletion.

Client-Side Integration

The frontend interprets the 409 status code to reveal the Force Remove option, while the 200 status confirms successful deletion. The client implementation uses standard fetch requests to communicate with the worktree API:

// Normal removal attempt - fails if dirty
await fetch(`/api/worktrees?path=${encodeURIComponent(worktreePath)}`, {
  method: "DELETE",
});

// Force removal - discards uncommitted changes
await fetch(`/api/worktrees?path=${encodeURIComponent(worktreePath)}&force=1`, {
  method: "DELETE",
});

Summary

  • Validation first: listWorktrees() in lib/worktree.ts filters out invalid targets before attempting removal (lines 38‑49).
  • Error parsing: extractGitError() converts Git failures into readable messages, specifically detecting "dirty" states (lines 26‑30).
  • Structured responses: The API route returns HTTP 409 with { "dirty": true } when uncommitted changes block deletion.
  • Force flag: Adding force=1 to the query string triggers removeWorktree() to append --force to the Git command (line 19).
  • Safe defaults: The system defaults to safe removal and requires explicit user confirmation to discard changes.

Frequently Asked Questions

What happens when Pi Web tries to delete a dirty worktree?

When removeWorktree() in lib/worktree.ts executes git worktree remove on a directory containing uncommitted changes, Git exits with an error. The API route catches this error, detects the "dirty" keyword in the message, and returns HTTP 409 Conflict with a JSON body indicating dirty: true.

How does the force removal parameter work in Pi Web?

The client appends force=1 as a query parameter to the DELETE request. The API route passes this boolean to removeWorktree(), which adds the --force flag to the Git command at line 19. This forces Git to delete the worktree and discard any uncommitted or untracked files without manual cleanup.

Where is the dirty worktree detection logic located?

The detection spans two files: lib/worktree.ts handles Git error extraction via extractGitError() (lines 26‑30), while app/api/worktrees/route.ts interprets these errors and translates them into the 409 HTTP response that signals a dirty state to the client.

What Git command does Pi Web use to remove worktrees?

For standard removal, Pi Web executes git worktree remove <path>. When force removal is requested via the API, it executes git worktree remove --force <path> to override dirty worktree protection.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →