Skip to content
zit Artifact [c2afd7d3b1]
Login

Artifact [c2afd7d3b1]

Artifact c2afd7d3b165afa9d9467929f824475da1e54cb8d6275a2a6aadc23b4b12831d:

Instance of technote [e72f92ca3f] - A tree is still a file history by dmestas on 2026-09-16 08:43:20.
C A\stree\sis\sstill\sa\sfile\shistory
D 2026-09-16T08:43:20.918
E 2026-09-16T08:43:20.918 e72f92ca3f4a23ffeb5308b887e04ea0c1f65ac6
N text/x-markdown
U dmestas
W 3883
# A tree is still a file history

0.3.14 gave `zit log CHECKIN PATH` an index and 0.3.15 made it durable. On a
submanifest repository both did nothing, and the reason is worth telling,
because the index looked like it was working the whole time.

## The shape of the non-bug

A submanifest check-in is a tree: its file set is carried by S cards that point
at sub-manifests, not by F cards on the check-in itself. The change-set capture
that feeds the index diffs two flat file lists, and it refused a tree outright —
"unknown at this layer" — because flattening a tree lives behind a facade the
derive layer did not reach into, and a wrong file list is worse than an honest
"I don't know".

So on a submanifest store the index recorded every check-in as unknown. That is
not a crash and not a wrong answer. The index was built, was valid, was adopted
on the next query, and answered nothing: a PATH query fell back to
reconstructing each check-in, one manifest at a time, exactly as it had before
any index existed. On a pkgsrc mirror of 218,444 tree check-ins that was about
39 minutes, every query, forever, while a decoded index header sat on disk
looking healthy.

The tell was in the header the whole time: 218,048 of 218,444 check-ins listed
as unknown. An index that knows nothing is indistinguishable from a fast one
until you ask it how much it knows.

## The fix is a substitution, not a new layer

The facade already had the seam. `flattenTreeFromLogMemo` takes a bare log
store and a caller-owned memo — it was written for a run of check-ins, because
a tree shares almost every node with its parent's and re-resolving them per
check-in is the cost the memo exists to avoid. The diff step now flattens a
tree through that seam on first sight, and `manifestFilesOf` hands the flattened
set to the same diff machinery, which never learns trees exist. A tree that
cannot be flattened — a sub-manifest absent, shunned, or corrupt — stays
unknown, which is the one case the old refusal was really protecting.

The index format moved to version 3. A version-2 blob built before this change
recorded every tree check-in as unknown and would have been trusted as valid
forever; it is refused and rebuilt once.

## What it costs and what it buys

| pkgsrc submanifest mirror, 218,444 check-ins | before | after |
|---|---|---|
| check-ins the index cannot answer | 218,048 | 0 |
| a warm PATH query | ~39 min, every time | 1.09s |
| the index build | 39 min, and useless | 34 min, once |

The same history minted flat answers a PATH query in about the same second, so
a submanifest store now gets what a flat store gets. Answers are identical to
reconstruction, checked per path.

The build is honest work: it flattens each tree and diffs two complete file
lists, so it costs files-per-check-in, not files-changed. On a wide tree that
is real time spent once. Making it proportional to what actually changed —
diffing the S cards and descending only into the sub-manifests whose hash moved
— is the next step, tracked on its own.

## Two smaller things in the same release

`zit artifact` now renders a tree check-in's S cards. The card printer had a
lane for every card letter except S, so a tree check-in displayed as a manifest
with no files at all while `ls` on the same hash listed thousands. That is also
why, more than once, a tree store looked empty or broken when it was neither.

And a saved login now authenticates a push that names no user. `push --user`
had always saved its derived secret, but autosync pushes with no arguments, and
the credential resolver returned anonymous the instant no user was named — so a
clone whose peer wanted a login pulled automatically and never pushed
automatically. The saved login now stands in, the way fossil reuses a
remembered one, and it never prompts, because there is no one at the keyboard
when autosync runs.

Z 5af3b4e72ea9a7e28b2f0dca63ae5945