# zit 0.9.0

zit 0.9.0 is about stores that live a long time and grow large: a forge's
bare store written by an application, a horizon or sparse clone kept in sync
for months, a history thinned to save space. It adds three verbs an
application can drive without a checkout, makes commits and syncs cost what
changed instead of what the tree holds, and closes several ways a clone or
a pair of concurrent commits could lose or hide work.

This release is from trunk. The caches beside a store change format and are
rebuilt once on first open; the store itself and the wire stay Fossil's.

## New

- **`commit -R STORE --stdin PATH -m MSG`** records one file's bytes from
  standard input as a check-in, with no checkout. It holds the store's lock
  throughout, so concurrent writers land in order. Unchanged bytes exit 3
  and print the parent. `--parent`, `--branch`, `--user`, `--allow-fork` and
  `--no-checks` apply as they do to commit.
- **`log CHECKIN PATH -F json`** and **`diff -F json`** report a file's history
  and a two-version diff in Fossil's field names, so an application reads
  them without parsing text.
- **`log PATH` and `annotate` follow renames**, continuing under the old name
  below the check-in that recorded one; `--no-follow` gives the
  name-literal view.
- **`purge artifacts HASH...`** thins a file's history: it removes named file
  versions, keeps every check-in, and stays recoverable with `purge undo`
  until `purge obliterate`. A version any open leaf or the checkout holds is
  refused. A local `.zit-thinned` list keeps pull and serve from bringing
  removed versions back. It is never sent to peers.
- **Exit 8** means content absent by policy: `cat`, `diff` and `annotate` of a
  version that was purged, shunned, or left out by a cone or horizon. `diff`
  used to show such a version as an emptied file, and `annotate` credited a
  removed version's lines to the next one.
- **`zit serve --read-only`** serves clones and pulls and refuses every push.
- **`zit init --submanifest K`** starts a store with sub-manifests on and k
  pinned, so a tree that will grow wide is a tree from its first check-in.
- **`--cone-parents`** gives a sparse cone the parents property: the root's
  files and the files directly in each parent directory of a member are
  covered too, so a narrow cone is navigable. The sparse-cone draft states
  the rule and its `cone-parents` pragma.

## Faster

- A flat manifest is stored as a delta of its skip base, the ancestor at its
  height with the lowest set bit cleared, so no chain is deeper than
  popcount(height) and no commit stores a whole manifest. This is
  Subversion's skip-delta scheme, by Greg Hudson.
- Sync, pull and a server taking a push store a check-in received whole as a
  delta against its parent. A sender answers with the delta it already
  stores when the peer holds its basis, in Fossil's delta form. A pull of a
  few one-file check-ins now moves kilobytes.
- `repo-cksum` is off unless turned on, so a commit no longer reads every
  blob in the tree for the `R` card. Fossil writes the card by default;
  `zit settings repo-cksum on` restores that.
- `revert` with no paths finds what changed first, as `status` does, and
  restores only that.
- A horizon clone's no-op sync resumes from its saved classification
  instead of classifying the whole store again.
- A large clone checks received deltas by walking each delta tree once
  instead of rebuilding every artifact through its whole chain.
- `crosslink --lines` no longer flattens each parent's whole tree. On the
  Linux kernel store its first run covers 1.77 times as many check-ins in
  the same time.
- The cache carries a checksum of its image alone and is read through a
  mapping, so opening a very large store checks what it reads instead of
  copying the whole cache onto the heap. File reads and writes move at most
  1 GiB per call, so on macOS a cache past 2 GiB is reused instead of
  re-derived.
- `import-git --into` opens the destination through its saved scan index
  and checks mapped check-ins by lookup.

## Fixed

- Two commits in one checkout could both succeed and fork the branch. A
  commit now refuses to publish when another moved its checkout while it
  was being prepared.
- A horizon clone's sync could stop uploading check-ins committed after its
  first sync. Its cursor now advances only by what it received.
- A horizon name tied on time resolves by ancestry before hash, as `update`
  does, so a branch committed within one clock tick resolves to its tip.
- A client reports a busy peer only for the 503 a zit server sends for a
  held lock; any other 503 gets the plain status line.
- A pushed sub-manifest setting needs Setup as well as Admin, and a card
  refused for want of it refuses only itself.

## Compatibility

The store and the wire are Fossil's. A stock `fossil clone` of a 0.9.0 store,
thinned or not, succeeds, and the delta form zit now sends is the one Fossil
already reads. Two things differ from 0.8.1 on disk: a new check-in has no
`R` card unless `repo-cksum` is on, and stored manifests are skip deltas,
which every reader expands as before.

The caches beside a store (`.zit-cache`) change format. The first open by
0.9.0 rebuilds them once, then they are reused. On a small store that takes
moments. On a store the size of the Linux kernel's history it took 43
minutes and 4.1 GB of memory, so a forge should open its large stores once
before serving traffic from them.

The sparse-cone, horizon and sub-manifest drafts gain the rules above.

## Qualification

The release manifest records native qualification against the exact tagged
source archive on macOS ARM64, Linux x86-64 and Linux ARM64, using the pinned
Zig 0.17.0 toolchain. Publication is gated on unit and end-to-end tests, Git
differentials, deterministic simulation, seeded fuzzing, and Fossil
interoperability checks on every distributed target.

For the basis of the conformance claim, see
[the 0.1.0 release notes](RELEASE-NOTES-0.1.0.md).
