Skip to content

fix: retrospective review fixes for merged PR #107#119

Merged
ikeikeikeike merged 1 commit into
mainfrom
fix/retro-review-pr107
Jul 16, 2026
Merged

fix: retrospective review fixes for merged PR #107#119
ikeikeikeike merged 1 commit into
mainfrom
fix/retro-review-pr107

Conversation

@ikeikeikeike

Copy link
Copy Markdown
Member

Retrospective /code-review sweep of merged PR #107 (fix: recreate a prunable worktree instead of no-op'ing on create), which shipped without pre-merge review.

What

PR #107 fixed AddOrAttach no-op'ing on a worktree whose directory was deleted out-of-band while git's admin record survived ("prunable"). Its own fixup commit a3415349 narrowed recovery to fire only when os.Stat(dst) returns ErrNotExist, specifically to avoid misclassifying a transient EACCES/stale-NFS stat error on a present worktree as prunable.

Why this PR

That narrowing only detects the "whole directory is gone" shape of prunability. git worktree list --porcelain also emits a prunable <reason> line when the directory survives on disk but its own .git file (or git's admin-side gitdir record) was removed independently — e.g. a partial teardown that clears .git before the rest of the tree, or a cleanup pass that strips dotfiles. Verified against real git 2.53.0:

$ git worktree list --porcelain
worktree /path/to/wt
HEAD 833400b...
branch refs/heads/featB
prunable gitdir file points to non-existent location

os.Stat(dst) reports no error at all in this shape (the directory is right there), so the ENOENT-only guard added in a3415349 silently returns the old no-op — reintroducing a variant of the exact half-materialised-worktree bug PR #107 set out to fix, just triggered by a different out-of-band deletion pattern than a full rm -rf.

Fix

  • internal/gitwt/gitwt.go: add a Prunable bool field to Worktree, populated by parsing the prunable porcelain line that List() was previously discarding entirely.
  • AddOrAttach now gates recovery on wt.Prunable || errors.Is(statErr, os.ErrNotExist) instead of the ENOENT check alone — trusting git's own authoritative verdict (which already covers every prunable reason, not just full deletion) while still falling back to the ENOENT check for git versions/entries where the annotation is absent. The a3415349 EACCES/NFS protection is preserved unchanged: a stat failure that isn't ENOENT, on an entry git does not flag prunable, still keeps the old no-op.

Test plan

  • go build ./...
  • go test ./...
  • golangci-lint run ./...
  • New test TestRunner_AddOrAttach_RecreatesPrunableWorktree_DirSurvivesGitLinkGone exercises this against a real git worktree: removes only the .git link (not the whole dir), confirms git flags the entry prunable while the dir still exists on disk, then confirms AddOrAttach recovers it. Confirmed the test fails against the pre-fix ENOENT-only guard (verified by temporarily reverting the guard locally).

PR #107's fixup (a341534) narrowed prunable-worktree recovery in
AddOrAttach to fire only on os.Stat(dst) returning ErrNotExist, to avoid
misclassifying a transient EACCES/NFS stat error on a present worktree
as prunable. That narrowing over-corrected: it only detects the "whole
directory is gone" shape of prunability, but `git worktree list
--porcelain` also flags an entry "prunable" when the directory survives
while its own `.git` link (or git's admin-side gitdir record) was
removed independently — e.g. a partial teardown that clears `.git`
before the rest of the tree, or a cleanup pass that strips dotfiles.
Verified against real git (2.53.0): `git worktree list --porcelain`
reports "prunable gitdir file points to non-existent location" for this
exact shape, while os.Stat(dst) reports no error at all, so the
ENOENT-only guard silently no-ops on it — reintroducing a variant of the
half-materialised-worktree bug PR #107 set out to fix.

Add a Prunable field to Worktree, populated by parsing the `prunable`
porcelain line List() was previously discarding, and gate recovery on
`wt.Prunable || ENOENT` instead of ENOENT alone. This delegates the
"is this worktree actually usable" judgment to git itself (which
already computes it correctly) instead of re-deriving an incomplete
proxy via a single stat call, while still falling back to the ENOENT
check for git versions/entries where the annotation is absent — the
EACCES/NFS protection the fixup added is preserved unchanged.

New test TestRunner_AddOrAttach_RecreatesPrunableWorktree_DirSurvivesGitLinkGone
exercises this against a real git worktree: removes only the `.git`
link (not the whole dir), confirms git flags it prunable while the dir
still exists on disk, then confirms AddOrAttach recovers it. Confirmed
the test fails against the pre-fix ENOENT-only guard.
@ikeikeikeike
ikeikeikeike merged commit 5310dd4 into main Jul 16, 2026
11 checks passed
@ikeikeikeike
ikeikeikeike deleted the fix/retro-review-pr107 branch July 16, 2026 08:43
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