Skip to content
zit Timeline
Login

Timeline

Parents and children of check-in [281c0ed815]

2026-07-18
23:08
status: report edited files by hashing tracked files against the current check-in

Completes the B-status-16 deferral. `status` previously classified only `added` and `missing`: a tracked file that was present but modified produced no line at all, and the gap was documented in a comment, in `--help`, and in an e2e test that pinned the silence. Documenting a hole is not the same as not having one, and "your working copy looks clean" is the wrong thing for a version control system to say about an edited file.

What changed:

- `status` now resolves the current check-in and compares every tracked file against it, emitting an `edited` row naming the path when the bytes or the file mode differ. A file whose contents match is still not listed.

- The comparison is not a second implementation. `diff` already resolved the current check-in, tolerated the legacy trailing-separator F-card, walked the delta manifest, and merged that effective file set against the sorted managed set. That machinery is extracted into `CheckinBaseline` (open/resolve) and `walkWorkingTree` (the merge), and both verbs now run through it. `diff` is a pure refactor here -- same output, less code. The `diff*Fail` diagnostic mappers become `compare*Fail` and take the verb as a parameter, the shape `checkoutFail` and `managedFail` already used.

- A verb supplies only a sink with `emit(entry)`. `DiffSink` renders diff records; `StatusSink` renders human rows and drops the deleted-from-check-in class, which `status` still defers because it iterates the managed set. The sink is comptime-generic rather than an erased pointer pair: both sinks are known at compile time, so the call stays direct and type-checked.

- The whole report is buffered and written once, as `diff` already did. A comparison failure part-way through the walk now leaves stdout empty rather than a header above a truncated report. Dirtiness is still never a failure: every `added`/`edited`/`missing` row exits 0 (B-status-15). Only a genuine comparison failure -- unreadable tracked file, corrupt or unresolvable check-in -- exits 1.

- `CheckinBaseline` is self-referential (the walk borrows the resolver, which borrows the log, which reads through the file storage), so it is initialized in place and must not be moved. The open/deinit contract used to rest purely on each call site placing its `defer` correctly; an `opened` flag now makes that invariant assertable, and both `deinit` and the failure path assert it.

- `walkWorkingTree` carries a termination guard against the exact bound (one step per managed path, one per effective file), plus a per-iteration progress assertion, because correctness of the merge rests on every switch arm advancing exactly one cursor. Debug-only, alongside the existing debug-only assertion that the managed set is sorted.

- `--help` no longer defers edited-file detection. It documents the three row classes and narrows the deferral text to the two cases that genuinely remain out of scope: a file deleted from the check-in but no longer tracked, and untracked "extra" files -- pointing at `zit diff` and `zit extras`.

Test evidence: new e2e coverage pins each acceptance criterion -- an edited tracked file is reported as edited in the born state; a rewrite with identical bytes (mtime bump, same content) is not reported; an executable-bit-only change is reported as edited; a file deleted from the check-in but untracked is still not reported; and a current delta checkout with an absent B-card baseline fails cleanly rather than reporting a false clean tree. The pre-existing e2e that pinned the deferral is replaced by the tests above. The help-text e2es compare against a hand-written expected string, so the `--help` change is pinned too. `zig build` and `zig build test` pass in this worktree after absorbing the sibling add/rm recursion commit (281c0ed815), and the commit is proved against a clean `fossil open` checkout running `zig build` then `zig build test`. check-in: 02d993f860 user: dmestas tags: release-0.1

22:52
Merge release-0.1: Implement add/rm directory recursion through the shipped extras.sweep walk

Lands release step P3-F4-addrm on trunk. `zit add DIR` and `zit rm DIR` were hard errors directing the user to name individual files, which made `zit add .` and adding a source subtree dead ends. The increment makes both verbs recurse: `add DIR` expands over the already-shipped `extras.sweep` of the checkout root rather than introducing a second walker, so the exclusions, the no-follow symlink posture, and the tracked-set subtraction are the ones `addremove` and `clean` already use, with the sweep lazy and memoized so multiple directory arguments still perform one enumeration. `rm DIR` expands over the tracked keys via `ManagedSet.iterate` rather than over the disk, so an already-deleted directory remains forgettable. A single containment predicate, `pathUnderDir`, serves both verbs so the prefix-boundary case cannot diverge between them. Help text and exit-status wording now describe recursion instead of the removed directory error.

