Artifact 865e6b9d654467ffc3dde75fec402a60afc56e0deef98fdbc70bfde2573571a5:
- File
conformance/repostate_gaps_test.go
— part of check-in
[67fc6ced4b]
at
2026-10-13 11:59:57
on branch main
— fix(manifest): link check-ins with absent files; refuse absent baselines (#415)
* fix(manifest): link check-ins with absent files; refuse absent baselines
The crosslink sweep held a check-in back until every file it names was stored. A root check-in, or one on a present parent, whose files never arrived got no event, plink, mlink, leaf or tag rows. fossil links it against phantom files (add_one_mlink, uuid_to_rid(..., 1)), and repo-state-01 Section 4.2 makes an unresolved reference a phantom, not an error. The sweep -- Crosslink, the close of every sync and clone, and verify.Rebuild -- now waits only on a delta manifest's baseline. The receive-time cascade and receive linker still wait for files while blobs are still arriving; the closing sweep bounds that wait.
verify.Rebuild linked a delta manifest whose baseline was absent, with no baseline id. fossil refuses it (fetch_baseline) and files it in orphan under a phantom for the baseline; artifact-format-01 Section 8.3 calls this a soft failure retried when the baseline arrives. Every path now does the same, so a sync requests the baseline and the phantom-fill cascade links the manifest when it lands. Rebuild clears and rederives orphan.
The gap case repostate.4.2.root-absent-file passes and is unmarked; new cases cover a present parent with an absent file and an absent or late baseline, each also through verify.Rebuild where fossil rebuild agrees.
Tracked in #406.
* fix(manifest): refuse a non-manifest baseline and drop the orphan row on link
Tracked in #406. (user: [email protected], size: 2179) [annotate] [blame] [check-ins using]
package conformance import "testing" // repostateGaps marks the repository-state cases go-libfossil is known to // fail, by case ID, grouped by the code at fault, each with the issue // tracking its fix. var repostateGaps = map[string]string{} // repostateDeviations marks the cases where go-libfossil follows fossil(1) // rather than the draft text, by case ID. var repostateDeviations = map[string]string{ "repostate.7.3.parentless-untagged": parentlessDeviation, // verify.Rebuild now derives leaves through the crosslink sweep, so it shares // the same branch-aware leaf rule and the same ยง7.3 deviation. "repostate.7.3.parentless-untagged.rebuild": parentlessDeviation, // The same check-in seen from Section 7.3's crosslink trigger: linking it // does not recheck it, as fossil's does not. "repostate.7.3.recheck-lone-untagged": parentlessDeviation, // An untagged child of a tagged check-in: Section 7.3 rechecks it and // its parent when it is linked. "repostate.7.3.recheck-untagged-child": untaggedChildDeviation, "repostate.7.3.recheck-untagged-child.rebuild": untaggedChildDeviation, } const untaggedChildDeviation = "follows fossil(1): a check-in with a parent but no branch " + "tag is never rechecked, so it is no leaf and its parent keeps its verdict, where " + "Section 7.3 rechecks every crosslinked check-in and its parents; widening the " + "candidates changed the leaf table of a real fossil-authored repository (the " + "libfossil corpus)" const parentlessDeviation = "follows fossil(1): an untagged parentless check-in is " + "never a leaf, where Section 7.3 makes every check-in without a same-branch " + "child one (the section's informative note records fossil's behavior)" // TestRepoStateGapKeys checks that every marked case exists. func TestRepoStateGapKeys(t *testing.T) { ids := map[string]bool{} for _, c := range repostateSuiteCases() { ids[c.ID] = true } for id := range repostateGaps { if !ids[id] { t.Errorf("repostateGaps names %s, which is not a case", id) } } for id := range repostateDeviations { if !ids[id] { t.Errorf("repostateDeviations names %s, which is not a case", id) } } }