feat(git): ensure_worktree and verify_worktree for per-work-item worktrees (swamp-club #1908, #1892) #338

Merged
stack72 merged 3 commits from 1908 into main 2026-09-28 21:46:53 +00:00
Owner

Closes swamp-club #1908 and #1892.

What

  • ensure_worktree — idempotently prepares one worktree and branch per work item:
    • fetches an exact base (full SHA, refs/heads/<x>, refs/tags/<x>, or a bare name meaning refs/heads/<name> — never a suffix match) into a private temp ref with --no-write-fetch-head --refmap= --no-tags, so concurrent calls against one repository never contend on FETCH_HEAD or the remote-tracking ref;
    • creates, attaches (existing free branch, or a registered worktree whose directory was deleted), or reuses the worktree untouched, so in-progress rework survives;
    • refuses paths outside an optional root, the primary checkout, the git directory, anything overlapping another worktree, a branch checked out elsewhere, unrelated history, detached HEADs, and in-progress rebase/merge/cherry-pick/revert;
    • baseCommit is the merge-base of HEAD and the fetched base (stable across rework after the base moves); expectedBaseCommit fails closed before anything is created; requireCleanPrimary ignores registered worktrees nested in the primary.
  • verify_worktree — read-only (--no-optional-locks, no fetch, no ref writes) fail-closed check against branch, base lineage, root, git-dir linkage, and remote URL, reporting every mismatch at once.
  • remove_worktree fix — compares symlink-resolved paths, so a path through a symlink (macOS /tmp) is removed instead of reported already absent.
  • README, manifest, version 2026.09.28.1 with upgrade entry.

Testing

255 tests pass, including real-git tests against a local bare origin (no network): every create/reuse/attach/refusal path, base-ref forms, relative and symlinked paths, paths with spaces, an 8-way concurrency test, verify_worktree read-only and multi-mismatch checks, and the remove_worktree symlink regression. The concurrency design was checked against real git first: without --refmap=, 11 of 12 parallel fetches failed on the remote-tracking ref lock; with it, 0 of 48.

Verification: verify-build and verify-reviews pass on the head commit; attestation posted.

Known limitations

  • A raw SHA base needs the server to allow fetching it (GitHub and Forgejo allow reachable SHAs; some self-hosted servers don't). It fails with git's error.
  • Resource names sanitise the branch like ensure_checkout does, so wi/1 and wi-1 share a data name.
  • Needs git 2.31+.

🤖 Generated with Claude Code

Closes swamp-club #1908 and #1892. ## What - **`ensure_worktree`** — idempotently prepares one worktree and branch per work item: - fetches an exact base (full SHA, `refs/heads/<x>`, `refs/tags/<x>`, or a bare name meaning `refs/heads/<name>` — never a suffix match) into a private temp ref with `--no-write-fetch-head --refmap= --no-tags`, so concurrent calls against one repository never contend on `FETCH_HEAD` or the remote-tracking ref; - creates, attaches (existing free branch, or a registered worktree whose directory was deleted), or reuses the worktree untouched, so in-progress rework survives; - refuses paths outside an optional `root`, the primary checkout, the git directory, anything overlapping another worktree, a branch checked out elsewhere, unrelated history, detached HEADs, and in-progress rebase/merge/cherry-pick/revert; - `baseCommit` is the merge-base of HEAD and the fetched base (stable across rework after the base moves); `expectedBaseCommit` fails closed before anything is created; `requireCleanPrimary` ignores registered worktrees nested in the primary. - **`verify_worktree`** — read-only (`--no-optional-locks`, no fetch, no ref writes) fail-closed check against branch, base lineage, root, git-dir linkage, and remote URL, reporting every mismatch at once. - **`remove_worktree` fix** — compares symlink-resolved paths, so a path through a symlink (macOS `/tmp`) is removed instead of reported already absent. - README, manifest, version `2026.09.28.1` with upgrade entry. ## Testing 255 tests pass, including real-git tests against a local bare origin (no network): every create/reuse/attach/refusal path, base-ref forms, relative and symlinked paths, paths with spaces, an 8-way concurrency test, verify_worktree read-only and multi-mismatch checks, and the remove_worktree symlink regression. The concurrency design was checked against real git first: without `--refmap=`, 11 of 12 parallel fetches failed on the remote-tracking ref lock; with it, 0 of 48. Verification: verify-build and verify-reviews pass on the head commit; attestation posted. ## Known limitations - A raw SHA `base` needs the server to allow fetching it (GitHub and Forgejo allow reachable SHAs; some self-hosted servers don't). It fails with git's error. - Resource names sanitise the branch like `ensure_checkout` does, so `wi/1` and `wi-1` share a data name. - Needs git 2.31+. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
ensure_worktree fetches an exact base (full SHA, refs/heads/, refs/tags/, or a
bare name meaning refs/heads/<name>) into a private temp ref with
--no-write-fetch-head, --refmap= and --no-tags, so concurrent calls against one
repository never contend on FETCH_HEAD or the remote-tracking ref. It then
creates, attaches, or reuses one worktree and branch per work item, refusing
paths outside an optional root, the primary checkout, the git directory, and
anything overlapping another worktree. A reused worktree is left untouched so
in-progress rework survives. baseCommit is the merge-base of HEAD and the
fetched base, and expectedBaseCommit fails closed when it differs.

verify_worktree is a read-only check of an existing worktree against branch,
base lineage, root, git-directory linkage, and remote URL, reporting every
mismatch at once.

remove_worktree now compares symlink-resolved paths, so a path through a
symlink (macOS /tmp) is removed instead of reported already absent.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
requireCleanPrimary read plain porcelain output, where git C-quotes a path
with a space, so a nested worktree at such a path made the primary look
permanently dirty. Parse NUL-terminated output instead.

The temp-ref cleanup in fetchBase reused the call's abort signal, so an
aborted run left the ref behind and a failed cleanup could mask the fetch
error. Run it without the signal and ignore its errors.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
fix(git): refuse ref-shaped branches and '..' paths in worktree methods (swamp-club #1908)
All checks were successful
CI / Review Integrity (pull_request) Successful in 1m10s
CI / Validate Attestation (pull_request) Successful in 1m15s
abb7521e94
branch=refs/heads/x was passed through, so worktree add -b created
refs/heads/refs/heads/x and the checked-out-elsewhere check never matched.
branch must now be a plain branch name; base still takes full refs.

path and root refuse a '..' segment instead of collapsing it before symlink
resolution, which could name a different directory than the kernel would.

merge-base exit 1 still reports unrelated history; any other failure now
surfaces git's error instead of being mislabelled.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
stack72 deleted branch 1908 2026-09-28 21:46:54 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
swamp-club/swamp-extensions!338
No description provided.