Skip to content
go-libfossil Timeline
Login

Timeline

50 descendants and 50 ancestors of f7c86ac519d56a15

2026-10-20
16:02
feat(content): compact sparse file holdings transactionally check-in: 91f8ae67bc user: [email protected] tags: feat/submanifest-reader, main, trunk
16:02
feat(sync): encode and validate sparse cone request pragmas check-in: 5c1fbd5ece user: [email protected] tags: feat/submanifest-reader, main, trunk
16:01
Merge main release fixes while preserving feature ticket coverage check-in: 67c2af8525 user: [email protected] tags: feat/submanifest-reader, main, trunk
15:58
Merge commit '8961b59' into feat/sparse-store check-in: dd6f1125ed user: [email protected] tags: feat/submanifest-reader, main, trunk
14:18
fix(manifest): publish carried file content in public check-ins check-in: 28430bfca2 user: [email protected] tags: feat/submanifest-reader, main, trunk
03:01
Merge remote-tracking branch 'origin/main' into feat/submanifest-reader

# Conflicts: # internal/checkout/vfile.go check-in: 741864259c user: [email protected] tags: feat/submanifest-reader, main, trunk

02:48
fix(checkout): restore the database/sql import the merge dropped check-in: ab7f1cde7b user: [email protected] tags: feat/submanifest-reader, main, trunk
02:45
Merge branch 'feat/submanifest-reader' into feat/sparse-store check-in: e4f56e44a9 user: [email protected] tags: feat/submanifest-reader, main, trunk
02:40
Merge branch 'fix/revert-swap-chain' into feat/submanifest-reader

# Conflicts: # internal/checkout/revert.go check-in: 32155296d0 user: [email protected] tags: feat/submanifest-reader, main, trunk

02:37
Merge branch 'fix/symlink-write-paths' into feat/submanifest-reader check-in: 79679cc3fa user: [email protected] tags: feat/submanifest-reader, main, trunk
02:09
Merge branch 'docs/repin-00' into feat/submanifest-reader check-in: d868c1b189 user: [email protected] tags: feat/submanifest-reader, main, trunk
02:08
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. check-in: 245a0ed306 user: [email protected] tags: feat/submanifest-reader, main, trunk

01:42
test(stash): fossil and go both refuse to merge a repointed symlink check-in: aec658f5a9 user: [email protected] tags: feat/submanifest-reader, main, trunk
01:42
fix(stash): do not merge a changed symlink, as fossil's stash apply does

Where a symlink was stashed or is now on disk, Apply writes the stashed file only when both are links and the link still holds the baseline. Otherwise it leaves the file and its row alone and reports it, as fossil's stash_apply prints "Cannot merge symlink" and counts a conflict. Apply and Pop now return the files they did not merge; Pop still drops the entry, as fossil's does. check-in: 2ee5684bf3 user: [email protected] tags: feat/submanifest-reader, main, trunk

01:40
test(stash): pop over a changed symlink does not merge it (red) check-in: d19a7ecca7 user: [email protected] tags: feat/submanifest-reader, main, trunk
01:36
Merge remote-tracking branch 'origin/main' into feat/submanifest-reader

# Conflicts: # CHANGELOG.md # internal/fsltype/julian_test.go check-in: 63f78ed893 user: [email protected] tags: feat/submanifest-reader, main, trunk

01:30
Merge branch 'docs/drafts-without-errata' into feat/submanifest-reader check-in: 27732942f1 user: [email protected] tags: feat/submanifest-reader, main, trunk
01:30
test: read isLink as an integer under both drivers check-in: 179069251f user: [email protected] tags: feat/submanifest-reader, main, trunk
01:30
Merge branch 'feat/submanifest-reader' into fix/revert-swap-chain check-in: c7a321a35d user: [email protected] tags: feat/submanifest-reader, main, trunk
00:51
test(conformance): drop the erratum files; the draft text is the authority

zit applied every erratum in its draft text and retired docs/rfcs/erratum/ at check-in 62a7d1bb3f. Each draft records its corrections in its own Changes appendix, so the suite stops pinning and reading erratum files: the "# erratum" pin lines, the parser branch that accepted them and conformance/rfcs/erratum/ are removed, and comments that named an erratum file cite the draft section instead.

The artifact-format, repo-state, sync-protocol and delta-format drafts are byte-identical at 62a7d1bb3f. The sub-manifest draft is re-pinned; it differs only in its expiry date, reflow, and the dropped pointer to the erratum files. The sparse-cone draft is re-pinned to the 19 October 2026 revision on zit's horizon-rfc branch (0acb5e185c, its merge of 62a7d1bb3f): section 3.6 is removed and section 3.4 states the literal prefix rule once, so SC-3.6-1 becomes SC-3.4-3 (a metacharacter is a literal character, never expanded, and the member may be refused), covered by the same case, renamed sparsecone.3.4.metachar. The refresh script copies the -01 texts to their -01 names and takes the horizon-rfc drafts from a second checkout. check-in: 3e1f791160 user: [email protected] tags: feat/submanifest-reader, main, trunk

00:25
docs(changelog): sparse store classification and verification check-in: 242b5f3d9e user: [email protected] tags: feat/submanifest-reader, main, trunk
00:25
test(manifest,verify): unit tests for commit and verify over a sparse store check-in: c935561cfc user: [email protected] tags: feat/submanifest-reader, main, trunk
00:25
test(conformance): zit checks the sparse store

zit clones go-libfossil's flat history through a cone, and both sides must report the same number of elided phantoms over the same cone. Each side then commits over its sparse store and pushes to a dense store of the other. The commit carries no R card, and the receiver holds the whole check-in. check-in: 0a63c3b22f user: [email protected] tags: feat/submanifest-reader, main, trunk

00:25
test(conformance): one sparse-store fixture for every origin check-in: a76ce676f6 user: [email protected] tags: feat/submanifest-reader, main, trunk
00:25
test(conformance): cover the sparse store's step-3 requirements

Cases elide out-of-cone blobs from a dense clone, as a cone-scoped clone would leave the store. They check the three absence classes, the client and server sync filters, recomputation on a cone change, loss of the cone (unreadable, or dropped by fossil rebuild, after which fossil's pull heals the store), verify and rebuild counts, R-card omission and skipping, a commit over elided subtrees pushed to a dense peer, widening, hybrid histories, and delta bases.

Holding out-of-cone sub-manifest nodes is recorded as a deviation from §5.1. zit holds them too, because a tree that cannot be enumerated never links.

A checkout's record names the phantom for a file that neither cone covers, so a checkout opens over a sparse store. check-in: b907b41650 user: [email protected] tags: feat/submanifest-reader, main, trunk

00:25
feat(sync): never request a sparse store's elided phantoms

The client's phantom load and the server's request_phantoms skip elided phantoms (draft-fossil-sparse-cone-00 §5.2). A dense store classifies nothing and behaves as before. Repo.StoreCone and Repo.SetStoreCone expose the store cone. check-in: 37f15485b4 user: [email protected] tags: feat/submanifest-reader, main, trunk

00:25
feat(manifest,verify): commit over a sparse store, and count absences

A commit over a store with a store cone leaves out the R card (draft-fossil-sparse-cone-00 §4.3). It refers to out-of-cone files the store lacks by hash alone (§5.3). VerifyRCard skips over a sparse store, and skips a manifest that has no R card, since an absent R card is not damage.

verify.Report gains Phantoms (transit) and Elided. Neither is an Issue, so a store complete within its cone verifies OK (§5.2). Rebuild keeps the store cone and reports the same counts. check-in: cd98ba38cb user: [email protected] tags: feat/submanifest-reader, main, trunk

00:25
feat(content): store cone and the three absence classes of the sparse store

The store cone (draft-fossil-sparse-cone-00 §5) is kept in a storecone table that exists only while a cone is set. fossil rebuild drops tables it does not know, so losing the cone reads as dense and the store heals toward dense (§5.2). A cone that does not parse also reads as dense.

LoadAbsences sorts every phantom into transit, shunned or elided by zit's rules. A phantom is elided when F cards name it only outside the store cone and no held delta uses it as a base. check-in: 524c749185 user: [email protected] tags: feat/submanifest-reader, main, trunk

2026-10-19
23:58
Merge branch 'feat/submanifest-pragma' into feat/submanifest-reader check-in: 6681b95432 user: [email protected] tags: feat/submanifest-reader, main, trunk
23:53
test(conformance): skip zit tests on a zit that predates send-submanifests

A zit older than check-in 0431958ff4 gates trees on the announced version, so against it the interop tests fail on the protocol change rather than on anything go-libfossil does. zitBinary now probes the binary for the pragma's name, which a zit that sends it carries; zit's version cannot tell, since 0.5.0 was tagged before the change. An older zit is skipped with its path, version and the check-in needed, or fails the run under REQUIRE_ZIT_BIN=1 as a missing one does.

Tracked in #452. check-in: b9c7364ff2 user: [email protected] tags: feat/submanifest-reader, main, trunk

23:44
Merge branch 'fix/update-cone-symlink' into feat/submanifest-reader check-in: 44ac676efe user: [email protected] tags: feat/submanifest-reader, main, trunk
23:12
vfile: assert a Disk is made, and test one on a storage without links check-in: 6eb99ff380 user: [email protected] tags: feat/submanifest-reader, main, trunk
23:10
test(stash): fossil pops go's symlink stash and go pops fossil's check-in: 805385f535 user: [email protected] tags: feat/submanifest-reader, main, trunk
23:09
test: fossil reads go's symlink undo state and go reads fossil's; RestoreFiles keeps links check-in: 03f0b0c67b user: [email protected] tags: feat/submanifest-reader, main, trunk
23:06
fix(stash): stash, revert and apply symlinks as fossil's stash does

Save reads each changed file through vfile.Disk, so a symlink is stashed as its target's name and recorded with file_islink, and its revert makes it a link to the committed target. Apply writes a stashed symlink as a symlink and deletes a link before writing, so neither writes through one. check-in: 5be36b41a0 user: [email protected] tags: feat/submanifest-reader, main, trunk

23:02
fix(checkout): revert a symlink as fossil's revert does

restoreCommitted writes through vfile.Disk with the row's islink, so a repointed link is made a link to its committed target again instead of having that name written into the file it points to. check-in: 6a440bc42f user: [email protected] tags: feat/submanifest-reader, main, trunk

22:58
fix(undo): save and restore symlinks as fossil's undo does

Move checkout's link-aware diskView into vfile.Disk, so checkout, undo and stash share one RepoFILE view: Stat, Read, Write (which deletes a link before writing, makes a symlink when allow-symlinks is on, creates the directory and sets the executable bit as file_setexe does), SetExe, MTime and Remove. The allow-symlinks setting is read by vfile.OpenDisk.

undo.Save, Undo, Redo, Swap, ReadFile and RestoreFiles now take a Disk: a symlink is read as its target's name and recorded with isLink, and is deleted and made a symlink again on restore, as undo_save and undo_one do, so Undo of an Update no longer writes through a link. check-in: 858c20561f user: [email protected] tags: feat/submanifest-reader, main, trunk

22:55
fix(manifest): warn with zit's §8.1 flag-day rule; pin the dated -00 revision

zit retired the sub-manifest draft's provisional phase the same day as the send-submanifests errata (erratum submanifests-00-s8.2-provisional-phase- retired): §8.2 is gone, and its one permanent rule, that the setting must not be enabled on a repository a non-reading peer serves, moves to §8.1. zit's enable warning now states that rule in place of the disposable- fixture text, so go-libfossil's, worded as zit's, does too.

The conformance suite pins the draft at zit check-in bbc6794bf0, the revision dated 19 October 2026 whose Appendix C lists the nine errata it applies; the Appendix A vectors are unchanged.

Tracked in #452. check-in: efba058666 user: [email protected] tags: feat/submanifest-reader, main, trunk

22:50
test: undo, revert and stash of a repointed symlink (red) check-in: 5a08dc00df user: [email protected] tags: feat/submanifest-reader, main, trunk
22:49
test(checkout): pin revert of renames against fossil's revert and undo

An add at a rename's old name ends as fossil's revert leaves it, by every name the revert can be given, and Undo restores what fossil undo does. A three-file rotation and a chain reverted by its middle name end under their committed names; reverting only a chain's second rename keeps the first one pending.

Tracked in #248. check-in: 4bec3ce730 user: [email protected] tags: feat/submanifest-reader, main, trunk

22:48
test(checkout): a renamed symlink is not merged, and the rename drops the old name

Covers the rename and dry-run paths of Update's symlink conflict: the link is listed in Unmerged, nothing is written through it, and only a real run deletes the old name, as fossil's update does. check-in: ef49615d6c user: [email protected] tags: feat/submanifest-reader, main, trunk

21:50
fix(sync): gate sub-manifest trees on the send-submanifests pragma

draft-fossil-submanifests-00 §7.2 no longer gates S-bearing artifacts on the peer's announced version: fossil 2.29 announces 22900 and reads no tree. A peer that reads trees now says so with pragma send-submanifests, which go-libfossil sends in every request right after client-version and in every reply, clone replies included, right after server-version, whatever the sub-manifest settings. The version threshold and the 22900 reader marker are gone; client-version and server-version announce 22800, the fossil release whose protocol this library speaks.

A tree manifest or sub-manifest is refused to a peer that has not sent the pragma with "Peer does not accept sub-manifests.", never converted or omitted. A server decides from the whole request it answers, a client from the replies it has received in the exchange. avoid-submanifests moves from the ci-lock answer into the same leading run of every reply.

Enabling emission over a stored submanifest-k outside 1 to 12 without an explicit k is refused, naming the stored value (§8.1).

The conformance suite pins the draft with the 2026-10-19 errata applied: SM-7.2-1 and SM-6.3-3 are met rather than deviations, SM-7.2-2, SM-7.2-3, SM-6.3-1/2/7 and SM-8.1-3 follow the corrected text, and SM-7.2-4 covers the pragma's placement. The zit interop pushes a tree go-libfossil wrote to zit and checks both sides refuse trees when the pragma is stripped.

Tracked in #452. check-in: 05a53c393d user: [email protected] tags: feat/submanifest-reader, main, trunk

20:09
fix(checkout): revert renames onto taken names, saving undo first

Checkout.Revert refused to undo a rename when another file held the old name: a swap, a chain, a new add or an untracked file there. Like fossil's revert it now saves each file it writes or deletes for undo, then reverts in one transaction, so Undo or fossil undo brings back what it overwrote, and a failed revert puts the files back.

Undoing a rename takes back the old name from whatever row holds it: a row renamed onto it is reverted to its own old name, and any other row is dropped, as fossil's UPDATE OR REPLACE drops it. Every committed file ends under its committed name; fossil's own revert leaves a swap renamed and stops tracking the second file of a chain.

Tracked in #248. check-in: 80764da743 user: [email protected] tags: feat/submanifest-reader, main, trunk

18:49
fix(checkout): Update decides the cone before renames, and never merges symlinks

A file outside a sparse checkout's cone is never on disk, so it read as missing, hence changed, and followed the target's rename; any upstream rename outside the cone then refused the update. It is now classified before renames are followed and matched by name: a rename outside the cone moves only the record, one into the cone writes the file under its new name, and an unedited file renamed out of it leaves the disk.

A symlink edited locally and changed by the target was merged through the link, writing conflict markers into the file it points at. As fossil's update does, it is now a conflict with nothing written, listed in UpdateResult.Unmerged; a rename still removes the old name. check-in: f373c9a8ae user: [email protected] tags: feat/submanifest-reader, main, trunk

18:34
fix(checkout): materialize a symlink entering the cone as a symlink

The cones merge called extractSingleFile without the islink argument the symlink fix added, so it did not build. SetCone now passes the row's islink, and a file entering the cone is written as Extract writes it: a symlink with allow-symlinks on. check-in: 36fccc1f87 user: [email protected] tags: feat/submanifest-reader, main, trunk

18:29
Merge branch 'feat/cones' into feat/submanifest-reader check-in: 1a17a3defe user: [email protected] tags: feat/submanifest-reader, main, trunk
16:51
Merge branch 'feat/submanifest-reader' into feat/cones

Update now follows the target's renames and saves undo state. A file outside the cone stays a record-only step: never written, chmodded, backed up or read for undo. A local edit that a rename would carry out of the cone is refused, naming the cone. check-in: 74aa290eda user: [email protected] tags: feat/submanifest-reader, main, trunk

14:41
Merge branch 'feat/submanifest-reader' into feat/cones

Update's rewrite meets the sparse worktree: a file outside the cone is a record-only step, counted in OutOfCone, and never planned, scanned, checked, written, backed up as NAME-original or chmodded; reloading vfile moves its record to the target. SetCone records the mtime of each file it materializes, as Extract now does. check-in: 47f89f0798 user: [email protected] tags: feat/submanifest-reader, main, trunk

00:43
feat(cli): repo status reports within a sparse checkout's cone; changelog check-in: 0f52dfecb9 user: [email protected] tags: feat/submanifest-reader, main, trunk
2026-10-18
18:46
refactor(checkout): keep the functions the cone touched within 70 lines

The unchanged-file pass of a commit, the on-disk half of a rename, the tracked-row lookup of Manage and Create's local records become helpers; Update's content precheck moves into buildUpdateMaps. The fossil interop test isolates fossil's home itself. check-in: 857b86be94 user: [email protected] tags: feat/submanifest-reader, main, trunk

18:41
test: fossil opening a sparse checkout cannot commit its uncovered files away check-in: f7c86ac519 user: [email protected] tags: feat/submanifest-reader, main, trunk
18:40
test(conformance): pin the sparse-cone and horizon drafts, with zit as the cone oracle

The two drafts from zit's horizon-rfc branch at 86d4b67372, their requirements tables (later phases recorded as not yet implemented), the sparsecone suite over the cone specification and the sparse worktree, and the horizon suite's one case. zit's serialized cones, the files its sparse worktrees materialize over flat and tree histories, and a commit from a zit sparse worktree are recorded under testdata/zit-cones and replayed without zit. check-in: d9b18ab619 user: [email protected] tags: feat/submanifest-reader, main, trunk

18:29
feat: sparse checkouts in the public API and the CLI

Cone, NewCone, CheckoutCreateOpts.Cone, Checkout.Cone and SetCone, UpdateResult.OutOfCone and ErrOutsideCone; repo open --cone/--cone-not and repo cone (show, add, remove, set, dense, test). check-in: 94304a520c user: [email protected] tags: feat/submanifest-reader, main, trunk

18:26
feat(checkout): the sparse worktree of draft-fossil-sparse-cone-00 §4

A checkout created with a cone materializes, scans and tracks only the files it covers; vfile stays the full record, so fossil still reads the checkout. Commits carry uncovered files from the baseline by hash, update applies uncovered changes to the record only, and tracking, renaming, reverting or merging across the cone is refused naming it. SetCone widens and narrows. check-in: 3672f3d4fb user: [email protected] tags: feat/submanifest-reader, main, trunk

18:26
feat(manifest): a cone predicate at the enumeration seam, and carried files

ListFilesFiltered narrows a check-in's file set to a filter's coverage; on a tree manifest the walk skips every sub-manifest whose key range the filter cannot cover. File.UUID carries an artifact the repository holds into a check-in by its name, verbatim. check-in: 337dde8a34 user: [email protected] tags: feat/submanifest-reader, main, trunk

18:26
feat(cone): the cone specification of draft-fossil-sparse-cone-00 §3

Inclusion and exclusion members as literal path prefixes, normalized through tracked-path validation, longest member deciding, dead exclusions and contradictions refused, and zit's canonical serialized form (version 1, and version 2 for exclusions) with its SHA3-256 digest. check-in: 027fc6c741 user: [email protected] tags: feat/submanifest-reader, main, trunk

17:33
fix(manifest): re-read settings before auto-retune; queue republished leaves

The post-commit re-tune re-reads the settings in its own transaction and does nothing unless the pin is still the commit's k and submanifest-auto-retune is still on, so a manual re-tune or the automatic one switched off since the commit is never overwritten. A sub-manifest a public commit makes public is queued for the next push, since it was never sent while private. The CLI states the k range from the manifest constants, and buildCheckinDeck's R-card computation moves to checkinRCard. check-in: c907b009c7 user: [email protected] tags: feat/submanifest-reader, main, trunk

17:26
fix: select over the newest check-in off trunk; honest submanifests status

Repo.EnableSubmanifests(0) analyzes the main-branch tip, and when no check-in is on the main branch (a repository whose history sits on another branch name) the newest check-in by time, the highest rid breaking a tie, rather than refusing; an empty repository still refuses. The godoc names the check-in in each case, and DisableSubmanifests' says existing trees remain and keep being served and that no note is returned.

`repo submanifests status` fails when a setting cannot be read instead of printing defaults, and shows a stored k outside 1..12 as invalid, with commits falling back to flat manifests. check-in: a659919be4 user: [email protected] tags: feat/submanifest-reader, main, trunk

17:19
fix(cli): refuse an explicit --k 0, and restore the SDK source links

`repo submanifests enable --k 0` ran automatic selection; §8.1 refuses an explicit k outside 1..12, so only an absent --k selects now. The SDK pages are rebuilt with gomarkdoc's repository flags, which a worktree cannot detect, so their source links match the published pages again. SM-7.2-3's note says the pragma is not reachable from the public API. check-in: 7a2f6dbe7a user: [email protected] tags: feat/submanifest-reader, main, trunk

17:19
fix(manifest): tree storage, privacy and post-commit work per review

- A public check-in makes every sub-manifest it names public, so a leaf a private check-in stored first is sent with the tree. - Sub-manifests are named by the hash policy alone (StoreUnderPolicy), in sha1, sha3 and auto repositories alike, and only those the commit wrote are queued for sync. - A sub-manifest that fills a phantom links the check-ins waiting on it once the commit is durable (§7.1, §7.3), not at the next sweep. - The automatic re-tune runs after the commit is durable, as zit runs it; its failure is logged and never fails the commit. - Enabling selects outside any write transaction, replaying one check-in at a time, then re-reads the settings in a short write; a pin that appeared meanwhile takes §8.1's precedence. - Checkin's transaction body moves to checkinTx, so neither function exceeds 70 lines. check-in: 0f543785e1 user: [email protected] tags: feat/submanifest-reader, main, trunk

17:19
feat(blob): store under the policy's name alone, reporting the outcome

StoreUnderPolicy names content by the repository's hash-policy algorithm and never reuses a copy held under the other algorithm's name, as draft-fossil-submanifests-00 §6.2 requires of an S card's hash. It reports whether the row was found, inserted, or filled a phantom, so a caller can queue only what it wrote and retry what waited on a phantom. Store shares its write-and-verify tail. check-in: 315b5021fc user: [email protected] tags: feat/submanifest-reader, main, trunk

17:04
feat: select over the main-branch tip, and add submanifests status

Repo.EnableSubmanifests(k) now analyzes the current trunk, the check-in the next commit most plausibly descends from, instead of the newest check-in; with no trunk check-in only an explicit k succeeds. A test puts a newer one-file check-in on another branch to show which one is read. Repo.DisableSubmanifests returns only an error and documents that existing trees stay.

`libfossil repo submanifests status` prints the three settings and whether the repository holds a tree; `retune` requires --k, and `disable` prints zit's note when trees exist. The SDK pages are rebuilt with make docs-gen-sdk. check-in: 5377023003 user: [email protected] tags: feat/submanifest-reader, main, trunk

16:58
test(conformance): check enable-time selection against zit

On four flat two-commit histories zit wrote, go-libfossil reproduces every manifest byte for byte and then enables at the k zit pinned; in each the replay moves the pin off the snapshot's value, so the replay port is what is being checked. The pins are recorded for CI under testdata/zit-writer, and SM-6.3-7 states the refinement. The changelog describes the API and CLI. check-in: aea1e207f4 user: [email protected] tags: feat/submanifest-reader, main, trunk

16:58
feat: sub-manifest settings in the Repo API and the CLI

Repo.EnableSubmanifests(k) now selects over the newest check-in itself, so callers pass only k (0 to select); Repo.DisableSubmanifests joins RetuneSubmanifests. `libfossil repo submanifests enable [--k N]`, `retune --k N` and `disable` drive them, printing the pin and the operator warning. The libfossil and cli SDK reference pages are regenerated. check-in: 0ebee94560 user: [email protected] tags: feat/submanifest-reader, main, trunk

16:58
feat(manifest): select k on enable as zit does, and add disabling

The draft's automatic selection is advisory, and zit lets history outrank the snapshot: on enable it replays the newest 200 check-ins through the tree builder at the snapshot's k and its two neighbours and pins the value whose steady-state commits materialize the fewest bytes, the lowest on a tie, keeping the snapshot when fewer than two check-ins replay or every candidate stays flat. replayK ports that step by step, so two writers enabling on one history pin alike.

DisableSubmanifests turns emission off and keeps the pin, returning zit's note when trees already exist. The out-of-range fallback logs zit's report. check-in: 6d79130418 user: [email protected] tags: feat/submanifest-reader, main, trunk

16:44
docs(changelog): sub-manifest writer check-in: 8d3962592c user: [email protected] tags: feat/submanifest-reader, main, trunk
16:44
test(conformance): cover the sub-manifest writer, with zit as oracle

The submanifest table gains the writer's requirements: the §6.1 predicate, §6.2's construction, limits, merge, stops, canonical encoding and storage order, §6.3's pinning, out-of-range fallback and selection, §6.4's manual and automatic re-tune, §8.1's enable rules and disabling, and the writer vectors of A.5. The writer reproduces every Appendix A tree, the A.5 three-level vector at k = 2 and the height-0 merge and encoding vectors at k = 3. 71 requirements: 66 covered, 3 partial, 2 deviations, 0 failing.

TestZitWriterInterop, with zit present, has zit commit ten scenarios (the A.1 set; 40 files at k = 1, 2 and 12; 3,000 files at k = 1, 3 and 5; deep paths; names with spaces, tabs, newlines and non-ASCII, executables and a rename) and requires go-libfossil's check-ins byte-identical. zit then clones, verifies and lists go-libfossil's trees, and verifies a tree go-libfossil commits on top of zit's; the §6.3 selection matches zit's analysis on five file sets. TestZitWriterBundle replays the recorded zit manifests in CI. zitRun now returns stdout only and sets FOSSIL_HOME, and zitListing reads names holding spaces, tabs or newlines. check-in: b77d8b119e user: [email protected] tags: feat/submanifest-reader, main, trunk

16:44
feat(manifest): write tree check-ins behind enable-submanifests

Checkin now honours draft-fossil-submanifests-00 §8.1: with enable-submanifests on and a pinned submanifest-k it stores the canonical tree first, marks each sub-manifest unsent, records the seen-submanifest indicator that arms the §7.2 gate, and writes a manifest of S cards with no F card and never a B card. The setting is off by default, so every commit is byte-identical to before. A stored k outside 1..12 falls back to a flat manifest and logs it (§6.3); unchanged sub-manifests are shared by content addressing.

EnableSubmanifests is §8.1's local request: a valid pin wins and a different explicit k is refused as a re-tune; with no valid pin an explicit k is pinned, or §6.3's selection runs over the current check-in; a request that cannot be met changes neither setting. RetuneSubmanifests is the manual §6.4 re-tune. After a tree commit the §6.4 gate re-pins k while submanifest-auto-retune is on, and only advises otherwise.

A sync run reports avoid-submanifests in SyncResult, and CheckinOpts.AvoidSubmanifests keeps a commit made in that sync flat (§7.2). The settings' names and parsers move to internal/manifest, which sync now uses. Repo.EnableSubmanifests and Repo.RetuneSubmanifests expose the two requests, the first returning the operator warning. A test shows fossil 2.28 never receives a tree this writer made. check-in: 14f17e675e user: [email protected] tags: feat/submanifest-reader, main, trunk

16:44
feat(manifest): build canonical sub-manifest trees

BuildTree is the construction of draft-fossil-submanifests-00 §6.2 over a check-in's sorted file entries at partition parameter k: the §6.1 predicate (leading zero bits of SHA3-256 of the name), the one-pass scan with its MIN and MAX limits and final-group merge at every height, the non-reducing and single-group stops, and the height-16 cap. Sub-manifests come back in storage order, children before parents, named by a caller function so the commit path can store as it names.

partitionCosts is §6.3's C(k), computed with zit's arithmetic so the two writers select alike; SelectPartitionK picks the least cost and the lowest k on a tie, and retuneTarget is §6.4's gate with zit's 15% margin. Tests reproduce Appendix A.2, A.3 and A.4 by name and pin the scan's limits on synthetic counts. check-in: 47cb5afcd7 user: [email protected] tags: feat/submanifest-reader, main, trunk

16:44
feat(deck): canonical encoding and sub-manifest serializers

draft-fossil-submanifests-00 §6.2 fixes minimal escaping for every filename a tree construction emits: exactly space, newline and backslash are escaped, every other byte raw. CanonicalEncode is that encoding; S cards and a sub-manifest's F cards now marshal with it, while a flat manifest keeps §4.3's, so no existing artifact changes name.

MarshalLeaf and MarshalInterior serialize the two sub-manifest shapes, and LeafCard gives one leaf F card's bytes for the §6.3 cost. A test reproduces the Appendix A.2 leaf byte for byte. check-in: e9ec7390c7 user: [email protected] tags: feat/submanifest-reader, main, trunk

2026-10-17
23:26
docs(submanifest): point the version-gate and config deviations at zit

Fossil has no plans to read sub-manifests, so the 22900 threshold is not only fossil 2.29's announcement: every later release clears it too. The fix is a version no fossil announces, agreed with zit and the draft; SM-7.2-1 now cites danmestas/zit#239, and SM-6.3-3's draft wording is tracked in danmestas/zit#240. check-in: 7408c02304 user: [email protected] tags: feat/submanifest-reader, main, trunk

21:54
fix(sync): close the review's sub-manifest transfer findings

- A clone to a client the 7.2 gate refuses could name a withheld tree as a delta source: walkCloneChain never asked the gate, and a refused artifact was still recorded as sent. A flat check-in stored as a delta of a tree manifest -- the normal flat-to-tree transition -- then reached a 2.28 client as a cfile it could never apply. A withheld source is now unforwardable, so its dependent goes out whole, and a refused artifact is not recorded as sent. Gimme answers and pushes already send full content, and igot only announces, so the clone was the one path. - The gate armed only once a crosslink set the seen-submanifest indicator, so a tree a sync had stored but not yet linked went out ungated. Every receive path now sets it as the artifact is stored -- checkDelta hands back the content it rebuilt, so a verified delta costs no second expansion -- and a repository an earlier version swept, which may hold trees that version filed as unparseable, arms the gate until the next sweep re-examines them. - The three sub-manifest settings are written inside one savepoint, so a database error partway leaves none of them applied. - The 22900 threshold is zit's and is also what an unreleased fossil 2.29 announces without reading trees. The constant says so, and the conformance suite records SM-7.2-1 as a deviation with a case that shows such a peer being sent a tree. check-in: 4deb9d870f user: [email protected] tags: feat/submanifest-reader, main, trunk

21:29
perf(manifest): parse a delta's baseline once per sweep for the tree check

A sweep asked every delta manifest whether its baseline is a tree by expanding and parsing the baseline again, after missingBaseline had just done so. Record tree-ness where the baseline is first verified, and walk only a tree baseline. check-in: edefbcd26f user: [email protected] tags: feat/submanifest-reader, main, trunk

21:13
test(testutil): exempt the zit interop test from the fossil-home check

It runs zit, not fossil, each time under a HOME of its own. check-in: 6b694f0419 user: [email protected] tags: feat/submanifest-reader, main, trunk

21:10
docs(changelog): sub-manifest reader, transfer gate and version announcement check-in: 3232f3b158 user: [email protected] tags: feat/submanifest-reader, main, trunk
21:10
test(conformance): read zit's sub-manifest trees end to end

TestZitInterop, with zit on PATH or in ZIT_BIN, builds a flat history in go-libfossil (edits, a delete, a rename, a merge), serves it for zit to re-mint as sub-manifest trees at k = 1, which gives interior sub-manifests, and clones zit's trees over HTTP. Every check-in's file set must equal zit's own `ls --long`, the clone must verify and every R card check. go-libfossil then serves the clone to a zit clone, which verifies and lists the tip the same; pulls a tree check-in zit commits to its own server; and, as a server, receives and links a tree check-in zit pushes to it. REQUIRE_ZIT_BIN=1 makes a missing zit a failure.

CI has no zit, so TestZitTreeBundle reads a recorded copy of what zit wrote (testdata/zit-trees: 81 artifacts and the per-check-in listings, rewritten by ZIT_REGENERATE=1), storing every manifest before any sub-manifest so the deferral and retry run on real zit output. check-in: 3e7b98e18d user: [email protected] tags: feat/submanifest-reader, main, trunk

21:05
test(conformance): add the submanifest suite (draft-fossil-submanifests-00)

Pin draft-fossil-submanifests-00 from zit check-in 3f86ff4261 and write its reader-scope requirements table, 55 rows, from the draft text. The writer sections, the informative 5.6, the operator rule of 8.2 and the 7.4 SHOULD are listed as skipped, with reasons, in the table's header.

84 cases: card-level cases through deck.Parse; tree cases that store artifacts and read them back through ListFiles, LookupFile, Crosslink, VerifyRCard, Verify, Rebuild, Checkin and the sync handler; and the Appendix A vectors, as zit's byte vectors (vendored under testdata/submanifest), checked for their recorded names, walked enumerations and R cards, the A.5 delta-over-tree vector included.

Result: 50 COVERED, 4 PARTIAL, 1 DEVIATION. SM-6.3-3 deviates by following zit: the configuration request is reqconfig /config, the scalar marker zit's server answers, sent once in the clone's cleanup round and never on pull. The PARTIAL rows: a reference cycle cannot be built from content-addressed artifacts (SM-5.1-5); go-libfossil writes no tree, so the emission rules hold vacuously (SM-7.2-3, SM-8.1-1); and the visit bound is not reached by a case (SM-10-1). check-in: 6ccebaeab5 user: [email protected] tags: feat/submanifest-reader, main, trunk

20:57
feat(sync): carry sub-manifest trees safely (draft-fossil-submanifests-00)

Gate, announce and configure sub-manifest transfer the way zit does, so the two implementations interoperate and no fossil release is handed a tree it cannot read.

- 7.2 gate: once the repository has stored an S-bearing artifact, a tree manifest or sub-manifest is withheld from a peer announcing a release below 2.29.0, zit's threshold, with one explicit error card per reply and never by omission. The server applies it to gimme answers and clone batches; the client to gimmes on push, recording each withheld artifact in the result. - This library reads trees, so it announces 22900 in client-version and server-version; a peer holding trees refuses them to anything lower. No fossil release gates any behaviour between 22800 and 22900. - A ci-lock reply carries pragma avoid-submanifests, where avoid-delta-manifests goes, while enable-submanifests is off. - 6.3: the three settings are /project members on the server, which also answers zit's reqconfig /config with them. Clone asks for them as zit's clone does, once, in its cleanup round with zit's scalar marker; fossil answers that with nothing. A reply's settings are staged and applied all or none.

The examined-blob table's old revision is dropped only when present, so a sweep pays no extra write. check-in: 707ffd05a2 user: [email protected] tags: feat/submanifest-reader, main, trunk

20:50
fix(verify): report a tree waiting on a sub-manifest as a missing reference

A tree check-in whose sub-manifest has not arrived is deferred with no derived rows (draft-fossil-submanifests-00 5.2, 7.3). Verify reported that as a missing event row and missing plink rows, which reads as damage. Report it once as a missing reference naming the sub-manifest, and count it in MissingRefs. check-in: ffdf538656 user: [email protected] tags: feat/submanifest-reader, main, trunk

20:49
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. check-in: f51becc770 user: [email protected] tags: feat/submanifest-reader, main, trunk

20:27
test(conformance): re-pin the -01 drafts with the 2026-10-14/16 errata (#436)

zit check-in 3f86ff4261 applies six more errata to the -01 drafts. Copy the three drafts and the five new -01 errata files, re-pin their SHA-256 in the requirements tables, and record the check-in in the README.

The draft text changes add six requirements and reword two:

- AF-4.7.3-2: a named delta's delete markers are filtered out of a delta-of-delta's file set and lookups. - AF-4.7.8-1: an absent and a present empty J value decode alike. - AF-6.2-7: the Z verification state is read from the bytes at n-35 and n-34, not from card lines. - RS-5.3-3: a cancel keeps its card's value on the row it writes. - RS-5.3-6: the event table and propagation treat that value as null. - RS-5.3-7: an artifact's tag cards apply in card order. - RS-6.2-4: a branch root carrying *sym-NAME and -sym-NAME keeps the tag. - SP-6.6-4: a client recognizes a refusal by its leading text.

go-libfossil already behaves this way. Each row has a case, checked against fossil 2.28 where the harness does: 421 requirements, 420 covered, 1 partial (SP-9.5-7, as before).

The repo-state §5.3 step 3 change is filed in zit as a -00 erratum and is not pinned, like the other -00 errata; the -01 draft carries its text. check-in: f61c8174a8 user: [email protected] tags: main, trunk

2026-10-13
17:32
chore(release): v0.12.0 changelog (#435) check-in: 7a25e35893 user: [email protected] tags: main, trunk, v0.12.0
16:51
fix(manifest): maintain the leaf table incrementally (RS-7.3-3) (#434)

* fix(manifest): maintain the leaf table incrementally (RS-7.3-3)

The leaf table was recomputed from scratch at the end of every crosslink batch, so a check-in lost its leaf verdict whenever a later batch linked an untagged child to it. repo-state-01 Section 7.3, as corrected, and fossil keep that verdict: a check-in and its plink parents are rechecked only when a branch tag is written to it or propagation updates or deletes its branch row (src/tag.c:195, :100-102), and the checks run when the batch ends (src/leaf.c leaf_do_pending_checks).

The rule now lives in a new internal/leaf package: Check (leaf_check), Rebuild (the explicit full rebuild) and Pending (the deferred schedule). internal/tag schedules a recheck at exactly the branch-row writes; the crosslink sweep, receive linker and repair gate apply a batch's schedule at the gate, each phantom-fill cascade transaction and each commit or tag transaction apply their own before commit, and verify.Rebuild keeps the whole-table recompute. Reparenting schedules nothing, as in fossil, so the reparent case is split into a one-batch case and a per-artifact case.

Tracked in #426.

* fix(manifest): recheck leaves a cancel reaches; drop rechecks of a failed receive tx check-in: 628f932301 user: [email protected] tags: main, trunk

16:10
test(conformance): refresh the corrected sync §9.4 erratum (#431)

zit check-in 00e1711e802e corrects the erratum's account of Fossil 2.28: forbid-delta-manifests defaults on only since 2.29, and 2.28 defaults it off. The draft text is unchanged. Re-pin the erratum copy and its SHA-256 in the sync requirements table, and record the check-in in the README. check-in: caa2797892 user: [email protected] tags: main, trunk

15:55
test(conformance): re-pin the -01 drafts with the 2026-10-13 errata (#430)

* test(conformance): re-pin the -01 drafts with the 2026-10-13 errata

Re-pin the repo-state and sync-protocol drafts to zit check-in 5a1b741b25d8, which applies five more -01 errata: repo-state §5.3, §7.3 and §8.3, sync-protocol §9.4/§9.5 and §10.1. zit now keeps each -01 text under the -00 file name; the copies keep their -01 names and conformance/rfcs/README.md records the zit path and check-in. The new errata are pinned, and the requirement tables are re-read against every changed hunk.

The §7.3 and §8.3 cases that were deviations now pass as written, and new cases cover a date tag's arrival order (§5.3), untagged leaves and a merge parent's recheck (§7.3).

The sync server answers each guard-passing ci-lock with pragma avoid-delta-manifests while forbid-delta-manifests is on, defaulting to on as the draft and fossil 2.29 do (fossil 2.28 defaults it off). The client leaves its transfer unchanged on that pragma. A file or cfile whose content does not match its hash now fails the client's round, in a pull and a clone: the reply rolls back, prefix included, and no further round starts.

The leaf table is still recomputed per crosslink batch, so a parent whose untagged child arrives in a later batch loses the verdict §7.3 keeps; that case is marked as a gap, tracked in #426.

* fix(sync): continue after a uvfile content error; renumber the avoid-delta-manifests row

A uvfile whose content does not match its HASH is a handler-reported content error: warn, commit the prefix and follow ordinary continuation (sync-protocol-01 10.1). The avoid-delta-manifests client row is SP-9.5-7 now that share-links took SP-9.5-5 and SP-9.5-6. Older unreleased changelog entries drop the pending-erratum wording. check-in: 600861484f user: [email protected] tags: main, trunk

15:33
chore: ignore .agent-config at any depth; drop a stray tool trace (#429)

.gitignore anchored .agent-config/* to the repository root, so a local tool's trace written under conformance/ was committed with the suite in #264. Remove it and ignore .agent-config directories wherever they are. check-in: fd0add2110 user: [email protected] tags: main, trunk

15:25
feat(sync): add share-links mode for pragma req-links and link (#428)

* feat(sync): add share-links mode for pragma req-links and link

sync-protocol-01 Section 9.5 makes share-links mode OPTIONAL. With SyncOpts.ShareLinks a pulling session sends pragma req-links in its first request, as fossil's --share-links does, unless SyncOpts.ProjectCode names another project. It keeps each pragma link URL ARG MTIME as config link:URL = ARG, defossilized and otherwise verbatim, replacing an existing link only with a strictly newer MTIME. A wrong token count, a URL that is not HTTP(S) or an MTIME that is not a positive decimal drops the link.

Where fossil 2.28 differs the draft is followed: fossil fails fatally on a link URL its parser rejects and reads MTIME with strtoll base 0.

New sync.9.5 client cases run against fossil sync --share-links as well. SP-9.5-4 loses its scope note and is covered; SP-9.5-5 and SP-9.5-6 are new rows for which links a client keeps and when it sends req-links.

Tracked in #425.

* test(sync): cover links in later rounds and push+pull req-links; match project code case-insensitively check-in: 5473c06942 user: [email protected] tags: main, trunk

15:15
fix(sync): answer a client clone_seqno with bad command; make Clone's phantom pull opt-in (#427)

* fix(sync): answer a client clone_seqno with bad command; make Clone's phantom pull opt-in

Two deliberate #255 deviations from sync-protocol-01 move to conformance.

SP-5.2-6: the server now answers a client-sent clone_seqno with "error bad command" and processes the cards after it, as Section 5.2 requires for a reply-only card and as fossil's page_xfer does.

SP-8.2-5: Clone now sends nothing after clone_seqno 0 and its cleanup round, so by default it leaves the phantoms a clone produces in place, as fossil(1)'s clone does. The follow-up pull that fetched them is now opt-in through CloneOpts.PullPhantoms, whose doc comment says enabling it departs from Section 8.2.

Both cases leave the sync deviations map. Tracked in #424.

* fix(sync): quote a client clone_seqno as received in bad command; keep DST coverage of the opt-in pull

Tracked in #424. check-in: d3fb5397c8 user: [email protected] tags: main, trunk

14:23
test(conformance): report deviations marked on repo-state API cases (#423)

rsAPICases copied each case's gap mark but never its deviation mark, so repostate.8.3.ckout-inclusive-whole-name, listed in repostateDeviations by #420, ran as an ordinary pass and the coverage report showed RS-8.3-4 as COVERED.

Copy the deviation mark too, and state that case the way the other deviation cases are written: it holds go-libfossil to the draft's answer (Section 8.3 retries "ckout." under ci, where ckout is a keyword, so it names the working files), checks fossil(1) against its own answer (the ckout tag's check-in), and is skipped as a deviation while go-libfossil follows fossil. RS-8.3-4 now reports DEVIATION until the 8.3 erratum is applied. check-in: e71b5c241f user: [email protected] tags: main, trunk

14:09
fix(sync): run each request, reply and clone round in one transaction (#422)

A received delta found wrong when its basis arrives later in the pass fails sync-protocol-01 Section 7.7's commit check, which now rolls back everything the pass stored, as fossil's page_xfer and client_sync transactions do, instead of undoing the delta by hand and keeping the rest. The phantom-fill cascades and the server's closing leaf and tag repair run inside that transaction. Any other card error still commits the prefix before it (Section 10.1); on the client that prefix now includes the reply's buffered igot phantoms and visibility changes.

db.DB.WithBoundTx binds a DB to one write transaction so code written against *DB, nested WithTx included, joins it as savepoints instead of contending for the write lock from a second connection.

SP-7.7-4 is covered; the new SP-10.1-6 has sync.10.1.integrity-error-prefix. Tracked in #418. check-in: c5f5838169 user: [email protected] tags: main, trunk

13:36
feat(resolve): checkout-inclusive request where ckout names the working files (#420)

* feat(resolve): checkout-inclusive request where ckout names the working files

Checkout.ResolveWorking is fossil's "ci+" request (repo-state-01 8.3): while the checkout has a version, "ckout" answers the working files, reported as WorkingVersion.Working rather than as a pseudo-RID. Every other name goes through the same resolver as Checkout.ResolveVersion, restricted to check-ins as fossil's ci+ is. The draft also makes ckout a keyword in a plain "ci" request; this follows fossil, where only ci+ has it.

New repostate.8.3 cases run the request against fossil test-name-to-id --type ci+, and RS-8.3-4 loses its scope note. Tracked in #419.

* fix(resolve): record the ckout retry as a fossil deviation; tighten tests check-in: 0889d8b777 user: [email protected] tags: main, trunk

12:56
fix(sync): verify a dangling delta when its basis arrives in the same pass (#417)

Tracked in #412. check-in: 056f36e0bc user: [email protected] tags: main, trunk

12:27
fix(sync): repair leaves and tags for check-ins the cascade links (#414) (#416)

* fix(sync): repair leaves and tags for check-ins the cascade links (#414)

A pulled check-in arrives as an announced phantom filled the next round, so the phantom-fill cascade links it. The session's closing sweep then found nothing left to link and skipped the leaf and tag-propagation repair, which gated on the sweep's own count: the check-in had an event row and no leaf row, and its parent stayed a leaf. The server handler had the same gap for a pushed artifact that fills a phantom.

AfterDephantomize and AfterDephantomizeBatch now return how many artifacts they linked. The sync client and the handler add that up over the session and close it with the new CrosslinkAfterCascades, which runs the repair once when the sweep or the cascades linked anything (repo-state-01 Section 7.3, RS-7.3-4), not once per file. The receive linker's cancellation paths gate on the same flag.

The repo-state harness gains a pull load: the first artifacts of a script are held and crosslinked locally, the rest pulled in process from a go-libfossil server holding them all. New cases repostate.7.3.pulled-child and repostate.7.6.pulled-retag, checked against fossil per artifact, each run as a .pull case too.

Tracked in #414.

* fix(sync): repair leaves of cascade links when a session fails (#414) check-in: 0035af8ac5 user: [email protected] tags: main, trunk

11:59
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. check-in: 67fc6ced4b user: [email protected] tags: main, trunk

11:38
test(sync): mark dangling deltas whose basis arrives in the same pass (#413)

* test(sync): mark a dangling delta whose basis arrives in the same pass

SP-7.7-4 requires every artifact stored in a pass whose bytes are available in that pass to be checked against its HASH. A delta stored dangling is not rechecked when its basis arrives later in the same reply or request; fossil's commit hook catches it and rolls the round back. Add client, clone and server cases for it, marked as gaps.

Tracked in #412.

* test(sync): note how the server late-basis case must tighten when its gap closes check-in: 8f03734a23 user: [email protected] tags: main, trunk

11:14
fix(sync): verify a received delta against HASH before storing it (#410)

sync-protocol-01 Section 7.7 verifies a reconstructable non-clone file before storage, and SP-7.7-4 has every newly stored artifact whose bytes are available hashed before commit. The client and a clone stored a delta against a held basis as given, and the server checked only a file delta, so a delta that did not give HASH stayed stored under that name until something expanded it.

The check moves to storeReceived, the one choke point every receive path shares, so the client, the server (file and cfile) and a clone refuse a delta whose result is not HASH, or that does not apply. An unheld basis still leaves the delta stored dangling.

Tracked in #405. check-in: b3914d95b3 user: [email protected] tags: main, trunk

11:14
feat(repo): name the current trunk and a branch's open leaf (#411)

* feat(repo): name the current trunk and a branch's open leaf

Repo.CurrentTrunk resolves the main-branch setting's name the way ResolveVersion does, as fossil does for update outside a checkout and open with no version (repo-state-01 Section 9). Repo.BranchOpenLeaf names the open leaf of a branch with the greatest mtime, or reports the branch not open; fossil's branch info takes whichever open leaf its query meets first, and this follows the draft. checkout.BranchLeaves and the new checkout.BranchOpenLeaf share one open-leaf query.

RS-9-1 and RS-9-4 lose their scope notes and gain cases.

* fix(repo): resolve the current trunk as a check-in, reject an empty branch name

Tracked in the stacked work on repo-state-01 Section 9. check-in: c2795c4f48 user: [email protected] tags: main, trunk

11:05
feat(resolve): resolve checkout-relative names and branch-type requests (#409)

* feat(resolve): resolve checkout-relative names and branch-type requests

Checkout.ResolveVersion resolves current, prev/previous and next relative to the open checkout, after tip and before the other forms, as fossil's symbolic_name_to_rid does; a keyword that names nothing falls through. Repo.ResolveBranchStart is the branch-type request: it resolves a name and walks back to the first check-in of its branch. Section 8.3 walks back from every result; fossil only from tag:NAME and plain names, and this follows the draft.

The repostate.8.3 checkout and branch-type cases run against the new entry points, with new cases for a keyword shadowing a tag, prev at a root, an untyped ckout, root:current and a branch-type hash. Tracked in #386.

* fix(resolve): match check-ins only for branch-type requests, merge via checkout

A branch-type request is rewritten to a check-in request (repo-state-01 Section 8.3), so a technote tagged with the branch name cannot answer it. A checkout with no recorded version resolves the ordinary forms, and merge resolves its version through the open checkout. Tracked in #386.

* fix(resolve): wrap a long test line check-in: cba2d0c823 user: [email protected] tags: main, trunk

10:59
feat(repo): set the hash policy by name (#408)

artifact-format-01 7.1 (AF-7.1-2) matches policy value strings case-sensitively and exactly and makes an unrecognized value fatal. No surface took a value string, so the row stayed partial.

Repo.SetHashPolicy takes sha1, auto, sha3, sha3-only or shun-sha1, as fossil's hash-policy command does with fossil_strcmp, stores the ordinal, and refuses any other string without touching the stored policy. Auto in a repository that already holds a SHA3 artifact stores sha3 (7.3).

New conformance cases drive every name, wrong-case names, unknown names and the empty name through it, and the AF-7.1-2 scope note is dropped. check-in: feea6a1614 user: [email protected] tags: main, trunk

10:52
fix(manifest): link a check-in atop an absent parent despite absent files (#407)

* fix(manifest): link a check-in atop an absent parent despite absent files

The crosslink deferral held back a check-in until every F-card blob was stored. When the primary parent's manifest is itself absent, no mlink row depends on those files (fossil's add_mlink returns before reading one, and the parent's crosslink derives the transition once it arrives), so the wait could never complete for files the repository will not receive. The libfossil repository holds one such check-in, a fossil-scm commit imported without its history; the sweep lost its event and plink rows that fossil rebuild writes (RS-4.2-2).

missingCheckinRefs now skips the F-cards when the primary parent is unavailable; the baseline is still required. Adds conformance case repostate.4.2.phantom-parent-absent-file and a unit test that also checks the child's mlink is derived once the parent arrives.

Tracked in #398.

* fix(manifest): record the remaining crosslink deferral gaps

Adds gap case repostate.4.2.root-absent-file, a receive-linker ordering test for the child-before-parent path, and notes the narrowed exemption. Tracked in #398 and #406. check-in: 15762adeb6 user: [email protected] tags: main, trunk

07:32
fix(sync): stop the client reply loop at a card with no artifact name (#404)

* fix(sync): stop the client reply loop at a gimme or igot with no artifact name

fossil's client reads gimme and igot only with blob_is_hname; any other line is an unknown command that ends the reply-card loop, as sync-protocol-01 Section 5.2 requires of a malformed defined card. A server case pins the existing bad-command answer to such a gimme.

Tracked in #397.

* fix(sync): stop the client reply loop at a file or cfile with no artifact name

Tracked in #397.

* test(conformance): split the case tables this round grew past 70 lines

Tracked in #397. Each half keeps its cases in order; only the table functions move. check-in: 636e873e6a user: [email protected] tags: main, trunk