Repository navigation
residue v0.0.6
-
Tests (#10):
scripts/recovery-check.shchecks whatremoveleaves when it is stopped right beforegit worktree remove, when the check after the move fails, when the move back fails or the original path is taken, and when a second remover of the same worktree runs after the first (with and without the trash), each fault with a control that removes when nothing is injected. Agitscript first onPATHholds or fails one Git command at a time; residue has no test hook. 8 cases. The test scripts unsetGIT_CONFIG_GLOBALandGIT_CONFIG_SYSTEM, so a caller's global Git config is never read or written.scripts/race-check.shnow stops a run that does not end within 60 s of the answer, with everything it started, and fails it instead of waiting forever. Outside both: a kill duringgit worktree removeitself. -
Tests (#8):
scripts/contract-check.shrunsremovewith no prompt against real Git, one repository per case, and pins its contract: the exit status (0; 3 for a refusal, a skip or an invalid target; 2 for a usage error), oneresidue.remove/v1document on stdout with the per-target result, reason and failing check ids, diagnostics on stderr only, and afterwards the file bytes, the registration, the branch, an unrelated worktree and no leftovers. 17 cases; a setup failure stops it with status 2, never as a product result. CI runs it on macOS, Linux and FreeBSD (as root and as an ordinary user). -
Tests (#9): the first in-crate tests, for the verdict fold in
Report::finish, on synthetic checks only. No checks isunknown; a failed check keeps; an unknown one without a failure isunknown; a failed check outranks an unknown one; stale outranks every check; only passing checks make a candidate. Every set of up to five checks is compared with that contract, order never changes the verdict, and adding a failed or unknown check never yields a candidate. CI runscargo test --lockedon macOS and both Linux runners. -
README: FreeBSD has a prebuilt x86_64 binary on every release since 0.0.5; the README still said it had none.
-
FreeBSD aarch64 (#2): every release from 0.0.6 on gets
residue-vX.Y.Z-aarch64-unknown-freebsd.tar.gznext to the x86_64 archive, attested and listed inSHA256SUMS.packages.ymlbuilds each from the tag's source in a FreeBSD 14 virtual machine of its own architecture (aarch64 is emulated on an x86_64 runner), checks that the toolchain's host triple matches the archive name, and runs--versionand.github/smoke.shthere before packing it. The two builds are independent: one failing keeps the other's archive, and neither holds back the Linux packages. -
NetBSD, OpenBSD, DragonFly and illumos (#2): activity probes. Before, residue built there but every worktree's activity was
unknown, so nothing was ever a candidate. NetBSD, OpenBSD and DragonFly: the base system'spslists the processes, then one sysctl per process asks the kernel for its working directory (kern.proc_args.PID.cwd,kern.proc_cwd.PID,kern.proc.cwd.PID), and on NetBSD and DragonFly for its executable. Decision, notfstat: on NetBSD and OpenBSD it names an open file only by mount point and inode (fstat(1): "Normally the name cannot be determined"), so it cannot say whether a process is inside a worktree. Open files are not read on these three, so a worktree with no use found isunknownthere (see the decision below). illumos:/proc/PID/path/, the working directory, every open descriptor and every mapped object (proc(5)); not/proc/PID/cwd, whose readlink is empty. On all four, a process whose working directory the kernel does not name does not count as inspected, apsline residue cannot read makes activityunknown, and so does a probe that inspected no process at all. JSONactivity.backendissysctlon the three BSDs andprocfson illumos. -
Decision (#2), a probe that cannot see open files never clears a worktree: residue must never remove work that is in use. The NetBSD, OpenBSD and DragonFly probes read each process's working directory (and executable on NetBSD and DragonFly), not its open files, so a process that holds a file open inside a worktree while it works elsewhere was not seen, and the worktree could be a removal candidate. Now a backend that reads less than working directories and open files (
Seen::limits) answersin_usewhen it finds a process inside the worktree andunknownotherwise, nevernot_observed:activity unknown (partial_probe), with the detailsysctl reads working directories only, not open files, so it cannot show that no process is using it(NetBSD and DragonFly:working directories and executables only). The decision is inSnapshot::evidence, the one place every check, re-check and check after the move asks, not a per-platform branch. So on those three systems nothing is a removal candidate, andremove,remove --allandwatchremove nothing there; remove a worktree withgit worktree remove. macOS (lsof), Linux (/proc), FreeBSD (procstat -f) and illumos (/proc/PID/path, which names open files) are unchanged. JSON: the shape is the same;activity.reasongains the valuepartial_probe, andactivity.stateis nevernot_observedon those three. The README documents theactivityobject. CI there: each script skips every case that expects a removal or a prompt, by name with the reason (scripts/partial-probe.sh, gated onuname -s), and checks instead that the clean, idle worktree is kept asunknownfor that reason inscan,explain,remove --yes,removeunder a terminal andwatch, with nothing moved aside or put in the trash. Assertions on the other platforms are unchanged; the in-crate tests pin the decision on synthetic snapshots. -
CI (#2): NetBSD 10, OpenBSD 7.8 and DragonFly 6.4 virtual machines run the build, both smoke tests and the race, contract and recovery checks, and an OmniOS r151054 (illumos) virtual machine runs the build, the smoke test and the contract and recovery checks, each as root and as an ordinary user. illumos skips the watch smoke test, because without inotify or kqueue a running
watchwakes on its five-minute timer, past the test's two-minute limit, and the race check, because illumosscriptcannot run a command. NetBSD and OpenBSD run the race check through.github/script-adapter/script, which turns itsscript -q FILE CMD...into theirscript -cform..github/smoke.shleaves its scratch directory before deleting it, which illumosrmrequires. -
Fix (#7):
watchchose a worktree because its upstream branch was gone, but the removal child did not know why, so an upstream that came back between the scan and the removal did not keep the worktree.remove --only-if-upstream-gonenow carries that reason: the child removes a worktree only when the local refs show its branch's upstream gone, checked at the plan, in the re-check right before the move, and after the move right before the deletion. An upstream that exists, no upstream, a detached HEAD or a failed lookup keeps the worktree, with the reason in JSON and the text output, and exit status 3.watchpasses it for work-landed removals only. Manualremoveand disk-pressure removals are unchanged. residue still never fetches, so a remote change that has not reached the local refs is not seen.--only-if-upstream-gonewith--all, or outsideremove, is a usage error. -
The third read comes after the move aside and the re-measure, which can take seconds on a large worktree. If the upstream is back by then, the worktree goes back in place. Every way the requirement keeps a worktree, including a failed lookup, is
skipped. -
Work landed now needs a remote-tracking upstream. A local upstream (
branch.NAME.remoteset to.) whose branch was deleted made a worktree look landed;--only-if-upstream-gonekeeps it, andwatchno longer picks it.scan --jsonandexplain --jsongainupstream_ref, the upstream's full refname (refs/remotes/origin/fix), andwatchreads it. -
Fix: an upstream counted as gone whenever Git marked it
[gone], which Git also does when the ref exists but does not resolve to a commit (it names a tree, or it is a symbolic ref to a missing ref). It now counts as gone only when its ref is absent; anything else is a lookup error.scan'supstream_gonefollows, sowatchno longer picks such a worktree, and--only-if-upstream-gonekeeps it.