Skip to content

Conversation

@ThomasK33
Copy link
Member

This changes workspace deletion for local worktree workspaces to also delete the associated local git branch.

  • Normal delete uses git branch -d (safe; only deletes if merged)
  • Force delete uses git branch -D
  • Adds guardrails to avoid deleting protected branches (e.g. main) or branches still checked out elsewhere

UI copy is updated (desktop + mobile) so users understand that deleting a workspace also deletes the branch.


📋 Implementation Plan

Plan: Delete workspace also deletes its git branch

Summary (what will change)

  • Archive remains non-destructive (metadata timestamp only).
  • Delete workspace will remove:
    1. the git worktree directory (existing)
    2. the associated local git branch by default (new)

Behavior details (per decisions captured):

  • Safe by default: normal delete uses git branch -d <branch> (only deletes if git considers it merged).
  • Force delete uses git branch -D <branch> (matches existing force: true semantics).
  • No remote deletion: we will not delete origin/<branch>.
  • SSH runtimes: keep simplest safe behavior (directory removal only; no explicit remote branch deletion).

Net LoC estimate (prod code): ~80–140 LoC (mostly WorktreeRuntime + UI copy).

Current behavior (confirmed in repo)

  • Delete call flow:
    • WorkspaceContext.removeWorkspace()api.workspace.remove()workspaceService.remove()runtime.deleteWorkspace()
  • WorktreeRuntime.deleteWorkspace() already does best-effort branch deletion, but only when the workspace name starts with agent_.
  • SSHRuntime.deleteWorkspace() deletes the remote directory only (no explicit git branch -d/-D).

Recommended approach

1) Backend: generalize branch deletion in WorktreeRuntime.deleteWorkspace

Files:

  • src/node/runtime/WorktreeRuntime.ts
  • (optional helper) src/node/git.ts

Steps:

  1. Change the branch-deletion gate

    • Today:
      • shouldDeleteBranch = !isInPlace && workspaceName.startsWith("agent_")
    • Proposed:
      • shouldDeleteBranch = !isInPlace (delete branch for all worktree workspaces, not just agent_*).
  2. Add safety checks before deleting the branch (defensive, and errs on “skip deletion” rather than “delete the wrong thing”):

    • If branch does not exist locally (await listLocalBranches(projectPath)): skip.
    • If branch is the repo’s detected trunk (detectDefaultTrunkBranch(projectPath)) or the current branch in the main worktree (getCurrentBranch(projectPath)): skip.
    • If branch is still checked out by another worktree (parse git -C <projectPath> worktree list --porcelain and see if any remaining worktree reports branch refs/heads/<name>): skip.
  3. Deletion command

    • Keep existing deleteFlag behavior:
      • force=falsegit branch -d "<branch>"
      • force=truegit branch -D "<branch>"
  4. Keep deletion best-effort

    • Continue treating branch cleanup as non-blocking (never fail workspace delete just because git branch -d failed).
    • Upgrade logging so it’s clear whether we skipped due to guardrails vs git failure.

2) UI: update delete UX copy to reflect branch deletion

Goal: make it obvious that “Delete” now also targets the branch.

Files / call sites to update:

  • Desktop command palette: src/browser/utils/commands/sources.ts (confirm("Remove current workspace…"))
  • Archived view:
    • src/browser/components/ArchivedWorkspaces.tsx tooltip/copy around “Delete permanently”
    • src/browser/components/ForceDeleteModal.tsx warning text (“Force delete” should mention branch deletion too)
  • Mobile: mobile/src/screens/ProjectsScreen.tsx delete confirmation + force delete wording

Copy guidance:

  • Mention local branch by name where feasible.
  • Mention that normal delete is “safe” (may not delete branch if unmerged) and that Force Delete is destructive.

3) Tests

  1. Unit tests (src/node/runtime/WorktreeRuntime.test.ts)

    • Add coverage for a non-agent_ branch:
      • Force delete (force=true) removes the branch.
    • Add coverage for safe delete (force=false) on a merged branch:
      • Create branch → commit → merge into main → delete workspace → branch removed with -d.
    • Add a guardrail test:
      • Workspace on main can be deleted as a worktree, but main branch must not be deleted.
  2. Integration test (optional but valuable)

    • Extend tests/ipc/removeWorkspace.test.ts for local runtime:
      • Create workspace branch, then remove with { options: { force: true } }, assert git branch --list <branch> is empty in the main repo.
Alternatives considered (not recommended for this iteration)
  • Add a “keep branch” checkbox / option to workspace.remove

    • Pros: avoids deleting user-provided existing branches.
    • Cons: requires API/schema changes, UI affordances across desktop + mobile, and new persistence semantics.
  • Delete only “Mux-created” branches (e.g. track whether branch existed at creation time)

    • Pros: safest.
    • Cons: needs new metadata field + backfill semantics for existing workspaces.

Given the explicit desire for “delete means delete” now that archiving exists, the simplest consistent behavior is: delete branch by default, with strong guardrails and clear UI copy.


Generated with mux • Model: openai:gpt-5.2 • Thinking: xhigh

Change-Id: I4fb0c7518f6ffb62c264a85bae86a36ef77a9fbf
Signed-off-by: Thomas Kosiewski <tk@coder.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant