zit 0.3.1 is a point release with one change in it: cloning a large history
is no longer slow. On the pkgsrc corpus — 305,369 check-ins over trees of
about 100,000 files — zit clone of a flat store did not finish; it was
killed after 3h52m. It now completes in under 20 minutes, and in zit's own
sub-manifest format it lands within 14% of fossil clone of the same
history while producing a store a third the size.
Nothing else changed. No artifact, tag namespace, or sync message differs from 0.3.0.
The change
A clone transfers artifacts and then derives the local caches, and the derive records, for every check-in, which files it changed against its parent. It did that by materializing the check-in's complete effective file list and its parent's, then diffing the two — work proportional to the size of the tree, paid once per check-in. On pkgsrc that is 100,000 entries twice over, 305,369 times, with every path and hash heap-allocated.
Fossil does not do this. Its add_mlink merges the two check-ins' own
changed-file runs and seeks the baseline they share, which is work
proportional to the change, not to the tree. zit now does the same:
- Two delta manifests over one baseline diff over their own F cards, with a binary search into the baseline for a name only one side mentions.
- Otherwise — a root check-in, a baseline change, a full manifest on either side — the two effective lists are walked side by side and compared as they are yielded. Neither list is ever materialized.
- Recently read manifests stay parsed in a bounded cache (Fossil keeps six warm; this keeps 64), and an entry a capture step is using is pinned so it cannot be evicted under the step holding it.
The derived records are byte-identical to what the previous code produced:
the .zit-cache files a clone writes compare equal before and after, and
zit timeline -v over pkgsrc agrees with fossil timeline -v on every
check-in and every file.
Measured
All figures on one machine, quiet, with fossil clone and zit clone
interleaved so both see the same conditions. Fossil 2.28.
| corpus | fossil clone |
zit clone |
|
|---|---|---|---|
| sqlite (~30k check-ins) | 96s | 93s | parity |
| tcl (~25k check-ins) | 391s | 93s | zit 4.2x faster |
| pkgsrc, sub-manifest store | 531s | 603s | 1.14x |
| pkgsrc, flat store | 531s | 1,063s | 2.0x |
Before this change the pkgsrc flat clone was killed at 13,963s without
finishing, against 531s for fossil clone — better than 26x slower.
Two things the numbers say that the headline does not:
The remaining gap is the compatibility format, not zit. A flat store
(import-fossil --compatible) preserves Fossil's artifacts verbatim, and
those manifests are large full-text documents: 1,816 bytes per stored
delta, 1.73GB for pkgsrc. The same history as sub-manifests is 186 bytes
per delta and 663MB, and clones in 603s. Packing the flat store does not
close this — zit pack on it costs 974s, improves the chains by 6.5%, and
changes clone time by nothing measurable. The flat form cannot be packed
into the sub-manifest form's shape; that is what conversion is for.
Timings on a loaded machine are worthless. Repeated runs of the same binary over the same input varied by 29% while other work shared the box. Every figure above was taken with the machine otherwise idle, and each was run twice.
Compatibility
- Artifacts, tags, and the sync protocol are unchanged; Fossil RFC
revision 00 remains the target. A stock
fossilclones, pushes, and pulls a 0.3.1 repository byte-for-byte. - No on-disk record changed. A 0.3.0 store, checkout record, undo journal, and cache are read unchanged, and a 0.3.0 binary reads a 0.3.1 store.
- No command, flag, or output changed.
Qualification
zig build test, e2e, smoke (Fossil interop against a real fossil),
muscle (41 differential steps), git-import-e2e, git-export-e2e, and
dst, plus the three-runner CI battery (macOS ARM64, Linux x86-64, Linux
ARM64). Three tests are new: the delta-level diff against a hand-built
baseline, its agreement with the walk-based diff, and a delta chain longer
than the manifest cache, which exercises the eviction path a small fixture
never reaches.