Skip to content
zit Artifact [c7d8ee1cdd]
Login

Artifact [c7d8ee1cdd]

Artifact c7d8ee1cddaf73902944200deafec2981dbba9f96119424d9a91b3768891cfc4:


Fast clones for CI and agents

A CI job or an agent needs one check-in's files, a build, and maybe a commit and a push. It rarely reads old history. A clone of a reasonably sized repository for that job should take about a second.

zit gets there with a horizon: the clone holds history from one check-in forward and knows what lies below it by name. Add a path cone and it holds only the part of the tree the job touches. Every artifact it holds is the origin's, under the same name, so the job opens, builds, commits and pushes as it would in a dense clone.

The default: a structural horizon at the commit being built

zit clone "$URL" work --horizon="$REV" --structural --no-open
cd work
zit open "$REV"

$REV is the full hash of the check-in being built (64 hex digits for SHA3, 40 for SHA-1); a prefix is refused. Your CI trigger usually has it. --structural leaves out the check-in manifests below $REV, and the origin sends a seed carrying the branch and tag state at $REV, so branches and tags above it still derive exactly. The default rule, descendants, holds $REV and every check-in descended from it.

When the job touches one area, add a cone, and name it again at open:

zit clone "$URL" work --horizon="$REV" --structural --cone=src --no-open
cd work
zit open "$REV" --cone=src

--cone repeats, takes files or directories, and pairs with --cone-not=PATH and --cone-file=FILE. A structural or cone clone at the tip moves only the tip's tree plus the structure from $REV forward.

The clone's last line says what the origin served:

cloned 9 artifacts into work in 2 rounds (horizon clone; origin served: structural; nothing elided locally)

--no-open and zit open "$REV" put the tree at the check-in being built. A plain clone opens the tip of the main branch instead, which is right only when that is what you build.

When to use a content horizon

Drop --structural:

zit clone "$URL" work --horizon="$REV" --no-open

A content horizon keeps every check-in manifest and control artifact of every age, and file content only from $REV forward. Use it when:

When to use a dense clone

zit clone "$URL" work

Use a dense clone when the job mirrors to Git (zit export-git refuses a horizon store), when other clones will be taken from this one, when it bisects or annotates across old history, or when the repository is small enough that the horizon saves nothing.

Deepening when a job needs history

Nothing below the horizon is fetched until something asks for it.

zit pull and zit sync carry the horizon on every request. Pass no flag.

What fails loudly

Each failure exits 1 (2 for a usage error) and says what to do.

You see Do
--horizon needs a full artifact hash pass the full hash
--horizon-rule and --structural need --horizon=HASH add --horizon
--horizon names …, which the origin does not hold as a check-in check $REV; nothing was cloned
this store holds history from its horizon … forward (from cat, diff, update) zit update FULLHASH to deepen, or re-clone with an earlier --horizon
content for '…' is elided by this store's horizon the same
a branch below it has no name here (from merge) re-clone with --horizon-rule=leaves
a Git mirror needs all of it (from export-git) export from a dense clone
sparse origin does not cover the requested clone cone clone from the origin, not from a horizon or cone clone

To change the cone, clone again with the new --cone; a fresh horizon clone is cheap. zit verify counts elided artifacts on their own line, never as faults:

sparse store: cone of 1 member and horizon at 9cd75f917cfbb709438fc28b633caaf8c88a6b4725c10442b5519c1bb1567edf (descendants); 4 artifacts deliberately elided
verified: 11 frames, 9 artifacts, 1832 bytes, 0 faults

Releases older than 0.7.0 refuse a horizon store rather than read it as dense.

Pushing from a horizon clone

A check-in made in a horizon clone is an ordinary check-in. Its manifest names its parent and its files by real hash, and nothing in it reveals the horizon, so a stock Fossil server stores it byte for byte.

Commit with --no-sync and push on its own line. Autosync failures only warn, and a CI job wants the push to fail the step:

zit commit --no-sync -m "CI: stamp $REV"
ZIT_PASSWORD="$CI_PASSWORD" zit push --user ci

If a check-in starts a branch and the clone's seed was stale, the origin refuses it with stale seed: … re-sync, then 'zit amend --branch …' and push again. Do that; the same push then lands.

Keeping the origin warm

To serve a horizon or cone clone, the origin classifies the request's cone once and keeps the classification, a cone index and a structural graph beside the store as .zit-cache sidecars. The first request for a cone pays for that work; later ones answer from it. A warm origin answers in about a second for a cone of a large tree.

So run one long-lived zit serve (or a forge in front of one) rather than spawning a server per request, and leave .zit-cache in place. Deleting it costs time, never data. The 0.7.0 release notes have the measurements.

A CI job

set -eu
# URL and REV come from the CI trigger: the origin and the full hash to build.
zit clone "$URL" work --horizon="$REV" --structural --cone=src --no-open
cd work
zit open "$REV" --cone=src
./ci/build.sh                       # your build hook
zit add src/generated.txt           # only if the job commits
zit commit --no-sync -m "CI: build $REV"
ZIT_PASSWORD="$CI_PASSWORD" zit push --user ci

Leave out --cone lines for a job that needs the whole tree, and --structural against a stock Fossil origin. See the command reference for every flag.