# zit 0.3.17

zit 0.3.17 fixes an authenticated push that named no URL, which silently went
out anonymous, and makes the file-history index on a submanifest repository
cost what changed rather than what exists.

No artifact, tag namespace, sync message, or on-disk repository format changed.
The index format is unchanged from 0.3.16.

## `push --user` with no URL pushed anonymously

`zit push --user U --password P`, relying on the saved remote rather than
naming a URL, discarded the credential before the push was built. The argument
parser answered "no URL" with a result that carried no other fields, and the
push verb rebuilt its arguments from that as a bare empty URL. The push then
went out with no login card at all, and a peer that required one refused the
write. With an explicit URL the same push authenticated and landed.

This is why 0.3.16's saved-login reuse looked blocked against fossil: the push
that would have saved the login never sent it. The read path had proved the
credential correct all along — a credentialed `config pull user` returned the
user table the same peer serves to administrators only — and the refusal was
zit dropping the card on the floor, not fossil rejecting it.

The parser now mirrors `sync`, which never had the defect: an omitted URL is an
empty one, resolved to the saved remote at run time, and the credential comes
through. A parser test pins it, and the S12 interop scenario commits into a
clone of a real fossil peer whose anonymous user cannot write and asserts that
an authenticated push naming no URL, and a following push naming no user at
all, both land on that peer's timeline.

## The tree-manifest index build is proportional to change

0.3.16 diffed two tree check-ins by flattening both to complete file lists.
That made the index work on a submanifest store, but at files-per-check-in per
check-in: 34 minutes and a gigabyte on a pkgsrc mirror, once per store.

A tree carries its file set entirely by S cards, each naming a sub-manifest by
hash, and a check-in shares almost every sub-manifest with its parent. The
diff now walks the two S-card runs side by side and skips any sub-manifest
whose hash did not move — neither side is even resolved. Only where the hash
differs are both sides read, their files diffed, and the walk recursed. A
sub-manifest present on one side only is enumerated whole. Deletes are held
until the whole diff has run, so a partition whose key moved between the two
trees cannot make a file the child still holds look deleted.

| pkgsrc submanifest mirror, 218,444 check-ins | 0.3.16 | 0.3.17 |
|---|---|---|
| index build, once | 34.3 min | **6.8 min** |
| sub-manifests read per check-in | every one (68,588 files flattened, twice) | about 4 |
| a warm PATH query | 1.09s | 1.06s |
| answers | identical to reconstruction | identical to reconstruction |

The same history at libspatialite's size built in 2.1s against 4.0s. Every
answer was checked per path against the flat twin and against reconstruction
with the index disabled.

Trees are no longer flattened eagerly when the diff memo parses them. The root
tree and a flat/tree pairing — a first tree after `submanifest enable`, or a
delta whose baseline names a tree — still flatten, on demand, because those
are full enumerations by nature. The derive feed, which every cold open runs,
never flattens a tree at all; it reports one as unknown, as it did before
0.3.16. A version of 0.3.16 could flatten a tree parent there for a flat child;
that no longer happens.

## Compatibility

- No on-disk repository or wire format changed; Fossil RFC revision 00 remains
  the target.
- `.zit-cache/finfo` keeps format version 3. An index built by 0.3.16 is
  adopted as it stands; only a rebuilt one takes the cheaper path.
- `zit push` with no URL and no saved remote reports "no remote", as `sync`
  does; before, it reported no URL. No other command or flag changed.
  `ZIT_FILEINDEX_WALK` now also prints `fileindex-subs`, a diagnostic seam.

## 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. Timings are release builds; the
pkgsrc figures are from the submanifest mirror in the benchmark corpus with the
index removed and rebuilt, and the 0.3.16 figures from the preceding binary on
the same store.
