Skip to content

v0.2.0 — wt-snapshot: capture a worktree before anyone deletes it

Choose a tag to compare

@shojikumaru shojikumaru released this 21 Aug 04:17
· 89 commits to main since this release
v0.2.0

v0.2.0 — wt-snapshot: capture a worktree before anyone deletes it

The weekly cleanup report kept finding worktree remnants because "may I delete
this?" had no safe answer. bin/wt-snapshot gives it one: it captures a dirty or
not-reconstructible worktree into a commit object under
refs/worktree-snapshots// in the main repository, so
deletion becomes reversible. The tool never deletes anything itself.

  • captures tracked dirty changes, untracked files, and the configured ignored
    path set (e.g. .omc/) through a temporary index; the user's HEAD, index and
    config are never touched and identity is passed explicitly per git call
  • a clean worktree at a reachable HEAD is an explicit no-op, so noise is never
    snapshotted
  • CK_WTSNAP_SECRET_SCAN_CMD, when set, sees every captured byte before anything
    is written; a hit aborts with the scanner's status and leaves refs and the
    object store untouched
  • restore rebuilds the worktree including the ignored set; prune lists expired
    refs and deletes nothing

Evidence for the two properties that matter most, measured on this head:
extended-attribute content cannot smuggle bytes past the scanner (payload holds
0 LIBARCHIVE.xattr records and 0 plain hits for a probe secret placed in an
xattr), and a staged submodule pointer is captured rather than reported as a
no-op (restored index gitlink oid matches the source exactly). A plain-file
secret aborts with exit 17 leaving loose-object count unchanged at 3 and no ref
written. Payload verification is pinned from both directions: an injected
unlisted member aborts with exit 70, and neutralising the member-type
comparison turns exactly one case red.

Suite on this head: 29 passed in tests/test_wt_snapshot.sh, all seven suites
green. Review: 4 rounds, 3 seats each round (Grok 4.6, Kimi K3, Muse Spark 1.2)
— two MAJORs found and closed in rounds 3 and 4, final round GO / GO-WITH-MINOR
/ GO.