Skip to content
zit zit 0.2.0
Login

zit 0.2.0

zit 0.2.0 is a capability release. It closes the 0.1.x line, which grew from 38 dispatched verbs to 61, gained the sub-manifest dialect, and grew a layered derived-state cache — and it says so, which the 0.1.x notes largely did not.

Why the minor bump, and why this document is long

0.1.4's notes covered Git interchange, sparse operation, and release portability well. They mention sub-manifests once and the word cache not at all, in a line that by then had already shipped a delta-manifest dialect, a zero-parse image, a rebuild checkpoint, and six other memo tiers.

That is a documentation debt, not a code one: the work was reviewed, tested, and pinned. But a capability nobody can find is close to a capability that does not exist, so 0.2.0 pays the debt down. Much of what follows describes machinery that has been in your repository for several point releases and has never been written up properly. Where that is true, this document says so rather than presenting old work as new.

The version is 0.2.0 rather than 0.1.5 because the accumulated surface — 23 new verbs, an alternate manifest dialect, and a cache hierarchy with its own invariants — is a different product from 0.1.0, and the number should say that.

Highlights

Sub-manifests

A conventional Fossil manifest lists every file in the check-in. For a large tree with small commits, that means each check-in restates a file list that barely changed — the cost of a commit scales with the size of the repository rather than the size of the change.

A sub-manifest stores the check-in as a delta against a baseline manifest, carried in R cards. The dialect is zit's own; it is not something a stock fossil client mints, which is why the whole feature is behind an explicit opt-in.

It is off unless you turn it on. The setting is false on every repository that has not been opted in by an operator act (zit submanifest enable). A repository that never enables it keeps byte-identical check-ins to 0.1.x.

Choosing the baseline interval. The parameter that decides the trade — how many check-ins share a baseline before a new full manifest is minted — is not guessed. zit submanifest analyze replays the repository's own history and reports the k that would have been cheapest for the commits you actually made. It accepts -R over a bare store, so the analysis does not need a working tree.

Commit-time drift advice. With sub-manifests enabled and pinned, a commit that drifts more than 15% from the analyzed optimum prints one advisory line and offers to retune. It never affects whether the commit succeeds — advice, not a gate.

Import. import-fossil chooses deliberately between two readings of the same history: --compatible preserves the original corpus byte-for-byte, so hashes and project code survive a round trip; --submanifest re-mints the history in the dialect, which changes artifact names and is therefore a new repository rather than the same one. import-git mints sub-manifests by default and takes --no-submanifests to produce flat manifests instead.

The cost of reading them. A delta manifest has to be resolved against its baseline before it can be read, so a naive implementation pays that cost per check-in per query. update, verify, and the import path share one sub-manifest memo across every commit in a run instead of rebuilding it, and the memo evicts by recency rather than freezing once it reaches its budget.

The derived-state ladder

The content log is the only authority in a zit repository. Everything else — every index, image, checkpoint, and memo — is derived from it and can be deleted. This section describes what the layers are, because 0.1.x shipped them without naming them.

The rule every layer obeys

A cache that is absent must degrade. A cache that is present must never answer differently from the log.

The failure this rule exists to prevent is the quiet one. A missing cache costs time and nothing else: the query falls back to the log and is correct. A partially correct cache is worse than no cache at all, because it answers confidently and wrongly, and nothing in the output says which layer produced the answer. Every tier below is testable against a full re-derivation, and --no-cache on the derived-state readers bypasses every sidecar and re-derives from the whole log, re-verifying integrity as it goes.

What the tiers are

Memos of derived state

Memos of log structure

Memos of content bytes

Memos of the working tree

What it is worth, measured honestly

On the same repository, one listing costs:

path time
full derive from the log 29.63s
record-body replay 0.34s
the zero-parse image 0.04s

Having a cache is worth about 87×, and the image a further 8.5×.

An earlier revision of the caching write-up reported 641.9s / 58.3s / 0.31s and claimed 188×. Those were Debug-build numbers compared against a release build — the comparison was invalid, and the figures above replace them, measured in the mode that ships. The embedded page at www/caching.html records the correction rather than quietly restating the numbers, because a performance claim that was once wrong is worth being able to check.

What the image does not carry

The image is not a second source of truth. Queries it cannot answer fall through to the rung below, and the arrangement is asserted rather than assumed: the cache tests compare a .fast_read image load against a full derivation and require query-equivalence, including the adjacency and tag surfaces.

The verb surface

0.1.0 dispatched 38 verbs. 0.2.0 dispatches 61. Added across the line:

The command reference (www/reference.html, served on fossil's own /doc/VERSION/PATH route) is pinned by tests in both directions. It had drifted for weeks before those pins existed — grep, purge, config, and trust all shipped undocumented, and nothing noticed, because noticing required a human to compare two lists.

0.2.0 tightens the pin from verbs to flags, holding every verb's real --help against its reference section in both directions: a flag the verb accepts that the section never names, and a flag the section documents that --help never mentions. The second direction matters more than it looks — an omission is discoverable from --help, but a promise sends someone hunting for something that is not there.

That audit immediately found two gaps the section-level pin could not see. pack --drop-elided — the sparse specification's narrow-then-compact deletion, destructive and irreversible — appeared in no help output at all, so nothing had anything to compare it against. It is now documented, including the part that matters most: unlike shun it leaves no tombstone, so a later widening re-fetches the content from a peer. settings --global was accepted but absent from the reference.

Merge

zit merge -X ours|theirs resolves every content conflict in favour of one side. A region only one side changed is still that side's under either strategy, so -X is not "discard their work" — it is "stop asking me". It deliberately does not resolve modify-vs-delete: that is a disagreement about whether a file should exist, not about its contents, and either reading silently destroys work. Git declines it too.

-X ours is not --backout, and the help says so outright: --backout reverses NAME's change; -X ours keeps your side where the two collide and reverses nothing.

zit precedent (alias zit rerere) remembers how you resolved a conflict and replays it when the identical conflict recurs. A merge records each conflict; the commit that resolves it attaches the resolution; a later merge whose conflicted bytes match uses it instead of writing markers.

The two compose without either knowing about the other: precedent is a pass over the merge outcome set, so a strategy that leaves no conflict leaves it nothing to replay or record. Verified in both directions.

Fixes

The stash sidecar could become an artifact. .zit-stash was absent from the control-path registry, so extras listed it and add accepted it. An ordinary zit add . followed by a commit put the contents of stashed work into a check-in and synced it to every peer — recoverable only by shunning. The stash help had promised "never an artifact" since the feature shipped; nothing enforced it. It is now a reserved authoritative name: invisible to extras, refused by add, and pinned by a test.

zit export-git --help printed a line from another verb's help. A verb-list entry had been pasted into its usage block, backslash and all. The new pin checks shape rather than text — no rendered help line may begin with a backslash — so the whole class is caught for every verb, including verbs added later.

Compatibility

Qualification

Each published binary is built from the same checksum-pinned source and qualified before publication:

The release manifest records the artifact checksums, the source identity, the effective target, and the qualification performed for each.