Artifact 6f1b843449d819617f040fe6d57d0a6849dac715e161d2339c1187c9d50b12da:
- File
internal/deck/deck.go
— part of check-in
[f51becc770]
at
2026-10-17 20:49:24
on branch feat/submanifest-reader
— feat(manifest): read sub-manifest trees (draft-fossil-submanifests-00)
Parse the S card and the SUBMANIFEST artifact type, and read tree manifests through one enumeration seam.
deck: the S card (exact arity, decoded filename, raw 40/64-hex hash, strictly ascending run between R and T), type Submanifest reached only by reassigning a D-less manifest, its F S Z legality row, and the 4.4 checks: no B with S, no F with S in a manifest, F xor S in a sub-manifest.
manifest: checkinFileCards, ownFileCards and lookupFileCard are the one place a check-in's file set is assembled (9.3). A tree manifest is walked depth first with an explicit stack, a depth bound of 16, the repeated-artifact check and the least-filename and ordering checks of 5.1; lookup binary-searches one sub-manifest per level (5.3); a delta may name a tree as its baseline (5.4). A tree that cannot be walked is an error, never a partial file set (5.2). mlink derivation, R-card verification and the delta-manifest builder read through the seam, and a commit on a tree parent is written flat.
Crosslink defers a check-in whose tree cannot be walked, recording the blocking sub-manifest in orphan so the phantom-fill cascade links it on arrival (7.3, 9.4). Storing an S-bearing artifact reserves phantoms for its absent references with the referrer's privacy (7.1) and sets the seen-submanifest indicator (8.1). A sub-manifest is filed as examined so the sweep never reselects it (9.2). The examined-blob table is versioned, so verdicts a parser without S cards reached are dropped. (user: [email protected], size: 3639) [annotate] [blame] [check-ins using]
package deck import "time" type ArtifactType int const ( Checkin ArtifactType = iota Wiki Ticket Event Cluster ForumPost Attachment Control // Submanifest is draft-fossil-submanifests-00's type 9: a slice of a // check-in's file set, as F cards (a leaf) or S cards (an interior node). // No card assigns it; Parse reassigns a D-less manifest to it (§4.3). Submanifest ) type Deck struct { Type ArtifactType A *AttachmentCard B string C string D time.Time E *EventCard F []FileCard G string H string I string J []TicketField K string L string M []string N string P []string Q []CherryPick R string // S is a tree manifest's or an interior sub-manifest's references, in // strictly ascending order of decoded filename // (draft-fossil-submanifests-00 §3.6). S []SubmanifestCard T []TagCard // U tracks the check-in user with three distinct states, mirroring // fossil's src/manifest.c:1008-1016 U-card handling: // - nil: no U-card in the manifest at all (SQL NULL at crosslink time) // - non-nil, "anonymous": U-card present but empty (resolved at parse // time, matching canonical fossil's own substitution) // - non-nil, non-empty: U-card present with a login name // Deliberately *string rather than string: a bare "" cannot represent // "absent" and "present-but-empty" at once, which previously collapsed // both into the same stored value. See User for constructing literals. U *string W []byte Z string // cPresent, nPresent and pPresent record that a C, N or P card was present // in the parsed manifest even when its value is empty, so Marshal can // re-emit a present-but-empty C/N comment or a bare P card and the artifact // round-trips. They are unexported because they matter only to the parse -> // marshal round trip: a caller building a Deck sets a non-empty value, which // Marshal still emits on its own, and the W card uses nil versus a non-nil // empty slice for the same distinction. See §4.2 (empty trailing token) and // §4.7.13. cPresent bool nPresent bool pPresent bool } type FileCard struct { Name string UUID string Perm string OldName string } // SubmanifestCard is one S card: the decoded least filename of the slice the // referenced sub-manifest carries, and that sub-manifest's raw hash. type SubmanifestCard struct { Name string UUID string } type TagCard struct { Type TagType Name string UUID string Value string // valuePresent records that a value token was present even when empty, so a // parsed `T +x * ` round-trips with its trailing space. Unexported for the // same reason as Deck.cPresent: Marshal emits a non-empty Value regardless. valuePresent bool } type TagType byte const ( TagSingleton TagType = '+' TagPropagating TagType = '*' TagCancel TagType = '-' ) type CherryPick struct { IsBackout bool Target string Baseline string } type AttachmentCard struct { Filename string Target string Source string } type EventCard struct { Date time.Time UUID string } type TicketField struct { Name string Value string } // User returns a pointer to s for constructing Deck.U. Go forbids taking // the address of a string literal directly, so composite literals go // through this instead of Deck{U: &s}. Deliberately *string rather than a // named struct type: passing *string straight to database/sql lets a nil // argument bind SQL NULL and a non-nil one deref to its value automatically // (see Deck.U), so crosslink/rebuild call sites need no special-casing. func User(s string) *string { return &s }