Artifact b0ce1eb99dd94a0a7037d4809fa6223e439bf06a690355cd32b2c85808561f37:
- File
internal/manifest/reparent.go
— part of check-in
[245a0ed306]
at
2026-10-20 02:08:49
on branch feat/submanifest-reader
— conformance: re-pin the three drafts to their -00 names
zit check-in 68a3f76cb7 renames the artifact-format, repo-state and sync-protocol drafts from -01 to -00 in their headers, cross-references and reference entries, and retitles each Changes appendix "Changes in This Revision". No normative text changed and no section number moved.
The pinned copies take zit's file names, the requirement tables pin the new hashes, and the comments, tests and docs that cited the -01 drafts cite the -00 ones. The refresh script copies every draft under its own name. The sparse-cone copy is re-pinned to d497c2432f, whose text is unchanged. Coverage counts are identical before and after. (user: [email protected], size: 4137) [annotate] [blame] [check-ins using] [more...]
package manifest import ( "database/sql" "fmt" "strings" "github.com/danmestas/go-libfossil/db" "github.com/danmestas/go-libfossil/internal/content" "github.com/danmestas/go-libfossil/internal/deck" libfossil "github.com/danmestas/go-libfossil/internal/fsltype" ) // ReparentCheckins applies every singleton `parent` tag by rewriting the // tagged check-in's ancestry to the parents the tag names. It is fossil's // manifest_crosslink_end reparent pass (src/manifest.c // manifest_reparent_checkin): the check-in's plink and mlink rows are deleted // and re-derived against the tag's parents instead of its own P-card. // // It runs after the candidate sweep and before tag propagation, as fossil // does, so the propagation walk and the batch's leaf rechecks see the // rewritten plink. Like fossil's, the rewrite schedules no leaf recheck of its // own (draft-fossil-repo-state-00 Section 7.3): an old parent left without a // child is rechecked only when a branch write in the batch reaches it, and an // explicit rebuild (leaf.Rebuild) always reaches it. A parent tag whose value // is not a well-formed list of artifact hashes is ignored, matching fossil's // reparent_abort. // // Each repair round scans tagxref for parent tags and rewrites only the tagged // check-ins, so the pass is O(reparented), not O(repository); a round with no // parent tag does one indexed query and returns. This matches fossil's // manifest_crosslink_end, which the receive path's repair gate runs once per // round rather than once per repository. func ReparentCheckins(tx *db.Tx) error { if tx == nil { panic("manifest.ReparentCheckins: tx must not be nil") } type reparent struct { rid libfossil.FslID value string } rows, err := tx.Query(`SELECT tagxref.rid, tagxref.value FROM tagxref JOIN tag USING(tagid) WHERE tag.tagname='parent' AND tagxref.tagtype=1`) if err != nil { return fmt.Errorf("manifest.ReparentCheckins query: %w", err) } var todo []reparent for rows.Next() { var r reparent var value sql.NullString if err := rows.Scan(&r.rid, &value); err != nil { rows.Close() return fmt.Errorf("manifest.ReparentCheckins scan: %w", err) } r.value = value.String todo = append(todo, r) } rows.Close() if err := rows.Err(); err != nil { return fmt.Errorf("manifest.ReparentCheckins rows: %w", err) } if len(todo) == 0 { return nil // the common case: no reparent, no cache to build } cache := content.NewCache(crosslinkCacheBytes) for _, r := range todo { if err := reparentCheckin(tx, cache, r.rid, r.value); err != nil { return err } } return nil } // reparentCheckin rewrites one check-in's plink and mlink to the parents named // in value, a space-separated list of artifact hashes. The rows are derived by // the same builders a normal crosslink uses, over a deck whose P-card is the // override: fossil reuses manifest_add_checkin_linkages the same way. func reparentCheckin( tx *db.Tx, cache *content.Cache, rid libfossil.FslID, value string, ) error { parents := strings.Fields(value) if len(parents) == 0 { return nil } for _, p := range parents { if !isHash(p) { return nil // malformed value: fossil leaves the plink rows untouched } } data, err := expandManifestBytes(tx, cache, rid) if err != nil { return nil // unreadable check-in: nothing to rewrite } d, err := deck.Parse(data) if err != nil || d.Type != deck.Checkin { return nil } reparented := *d reparented.P = parents if _, err := tx.Exec("DELETE FROM plink WHERE cid=?", rid); err != nil { return fmt.Errorf("manifest.reparentCheckin plink delete rid=%d: %w", rid, err) } if _, err := tx.Exec("DELETE FROM mlink WHERE mid=?", rid); err != nil { return fmt.Errorf("manifest.reparentCheckin mlink delete rid=%d: %w", rid, err) } if err := insertCheckinPlinks(tx, rid, &reparented, checkinBaseid(tx, d)); err != nil { return fmt.Errorf("manifest.reparentCheckin plink rid=%d: %w", rid, err) } if err := insertCheckinMlinks(tx, cache, rid, &reparented); err != nil { return fmt.Errorf("manifest.reparentCheckin mlink rid=%d: %w", rid, err) } return nil }