pub fn is_git_oid(s: &str) -> Result<(), String>Expand description
Predicate: assert that s is a valid Git commit OID — the canonical
shape the typed :fonte (:tipo git …) :rev axis carries. The
reproducibility contract :rev carries vs. :tag / :branch
(CAIXA-SDLC §V — Substrate; :tag resolves to whatever the upstream
has tagged today, :branch to whatever the upstream’s HEAD points at
today, :rev to exactly one immutable commit forever — same shape
Unison’s content-addressed code identity gives terms by
construction: the hash is the address, the address never moves):
- exactly
GIT_OID_SHA1_LEN(40, SHA-1) orGIT_OID_SHA256_LEN(64, SHA-256) characters — the two canonical Git hash-algorithm widths; anything in between is an abbreviated prefix (the canonicalgit log --short/git rev-parse --short HEADpaste-from-release-notes footgun), which is ambiguous across repository history and surfaces at clone time as anambiguous argumenterror far from the source caixa.lisp; - every byte in
[0-9a-f](lowercase ASCII hex) —git rev-parseandgit show --format=%Hboth emit lowercase exclusively, so an uppercase-bearing:revround-trips inconsistently across the resolver’sgit fetch <remote> <:rev>↔git rev-parse HEADequality-check pipeline and fails the lacre’s content-addressing equality probe with a confusing case-only diff; - no whitespace, no control bytes, no non-ASCII, no refname
punctuation (
~ ^ : ? * [ \), no/separators — every character outside[0-9a-f]is rejected on the same predicate arm, so a:rev "main"(the canonical “I conflated:revand:branch” footgun) lands at the same gate as a:rev "v0.1.0"(:tagmis-slot) or a:rev "c0ffee:scratch"(refname-shape leak); the typed:revslot’s valid set intersects the:tag/:branchslot’s valid set at exactly the empty set, structurally — every refname is rejected here, every OID is rejected byis_git_ref_name. - not the all-zero null-OID sentinel (
"0000…0000"— 40 zeros at SHA-1 width, 64 zeros at SHA-256 width). Git reserves this value as the “no commit” sentinel ingit update-ref/ pre-receive hook flows (<old-value>for create,<new-value>for delete) and no commit in any object database has this OID, so a:rev "0000…0000"is structurally impossible to resolve. The canonical “I copy-pasted the sentinel out ofgit update-ref --stdindocs / pre-receive hook example” footgun would otherwise pass every other shape arm (canonical length, lowercase hex) and surface atgit fetch <remote> 0000…0000time with a quoting-confused “couldn’t find remote ref” error far from the source caixa.lisp, with the lacre’s content- address locked to agit:0000…0000closure that never equals any upstream’s actualHEAD. Mirrorsis_git_ref_name’s canonical-OID-shape pre-emption arm (line 1322) — both predicates carry one self-aware arm that catches values structurally valid for the alphabet but operationally meaningless on the typed axis.
Returns the parser-shaped reason on rejection (without wrapping in
any error variant) so each per-axis caller — crate::DepError::FontePinShape
at validate time on the :fonte :rev axis, the future per-pin gate
on lacre.lisp resolved-rev axes, the future M4 per-dep CR
materializer’s per-pin validator — wraps the same reason in its own
typed *Invalid { axis, reason } variant. The reason wording is
axis-agnostic (“git commit OIDs are lowercase hex ([0-9a-f])”) so
every call site reading the same diagnostic points at the same rule.
Empty input is rejected here (defensively) and at each call site via
the narrower crate::DepError::FontePinEmpty variant — the same
empty-first cascade is_dns_1123_label, is_gateway_api_http_path,
is_wit_world_ref, is_nats_subject, is_wasi_keyvalue_slot,
and is_git_ref_name all carry.
Sibling of is_git_ref_name: the two predicates together bracket
the :fonte pin axes — refname-shaped (:tag / :branch) vs.
hex-OID-shaped (:rev) — so an authored value lands in exactly one
of the two valid sets, and a cross-axis mis-slot (:rev "main" /
:tag "deadbeef…") is a build error at the offending axis’s
predicate, not a clone-time surprise.
§Errors
Returns the parser-shaped reason naming the specific violation
(length / character-class), without wrapping in any error variant —
every caller maps the same String into its own typed
*Invalid { axis, reason } enum variant.