Skip to content
zit zit 0.3.14
Login

zit 0.3.14

zit 0.3.14 makes a file's history cheap to ask for. zit log CHECKIN PATH reconstructed a check-in manifest for every check-in it walked, so one file's history on a mirror of SQLite took over two minutes and the cost grew with how much unrelated history sat behind it. It now answers from an index the derive was already computing and discarding.

No artifact, tag namespace, sync message, or on-disk format changed. The index is a .zit-cache member: advisory, rebuilt from the log if deleted, and answering nothing the log cannot.

log PATH and timeline -p (zit#188)

Two defects, fixed in that order.

The predicate asked twice. It computed a check-in's effective content for PATH once as the check-in and once as the primary parent of the check-in below it, and each call re-read, re-verified and re-parsed the manifest because fileAt took neither the walk's plaintext memo nor a baseline memo. Threading both — and the answer itself, memoised by rid — took reconstructions per walk from 2N-1 to exactly N. timeline -p PATH shared the defect and is fixed with it.

The predicate should not have been reconstructing at all. zit already computes each check-in's change set against its primary parent during the derive (ChangedFile is Fossil's mlink row) and streams it to the crosslink feed without keeping it. That fact, inverted by path, is now persisted as .zit-cache/finfo, keyed to the log tail and the shun fingerprint exactly like the cluster and disposition maps. The PATH walk traverses the derived adjacency — as the unfiltered walk has since #98 — and the index narrows it.

log --long -n 50, SQLite mirror (131,558 artifacts) before after
README.md 72.20s 1.58s
src/btree.c 24.88s 1.11s
manifest reconstructions (README.md) 25,106 141

Output is byte-identical, checked per path rather than inferred.

The index narrows; it does not decide. captureFileFacts diffs at F-card level, so a delta manifest that restates a path with the hash it already had reads as a change there while the path's effective content did not move — one check-in in 2,113 on btree.c's full history, an over-report and never an omission. Every candidate the index names is therefore still confirmed by the change predicate, which is what keeps the answer exact. ZIT_NO_FILE_INDEX=1 answers by reconstruction alone; an end-to-end test compares the two for every tracked path so a regression cannot silently drop a file's history.

Building the index is a one-time pass over the check-ins (about 164s and 882 KB on the SQLite mirror) and happens on the first PATH query.

What this does not fix. About 0.7s of each figure above is opening the repository, which is unchanged here and is now tracked on its own. A warm process pays it once; a single CLI invocation pays it every time.

zig build cross

A new gate step compiles the CLI for x86_64-linux and aarch64-linux and runs nothing. This project links no libc and macOS links libc unconditionally, so a call that demands libc builds on a developer's mac, passes every local lane, and refuses on both Linux runners. That happened during this release's development. gate depends on the new step; CI does not run it, because CI compiles the real thing on all three targets it ships for.

Compatibility

Qualification

zig build test, e2e, smoke (Fossil interop against a real fossil), muscle (41 differential steps), git-import-e2e, git-export-e2e, dst, and cross, plus the three-runner CI battery. The timings above are release builds against the flat SQLite mirror; the before figures come from the preceding binary run on the same store.