Skip to content

fix: refuse to delete the worktree the user is standing in - #11

Merged
tnagatomi merged 5 commits into
mainfrom
fix/current-worktree-deletion
Jun 18, 2026
Merged

fix: refuse to delete the worktree the user is standing in#11
tnagatomi merged 5 commits into
mainfrom
fix/current-worktree-deletion

Conversation

@tnagatomi

@tnagatomi tnagatomi commented Jun 18, 2026

Copy link
Copy Markdown
Owner

Problem

Deleting the worktree the user is currently standing in produced a cryptic exit status 128:

Fix

Two layers of defense:

TUI — make the current worktree non-selectable. The safety model already treats IsCurrent as NotDeletable; the TUI now matches that by refusing BadgeCurrent in isSelectable, mirroring the existing BadgePrimary refusal. A new [current] badge (suppressed when the primary is also current, to avoid [primary] [current] noise) marks the row and dims it like the primary so the refusal is self-explanatory.

Deleter — refuse to remove the CWD worktree. deleter.Delete is a public DeleteFunc that does not track the current worktree, so a non-TUI caller (or a future one) could still hit the same 128. The guard now captures the symlink-resolved CWD once and refuses any target that is the CWD or an ancestor of it (a subdir CWD vanishes with the worktree too), returning a distinct errCurrentWorktree OpRemove failure. This is defense-in-depth for the TUI change.

Out of scope

The TUI failure banner still says only N operation(s) failed without per-target reasons. The current-worktree path no longer reaches it, so it's left for a separate issue.

Introduces the badge symbol the loader and TUI will use to mark and
refuse the worktree the user is standing in, mirroring BadgePrimary.
The loader now emits BadgeCurrent on the linked worktree the user is
standing in, so the TUI can both show it and refuse to select it. The
primary is suppressed (isMain) since it is already non-deletable and
shows [primary]; a redundant [current] would only add noise.
The current worktree was selectable (only the primary was refused) even
though the safety model already marks it NotDeletable, so a user could
select it, delete it, and trigger a cryptic reload 128 once their CWD
vanished. isSelectable now refuses BadgeCurrent too, and the row is
dimmed like the primary with [current] shown so the refusal is
self-explanatory. The bulk safe-select key is unaffected since it
follows Deletable, which is already false for the current worktree.
Delete is a public DeleteFunc that does not track the current worktree,
so a caller that handed in the worktree the process is standing in would
remove the process's own CWD: os.RemoveAll succeeds, but the post-delete
reload then runs git -C <gone-path> and exits 128, surfacing as a
cryptic failure even though the deletion itself worked. The guard now
captures the resolved CWD once and refuses any target that is the CWD or
an ancestor of it (a subdir CWD vanishes with the worktree too), with a
distinct errCurrentWorktree failure. This is defense-in-depth for the
TUI non-selectability landed in the previous chunk.
Notes the new [current] badge in the badge enumeration and states that
the primary and current worktrees are not selectable, matching the
behavior landed in the preceding chunks.
@tnagatomi
tnagatomi merged commit 0dc4e21 into main Jun 18, 2026
4 checks passed
@tnagatomi
tnagatomi deleted the fix/current-worktree-deletion branch June 18, 2026 14:07
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