Skip to content
zit Artifact [87e2992c4c]
Login

Artifact [87e2992c4c]

Artifact 87e2992c4cb346cbb069794ac4cd39b85baf125ba87c9a381a9d43f4a9a52b50:


# zit 0.3.1

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 (zit#172)

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 `fossil` clones, 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.