Skip to content
zit Check-in [8d3ebc0c9d]
Login

Check-in [8d3ebc0c9d]

Overview
Comment:Merge iss8: pull converges past the server's unclustered window

A fossil server folds artifacts older than its ~100-entry unclustered window into clusters and stops announcing those members with igot -- the M cards are the announcement. The client stored a received cluster as ordinary content and read nothing from it, so a pull past that window fetched only the cluster and then terminated, silently, having gained nothing. Cluster membership now folds into the phantom set, so the next round gimmes the members. A pull that reaches a clean terminal still lacking artifacts the peer announced now exits nonzero, rather than reporting a success that leaves a repository quietly out of date.

Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 8d3ebc0c9d8b8275023b425d72a25f2bd2af7f94532ee8abb5cbe45633b141dc
User & Date: dmestas on 2026-07-19 20:37:09.039
Other Links: manifest | tags
Context
2026-07-19
20:43
Merge iss4: zit stat, the store's own byte accounting and artifact roll

The store could not be interrogated from outside a checkout: no way to ask how many artifacts it holds, which ones, or how many of its bytes are content rather than framing. zit stat answers from one pass over the frames -- store-bytes, content-bytes, frame overhead, artifact and delta counts. Content is summed from each frame's payload during the walk, never derived from the .zit file size, which also carries the store header, a per-frame header and padding; a size-derived figure would look like a result and be a fabrication, so the two stay separate fields and the overhead between them stays checkable. --artifacts lists the stored hashes for comparison across stores. The frame walk and its accounting live in storestat, shared with the storage benchmark, so the verb inherits the benchmark's exact-accounting test and the two cannot drift. check-in: 921678c019 user: dmestas tags: trunk

20:37
Merge iss8: pull converges past the server's unclustered window

A fossil server folds artifacts older than its ~100-entry unclustered window into clusters and stops announcing those members with igot -- the M cards are the announcement. The client stored a received cluster as ordinary content and read nothing from it, so a pull past that window fetched only the cluster and then terminated, silently, having gained nothing. Cluster membership now folds into the phantom set, so the next round gimmes the members. A pull that reaches a clean terminal still lacking artifacts the peer announced now exits nonzero, rather than reporting a success that leaves a repository quietly out of date. check-in: 8d3ebc0c9d user: dmestas tags: trunk

19:14
sync: absorb cluster membership so pull converges past the unclustered window

A fossil server folds artifacts past its ~100-entry unclustered window into cluster artifacts and stops announcing them with igot: the M cards become the announcement. The client stored a received cluster as ordinary content and read nothing from it, so no phantom appeared, the 3.7(a) continuation saw an empty table, and the exchange terminated after one round having fetched only the cluster. Repository size, not delta size, was the trigger; clone converges on the 8.2 RID cursor, not on gimme, so it was unaffected.

Client.absorbCluster folds the members into the phantom set with the disposition an igot for each would produce, so the next round gimmes them. Membership is validated before any is applied; a clone skips it for the reason it announces no igot. On the cfile path the container inflates into a scratch buffer, not through store.content, which would memoise plaintext.

Client.unresolvedPhantoms counts announced-but-unobtained artifacts; pull exits nonzero when a run reaches a clean terminal with any outstanding, so a pull that stops short of what the peer holds is no longer reported as success. closed check-in: 5535de0f45 user: dmestas tags: iss8

17:32
Merge blockers: a failed mv changes nothing, and its recovery is exact (B4)

mv recorded a rename in the managed set and persisted it before attempting the filesystem move, with no rollback. A move into a nonexistent directory left the set naming a path that held no file: status invented a missing entry, commit refused over it, and the recovery mv --help documents did not work.

mv now validates that the destination's parent exists and refuses before touching anything, so the failing case records nothing.

Two further defects on the same path. An inverse rename replaced the pending record instead of dropping it, so 'mv a b' followed by 'mv b a' left a b-to-a rename queued; the next commit carrying a real change would then emit an old-name F card asserting the file was formerly named b, a name never committed. That is false provenance in a permanent artifact, produced by the exact sequence the help text advertises as recovery. mvRecord now nets an inverse rename back out.

The error path printed raw Zig error names — a user saw 'AccessDenied' where clean and init already print 'permission denied' through reasonOf. It now uses the same mapping.

A residual window remains: a crash between the move and the record write is not atomic, documented at the site. Full working-tree crash consistency is a separate concern. check-in: 33a2ceb6d1 user: dmestas tags: trunk

Changes