Merge evidence: `fossil merge release-0.1` reported no conflicts, updating only src/e2e.zig and src/main.zig; the tree carries no conflict markers (the open-marker occurrences that a grep turns up in src/merge3.zig and src/e2e.zig are that module's own conflict sentinel constant and the tests asserting on it, not merge residue). The merged tree — not merely the branch — was re-verified green on trunk after the merge: `zig fmt --check .`, `zig build`, and `zig build test` all exit 0. `fossil extras` is empty, so the merge adds no file it fails to track.

No borrowed techniques in this change, so no attribution is owed. check-in: fca1e073c2 user: dmestas tags: trunk

22:45
Implement add/rm directory recursion through the shipped extras.sweep walk

`zit add DIR` and `zit rm DIR` were hard errors that told the user to name individual files. That made the two most common invocations -- `zit add .` and adding a source subtree -- dead ends, and it was the last place where the CLI asked the user to do work the repository already knows how to do.

What changed:

- `add DIR` now expands to every untracked file beneath the directory. It reuses the ALREADY SHIPPED `extras.sweep` of the checkout root rather than introducing a second walker: the same walk, the same exclusions, the same no-follow symlink posture `addremove` and `clean` use. The sweep is lazy and memoized for the whole invocation, so `zit add a b c` over three directories still performs exactly one enumeration. Because the sweep already subtracts the tracked set and the depth-0 control names, neither can be re-inserted or re-reported by the directory path. - `rm DIR` expands over the TRACKED keys via `ManagedSet.iterate`, never over the disk, so a directory the user has already deleted is still forgettable. Keys are copied into the arena before the first removal, because `iterate()`'s slice and its backing bytes are invalidated by `remove` -- the same discipline `addremove` uses for its remove set. Ascending order is preserved and every expanded key still passes the per-key control-file guard (B-rm-7). - One containment predicate, `pathUnderDir`, serves both verbs, so the boundary case cannot diverge between them: `src2/c` is not under `src`, because containment requires the separator immediately after the directory's bytes. - No new diagnostic class. `zit rm .` with nothing tracked resolves to the existing not-tracked message rather than inventing an empty-expansion error; `zit add .` on a tree holding only control files adds nothing and succeeds. - The `add` and `rm` help text and exit-status wording now describe recursion instead of the removed directory error.

Test evidence: the B-add-5 and B-rm-5 contract tests were rewritten to pin the new behavior (a named symlink is still rejected; a directory now recurses; `.` at the root is never reported as outside the repository), and a new P3-F4 block of end-to-end tests covers nested trees, prefix-boundary siblings, multiple directory arguments sharing one sweep, --dry-run, --hard, and rm of a directory that no longer exists on disk. `zig build test` passes in this worktree after absorbing the sibling packaging commit (3645c03e2d), and the commit is proved against a clean `fossil open` checkout running `zig build` then `zig build test`. check-in: 281c0ed815 user: dmestas tags: release-0.1

20:52
Narrow .paths to docs/rfcs so working docs never ship in a package

`.paths` named the whole `docs` directory. A Zig package is cut from the working tree, not from what fossil tracks, so the ignore-glob does not govern it: a bare `docs` entry ships whatever sits under docs/ at package time — including the ignore-glob'd docs/plans/*.md working documents, which are internal and must never leave the repository.

Naming docs/rfcs directly is also what the repo convention already states (0ad7fa92c6: 'docs/ is for the RFCs only'); the manifest now encodes that convention instead of relying on docs/ happening to stay clean. A new pin in src/packaging_test.zig asserts it so the bare-docs form cannot drift back. check-in: 3645c03e2d user: dmestas tags: release-0.1