Skip to content
zit Artifact [ea9b9da987]
Login

Artifact [ea9b9da987]

Artifact ea9b9da987cc01424679e9f3f871541dbc5aadddb8b6f5c9021a15f695ae17e6:


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 tip

zit clone "$URL" work --horizon=tip --structural

This is the recipe for an agent that wants the latest work and no history. tip is the newest check-in; a branch or tag name works the same way, and --depth=N starts the horizon N check-ins further back along first parents. The origin resolves the name and answers the hash, and the clone records only that hash, so later pulls stay pinned to it. The clone prints which hash the name resolved to.

CI: 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). Your CI trigger usually has it; pass it rather than a name, so the clone is the commit being built even if the branch moved. --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-rule, --depth and --structural need --horizon=HASH or --horizon=NAME add --horizon
horizon name not found: … check the branch or tag name (a hash prefix is not a name); nothing was cloned
--horizon names …, which the origin does not hold as a check-in check $REV; nothing was cloned
the origin did not resolve --horizon=…; resolving it here from the transfer nothing to do: the origin is stock Fossil or an older zit, which served everything, and the clone resolved the name itself
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 or hash here (from merge) re-clone with --horizon-rule=leaves or an earlier --horizon
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.