17 descendants and 50 ancestors of 78483726d9a1a9ea
|
2026-10-23
| ||
| 03:28 |
Merge branch 'wip/hz-close-sync' into feat/horizon-cones
An origin answering a horizon request never sends a cluster, even one a gimme names; an origin may name a history URL in horizon-served (horizon-history-url), which the client reports; and a merge with no peer to ask refuses whenever either side's ancestry crosses the horizon. Conflicts in the changelog and the CLI horizon tests kept both sides. leaf check-in: c4b191ed05 user: [email protected] tags: feat/horizon-cones, trunk | |
| 03:03 |
fix(merge): refuse a merge whose ancestry crosses the horizon without a peer
draft-fossil-horizon-00 §5.5 has a structural clone ask the origin for the base whenever either side's ancestry crosses the horizon, even when its own walk finds a common ancestor: over part of the graph that ancestor need not be the nearest. Without a peer Checkout.Merge and Repo.Merge refused only when the walk itself crossed, and merged over a held base whose sides' ancestry crossed below it. Both now refuse such a merge with an error matching ErrBelowHorizon that names the horizon and points to Checkout.MergeContext with a Peer; MergeContext refuses the same way when the origin does not answer. A merge whose ancestry stays above the horizon, or one given its base, still merges locally. The CLI's merge already falls back to asking the remote on ErrBelowHorizon, so it now asks in these cases too. check-in: 263de5a736 user: [email protected] tags: feat/horizon-cones, trunk | |
| 03:02 |
Merge branch 'wip/repin-5d61' into feat/horizon-cones
The horizon draft pinned at zit horizon-rfc 5d618aaeb1: an origin with no tag in force mints no seed and serves the content level, and choosing C is shown with each rule's cost (repo cone cost, Repo.HorizonCost). check-in: 77e74bd011 user: [email protected] tags: feat/horizon-cones, trunk | |
| 03:02 |
feat(cli): report what each horizon rule would hold
Draft §3.4 asks a tool that suggests a horizon to show, before the choice, what each rule would hold in check-ins and bytes. repo cone cost <check-in> prints that for descendants and leaves from the local repository, and Repo.HorizonCost returns the figures. The --horizon help of repo clone and repo cone store points to it, noting that a clone has no repository yet. check-in: 834b28d9fe user: [email protected] tags: feat/horizon-cones, trunk | |
| 03:02 |
docs(conformance): re-pin the horizon draft to zit 5d618aaeb1
The draft now says an origin mints no seed, and serves the content level, when no tag is in force at any check-in the seed would cover, since a control artifact needs a T card. HZ-5.2-2 loses its scope note and the new HZ-5.2-7 row is proven by a case that mints none, advertises horizon-served content and leaves the clone holding no seed. HZ-3.4-1 is restated as guidance for the tool; it is covered with the cost report in the next commit. check-in: 4e9dea8cc1 user: [email protected] tags: feat/horizon-cones, trunk | |
| 02:38 |
Merge branch 'wip/hz-close-annotate' into feat/horizon-cones
Annotate in a structural store marks the lines that reach the horizon and names the deepen that would show their origin (§5.5); the origin memo keeps the trees a horizon's walk reads in a bounded cache, so a second horizon over the same store reads only trees the first did not. check-in: 479cbaad12 user: [email protected] tags: feat/horizon-cones, trunk | |
| 02:24 |
perf(content): keep the trees a horizon's walk reads in the memo
draft-fossil-horizon-00 §6.2 asks an origin to read each structural artifact's references once per store and classify every cone over that graph. The memo did so for kinds and P, B, S and A cards, but the F cards of a horizon's trees were walked from the store by the first classification of each horizon, so a second horizon under another C or rule re-read every tree it shared with the first. The memo now keeps, per store lineage, each manifest, baseline and node a walk read: its own F-card targets and its B and S references, so a delta manifest shares its baseline's entry. A view stages what it reads and the memo takes it on Publish, so a request that rolls back leaves nothing; a kept piece serves a view only when its rid is held at that view's prefix, and its size is charged as a read would charge it, so budgets fail where a fresh walk fails. Names are interned and the cache is capped at 64MiB with LRU eviction: fossil-scm's history names about 21M F entries, over 300MiB uncapped. On a copy of a 21k-check-in fossil repository, horizon B (2000 check-ins back) classified after horizon A (1000 back) took 2.5-3.0s and now 1.6s, reading 995 trees instead of 1995; A is unchanged at about 1.3-1.6s. check-in: 13f8021952 user: [email protected] tags: feat/horizon-cones, trunk | |
| 02:20 |
feat(sync): name a history URL in horizon-served
draft-fossil-horizon-00 §6.2.5 lets an origin append to pragma horizon-served the URL of an interface outside the protocol at which history below a horizon can be queried, and §5.5 has a client direct the user there. go-libfossil emitted the level alone and dropped any URL a reply carried. An origin now appends its horizon-history-url setting when set. The value rides the pragma as one raw card token, so SetConfig refuses anything but a single http or https URL with a host and no white space or credentials, and the handler emits nothing for a value stored past that check. The client records a valid URL from any origin in its clone and sync results, exported additively on the public results, and the CLI's clone and cone store/deepen print it. check-in: fe06d02a12 user: [email protected] tags: feat/horizon-cones, trunk | |
| 02:16 |
feat(annotate): report lines from at or below a structural horizon
draft-fossil-horizon-00 §5.5 requires an operation needing history below the horizon to detect the crossing and name the horizon. Annotating back past C stopped at the parent the store does not hold and credited every remaining line to the oldest version it reached, presenting a guess as an attribution. The walk now asks content.BelowHorizon about the parent it could not load; when it lies below the structural horizon, the lines left without credit are marked BelowHorizon and reported against the horizon's check-in. The public AnnotatedLine carries the flag, and repo annotate prints such lines against ^<horizon> with one trailing note naming the horizon and repo cone deepen. A content horizon holds every manifest, so BelowHorizon never fires there; a conformance case clones both depths to show it. HZ-5.5-1 is now covered for every operation it lists. check-in: 1823fe8ca8 user: [email protected] tags: feat/horizon-cones, trunk | |
| 02:13 |
fix(sync): withhold a cluster a horizon request names by gimme
An origin answering a horizon-scoped request already announced no cluster in its clone, pull or sync inventory and answered req-clusters with nothing, but a gimme naming a cluster still got the cluster's file card. A horizon clone that learned a cluster's name anywhere could so still be sent one, against draft-fossil-horizon-00 §4.5 and §6.2. The handler now drops a cluster from a gimme's answer when the request carries a horizon pragma. A new conformance case records the origin's replies to a horizon clone, pull and sync across freshly minted clusters, with and without a cursor, and to every gimme and req-clusters shape, and checks no cluster name crosses; an interop test checks zit's origin the same way. check-in: 5768a5342d user: [email protected] tags: feat/horizon-cones, trunk | |
| 02:05 | conform(horizon): HZ-3.4-1 waits on a wire form, not on step 3 check-in: 265daf2d5f user: [email protected] tags: feat/horizon-cones, trunk | |
| 02:00 | docs(changelog): one statement of the final horizon pin; RepoCloneCmd no longer comparable check-in: 10c8e37095 user: [email protected] tags: feat/horizon-cones, trunk | |
| 01:57 |
Merge branch 'wip/repin-2445' into feat/horizon-cones
The horizon draft re-pinned to zit horizon-rfc 24457c7dd6, the batch agreed with zit's author: the list-form merge-base query (at most two secondaries; silence when a named check-in is unknown), must-ask when either ancestry crosses the horizon, deepen refusing without a common ancestor, boundary labels over every held manifest, the seed's date and label, the structural fallback dropping the seed, and cursors voided when the leaves rule's held set grows below them. check-in: c216f38b6a user: [email protected] tags: feat/horizon-cones, trunk | |
| 01:42 |
docs(changelog): the re-pin to zit 24457c7dd6
Record what the re-pin changed in behavior: the list-form merge-base query and when a merge asks it, the seed's U label, the deepen refusal's remedy, and the cursor voided by a leaves-rule fork. check-in: bb5fc8ebff user: [email protected] tags: feat/horizon-cones, trunk | |
| 01:42 |
test(conformance): the baseline parent label and the fallback with zit
The re-pinned draft makes the parents of a held baseline below C boundary hashes (5.3, 6.2.2) and states the structural store's fallback for a pull or sync (6.2.5, 6.3). go-libfossil already met both; these cases check them against the final text. A case on the delta-baseline fixture (c3 and c4 delta manifests on c2, C = c3) checks that c1, the baseline's parent, is labelled no later than the reply delivering c2 and is held as an elided phantom the clone never asks for. An interop test runs the fallback with zit both ways: zit's structural store pulls from go-libfossil serving as a horizon-ignorant peer, and go-libfossil's pulls from a zit that cannot place C; each falls back to the content horizon, discards its seed and fetches the manifests below C. The recording origin gains an ignorant mode for it. check-in: a553cc1e11 user: [email protected] tags: feat/horizon-cones, trunk | |
| 01:42 |
fix(sync): void a cursor when a fork moves a leaves-rule meeting point
Under the leaves rule a new childless check-in forked below C can move the meeting point of its ancestry with C's deeper, and older check-ins join K together with their blobs. Those sit below every cursor the origin had handed out, so a client holding one was never told of them: the origin recorded a reclassification only when a phantom was filled (6.2.6 requires recording any change of the classification). The origin now also voids a cursor when K holds a check-in at or below it that the held set computed over the check-ins it held through that cursor lacked (horizon.Graph.JoinedAfter, a walk bounded by rid). Like the slot walk's void it is a function of the store, so the memo and a fresh classification agree: TestHorizonMemoEqualsFresh compares them at every cursor and now requires some world to void this way, and a cursor conformance case pulls under the leaves rule across such a fork, the stale cursor answered as if absent once and the next one honoured. check-in: 2c0329ede4 user: [email protected] tags: feat/horizon-cones, trunk | |
| 01:41 |
fix(sync): name the remedy when a deepen has no common ancestor
The re-pinned draft's 5.8 has a deepen that finds no common ancestor of C and its target, here or at the origin, refuse and name the remedy, a fresh clone with an earlier horizon, and never move C to a check-in that is not its ancestor. Deepen already refused with ErrNoDeepenPoint and left C and the seed in place, but its message named no remedy; it now does. A conformance case covers both refusals (the origin answers -, and the origin does not answer) and checks that C, the seed and the derived state stay as they were. check-in: 7a2c0b3bc3 user: [email protected] tags: feat/horizon-cones, trunk | |
| 01:41 |
feat(sync): ask the list-form merge base whenever a merge crosses
The re-pinned draft's 6.2.4 query is a primary and one or two secondaries, `pragma horizon-mergebase <primary> <secondary>...`, and the answer repeats the query in order and appends Fossil's pivot over the set. A two-hash query was answered as the base of a pair, so a merge into a checkout with a merge already pending could be told a base Fossil's merge would not choose. The origin now takes the list (sync.MergeBaseQuery), answers nothing when it lacks any named check-in or is asked more secondaries than a six-token card can repeat, and the client takes only an answer that repeats its query. 5.5 now has the clone ask whenever either side's ancestry crosses the horizon, not only when its own walk does: over part of the graph the common ancestor found need not be the nearest. MergeContext, given a peer, checks the ancestry of the merged-in version, the checkout and its pending plain merges (content.AncestryCrosses), asks the pivot of the merged-in version over the checkout and those merges, and merges over the answer, falling back to Merge when the origin does not answer, as zit's merge does. With two merges already pending the query cannot carry them, and asking without them would name another base, so the merge is refused before anything changes. The deepen point's query names the target over C, as zit asks it. check-in: 78483726d9 user: [email protected] tags: feat/horizon-cones, trunk | |
| 01:40 |
fix(sync): label a seed's U card with the minting implementation
The re-pinned draft (5.2.2) makes a seed's U card the origin's own label for the minting implementation, as zit writes zit-serve, never a user's login: a seed is a tag application no user made. The origin wrote the role name "origin"; it now writes "go-libfossil". The seed-date case is restated to the final text: D is the latest summarized tag event rounded up to a whole second and written with no fraction, checked with an event a quarter second past the second, and the dropped clause (D strictly later than the seed replaced) is no longer asserted. The structural fallback case moves to the new 6.3 row for a store already at the structural horizon. check-in: fe7d47778e user: [email protected] tags: feat/horizon-cones, trunk | |
| 01:40 |
docs(conformance): re-pin the horizon draft to zit 24457c7dd6
zit's horizon-rfc check-in 24457c7dd6 revises the horizon draft; the other six drafts are byte-identical at it and at 3a2df62515. Each change has its own Appendix C bullet: the seed's D card is the latest summarized tag event rounded up to a whole second with no fraction, its U card the origin's label for the minting implementation, and the strictly-later clause is dropped (5.2.2); a merge asks the origin whenever either side's ancestry crosses the horizon (5.5); a deepen with no known common ancestor of C and the target is refused naming the remedy (5.8); the merge-base query takes a list of secondaries (6.2.4); a boundary hash is a parent of any held manifest, a baseline below C included (5.3, 6.2.2); and a store already at the structural horizon falls back to the content horizon, discarding its seed, when its replies lack horizon-served structural (6.3). The 3.4 cost note changes no rule. Restate the rows whose text changed (5.2.1, 5.2.2, 5.3, 5.6, 6.2.2, 6.2.3, 6.2.4, 6.3) and add rows for the new statements (5.3, 5.5, 5.8, 6.2.4, 6.3), so each is checked against the final text. check-in: 9a0f07fe70 user: [email protected] tags: feat/horizon-cones, trunk | |
| 01:16 |
Merge branch 'wip/repin-3a2d' into feat/horizon-cones
The horizon draft re-pinned to zit horizon-rfc 3a2df62515, and its new rule: under either horizon a path cone elides the sub-manifest nodes no in-cone slot reaches, and an origin withholds them. Interoperates with zit at 3a2df62515 both ways. check-in: 113123abae user: [email protected] tags: feat/horizon-cones, trunk | |
|
2026-10-22
| ||
| 23:43 |
feat(sync): withhold out-of-cone nodes of the trees a horizon keeps
Under either horizon with path members an origin now sends only the sub-manifest nodes on paths into the cone of the trees its horizon keeps (every check-in's at the content depth, K's at the structural one), announces none of the rest and answers no gimme for them; a request without path members still receives every node (draft-fossil-horizon-00 §6.2 at zit 3a2df62515). The walk is zit's walkSlots (src/sparse.zig): only slots that may hold an in-cone path are descended, a last slot takes its parent's bound, and a node is walked again only under a wider bound. It runs over the classification's facts, never the store, and the origin's memo keeps it by cone so a later request walks only the roots appended since, falling back to a fresh walk when a root arrives below the ones walked. A clone round sends nodes by the per-check-in tree walk it already uses without a horizon, walking a check-in outside the held set for its nodes alone. Each kept node records the lowest root that keeps it, so a cursor is void (§6.2.6) exactly when some node at or below it is kept only by a root above it: stateless, and the same for a fresh classification and the memo, where zit voids every cursor below the store count at the moment its sidecar sees the change. The memo returned a cone's earlier classification whenever no fact list grew, keeping that store's names, so an artifact appended or filled without becoming a record read as unnamed; it now carries the current facts. Found by the memo equivalence test, which now covers path cones under both horizons, the slot walk, cursor voiding and a tree form that widens a node's bound. check-in: 0de31d6f8e user: [email protected] tags: feat/horizon-cones, trunk | |
| 23:43 |
feat(content): elide out-of-cone nodes under a content horizon
draft-fossil-horizon-00 at zit 3a2df62515 has a path cone elide sub-manifest nodes under either horizon, as the sparse store does (§3.2, §4.1). A content-horizon store held every node of every age, so classification kept a phantom node no in-cone slot reaches as transit and the store kept asking for it. Such a node is now elided, below the horizon too; a node on a path into the cone stays wanted at every age, and a horizon without path members still wants every node. With nodes elided, a check-in's tree is read by the sparse readiness check before its spine is linked, and that check waited on every covered file. Under a horizon a covered file may lie below it, and which do is placed over the very check-ins the check gates, so a check-in below the horizon was never linked and the derivation lost it. Under a horizon the check no longer waits on files: the spine it publishes reads none, and the classification at rest tells an elided file from a transit one. check-in: 63d67666cd user: [email protected] tags: feat/horizon-cones, trunk | |
| 23:19 |
docs(conformance): re-pin the horizon draft to zit 3a2df62515
zit's horizon-rfc check-in 3a2df62515 changes one rule of the horizon draft: a path cone now elides sub-manifest nodes under either horizon, as the sparse store does. Of the trees the horizon keeps (every check-in's under a content horizon) only the nodes on paths into the cone are held, an origin withholds the rest, its walk is bounded and kept like the classification, and a cursor is voided when an appended tree makes an older withheld node kept. The other six drafts are byte-identical at both check-ins. Restate the rows whose text changed (3.2, 4.1, 5.1, 6.2.6), add rows for the new statements (4.1, 4.3, 6.2), and mark the restated rows the code does not meet yet rather than leave them claiming coverage. check-in: bd80910304 user: [email protected] tags: feat/horizon-cones, trunk | |
| 22:54 | docs(changelog): the origin classification memo check-in: abecc56d21 user: [email protected] tags: feat/horizon-cones, trunk | |
| 22:54 |
fix(sync): label the parents of every held manifest at a structural horizon
A structural clone holds the manifests of the held set and everything they reach through B and S cards (draft-fossil-horizon-00 §5.1), so a delta manifest's baseline below C is held. Its parents were labelled by nobody: boundaryOf took the parents of K's check-ins only. The clone then classified such a parent as transit and requested it, and the origin, which withholds manifests below C, never sent it; the clone stalled with one phantom outstanding. zit's origin has the same gap (sparse.zig 758-783), and zit's author agreed the draft will define a boundary hash as an unheld parent of any manifest the clone holds. boundaryOf now labels the parents of K and of every check-in the reach holds. The regression is the HZ-3.1-4 case for a delta manifest in K whose baseline lies below C: the clone completes, the baseline is held but not a leaf, and K's check-ins derive the dense repository's tags. check-in: f0354673e2 user: [email protected] tags: feat/horizon-cones, trunk | |
| 22:48 |
Merge branch 'wip/hz-memo' into feat/horizon-cones
The origin's classification memo (draft-fossil-horizon-00 §6.2): the reference graph and each cone's classification kept per store prefix and extended by appended artifacts, recomputed in full only when an arrival was already cited. A pull after a one-artifact push on fossil-scm.org's repository answers in about 0.25 s instead of 2.7 to 4.5 s. check-in: 7dec3e4fb3 user: [email protected] tags: feat/horizon-cones, trunk | |
| 22:34 |
test(conformance): cover the origin's classification memo
HZ-6.2-9 and HZ-6.2-10 were gaps. The new cases read, from the handler's report of what each request's classification read, that a pull over an unchanged store reads nothing, one after a commit reads what it appended, a blob after its manifest is classified alone, a parent after the child citing it forces a full read, and a cone never classified is classified without re-reading the store; and that every reply announces what a second handle with an empty memo announces. HZ-6.2-10 is partial: a horizon's first classification walks its held set's trees from the store, as zit's does. The case list joins the horizon suite from the coverage table, leaving the horizon case files to the work in flight there. check-in: 6f8176bb93 user: [email protected] tags: feat/horizon-cones, trunk | |
| 22:34 |
fix(content): the non-artifact record appearing extends the memo
A store crosslinked only locally has no non-artifact record until its first received artifact is crosslinked, so the first push to an origin made the memo read the whole store again. The record appearing is every version it names being named at once; those were read raw, and reading file content raw adds nothing, so the extension takes them as files and goes on. Only a version whose raw read added a record or an unreadable name still forces a full read. check-in: bdff158325 user: [email protected] tags: feat/horizon-cones, trunk | |
| 22:31 |
perf(sync): extend the memo by the arrivals' metadata alone
Measured on a dense 68k-artifact, 21k check-in repository, a horizon pull after a one-artifact push still spent most of its time scanning every blob's metadata to extend the memo, and a structural request extended it twice: once to place the horizon ahead of the request's transaction, unpublished, and again inside it. An extension now carries the artifact names over and reads metadata only for the arrivals and the delta ancestors their reads expand. A structural request places its horizon in a read-only transaction of its own, a committed snapshot, and publishes that view, so the inventory finds the memo current. TestCorpusHorizonPullAfterPush (LIBFOSSIL_CORPUS) times it: a pull after a one-artifact push goes from about 2.7s to 0.2s for a content horizon, and from about 4.4s to 0.3s for a structural one. check-in: 62ffc8ff92 user: [email protected] tags: feat/horizon-cones, trunk | |
| 22:26 |
feat(sync): an origin classifies horizon requests through its memo
Each request re-read the store to classify its cone, so a pull after a one-artifact push to a large origin cost a full read. The handler now takes each store's classification memo (one per pool DB, dropped with it) and classifies its inventory and its structural reach through a view of it. The view is published only once the request has committed and stored no artifact: the inventory classifies before received files are crosslinked, so such a view describes a store that never committed. A caller holding its own transaction (a bound DB) classifies afresh. SparseClassified now reports what the classification read: the whole store on a full read, the arrivals on an extension, none when the memo covers the store. A property test drives random commits in every manifest form, pushes in any order and announcements against an origin and checks, after each step, that the memo keeps exactly what a fresh classification keeps for path cones, content and structural horizons under both rules. check-in: 83f8e43ebd user: [email protected] tags: feat/horizon-cones, trunk | |
| 22:26 |
fix(content): the memo re-reads a store whose old rows were linked late
A go-libfossil store is not append-only: a phantom fills at its old rid, and the crosslinker can link an old artifact once what it waited on arrives. An extension by the appended and filled rids alone would miss that, so the memo counts the check-in and plink rows at or below its prefix and reads the store in full when they move other than by a fill, or when a filled phantom is the base of a stored delta (the delta was unreadable before the fill). A new check-in's mlink rows naming an old file version no longer force a full read: reading file content raw adds nothing to the facts, so only a version that did is a change. check-in: 423b5535d4 user: [email protected] tags: feat/horizon-cones, trunk | |
| 22:18 |
feat(content): an origin classification memo extended by appends
An origin re-reads its store for every horizon request: the reference facts behind the path classification and the trees of the whole held set. draft-fossil-horizon-00 §6.2 asks it to keep the classification keyed by cone digest and store prefix, extend it by the artifacts appended since, and recompute in full only when an arrival carries a name a held P, B or S card cited while it was absent. Memo keeps the store's cone-independent references (the reference facts, the check-in graph and the dangling P/B/S names) at a prefix, and per cone digest and per horizon the classification over them. A view extends each by the appended and filled rids: the loader's decisions for those rids alone, classification of the facts' new tails, and the tree walk of the check-ins K gained. Anything other than an append or an uncited fill (a cited arrival, a deleted or phantomized row, a first tree) reads the store in full. Views are copy-on-write and reach the memo only when published, so a rolled-back request leaves nothing behind. check-in: e5bdac3d33 user: [email protected] tags: feat/horizon-cones, trunk | |
| 22:12 |
feat(horizon): a check-in graph a held set extends over
An origin computes K for every horizon request by reading every plink edge and held check-in from the store. draft-fossil-horizon-00 §6.2 asks it to read those references once into a graph keyed to the store prefix and extend that graph by the artifacts appended since. Graph is that graph: loaded once, extended copy-on-write by the rids that changed, and HeldSet over it equals HeldSet over the store. check-in: b1933e5f82 user: [email protected] tags: feat/horizon-cones, trunk | |
| 22:07 |
Merge branch 'wip/hz-small-rows' into feat/horizon-cones
HZ-3.1-2: the leaves rule's cost is stated where it is chosen. HZ-3.4-2: a horizon reaches the wire only as a full hash and a rule name. check-in: c79a113e0c user: [email protected] tags: feat/horizon-cones, trunk | |
| 22:07 |
feat(horizon): state the leaves rule's cost; prove the wire carries only hash and rule
A client that wants to merge across live branches offline should pick the leaves rule knowingly (HZ-3.1-2). The rule holds every descendant of where each childless check-in's history meets C's, which on a repository with old unmerged branches is nearly the whole history. clone and cone store --leaves now warn once on stderr, the flag help says so, and HorizonLeaves documents the cost for library callers. HZ-3.4-2 asks that a horizon never be a count or a date on the wire. A case records the request cards of a horizon clone and pull under both rules and asserts one pragma of a full hash and a rule name; the CLI's clone refuses a name, and its store cone resolves a branch name to the full hash first. check-in: d7a8b71b9b user: [email protected] tags: feat/horizon-cones, trunk | |
| 21:38 | docs(changelog): sparse stores rebuild check-in: e5cedd31cb user: [email protected] tags: feat/horizon-cones, trunk | |
| 21:38 |
Merge branch 'wip/rebuild-perf' into feat/horizon-cones
A sparse or horizon store of a real-sized repository rebuilds: deferred check-ins are linked after the sweep and its non-artifact record, so reference loading answers from the tables instead of reading every blob against the budget. check-in: ba1e308608 user: [email protected] tags: feat/horizon-cones, trunk | |
| 21:37 |
test(manifest): pin that a rebuild loads references after the sweep
A sparse rebuild must load whole-store references once the sweep has filled the tables they are answered from, or it reads every blob raw and exceeds the reference budget. The loader is now a seam the test observes, and the corpus test checks every phantom classifies as before a rebuild. check-in: 1faf2d23c3 user: [email protected] tags: feat/horizon-cones, trunk | |
| 21:28 | Merge branch 'feat/horizon-cones' into wip/rebuild-perf check-in: 31766dc383 user: [email protected] tags: feat/horizon-cones, trunk | |
| 21:28 |
fix(manifest): judge deferred check-ins after the sweep has filled the crosslink tables
A sparse or horizon store's deferred check-in is linked partially, which loads whole-store references. Those are answered from mlink and the non-artifact record, but a rebuild empties mlink and records non-artifacts only at the end of the batch, so the first deferred check-in found nothing to answer from, read every blob raw and exceeded the 512 MiB expansion budget on a real-sized repository (Rebuild failed after about a minute). linkBatch now sets deferred check-ins aside and links them partially once the sweep and the non-artifact record are done, when the references are answered from the tables as they are after a clone. A rebuild of the {src}-cone store now completes in about 50 s under 700 MiB; its derived tables are row-for-row identical to those of the previous code run with the budgets raised far enough to finish. check-in: 85a71d319a user: [email protected] tags: feat/horizon-cones, trunk | |
| 21:17 | docs(changelog): rebuild in under a minute check-in: 23831a1bce user: [email protected] tags: feat/horizon-cones, trunk | |
| 21:17 |
Merge branch 'wip/rebuild-perf' into feat/horizon-cones
Rebuild of fossil-scm.org's repository in about 50 s instead of 3 to 6 minutes: delta-chain availability answered from the sweep's cache, and each manifest parsed once. Every derived table is identical before and after. check-in: 6339d6fb50 user: [email protected] tags: feat/horizon-cones, trunk | |
| 21:17 |
perf(manifest): stop re-walking delta chains and re-parsing manifests in a rebuild
Rebuilding a clone of the Fossil SCM repository (67,894 artifacts, 21,008 check-ins) took about 3 minutes of which 104 s was parentTreeGap asking content.AvailableByUUID, an uncached delta-chain walk with one SQL statement per node, for every parent of every check-in. It now uses the sweep's availability cache like the rest of the guard. The same sweep parsed each check-in three times (as a child, as its child's parent, as a delta baseline) and rebuilt its file maps. The sweep's content cache now keeps the parsed deck, file views and baseline rid of its few most recent check-ins (Cache.Derived), dropped with the cached bytes on Invalidate and Clear. Derived hits count as reuse in the cascade stats, so the shared-cache regression guard keeps measuring it. The rebuilt event, mlink, plink, tagxref, leaf, filename, cherrypick, orphan, unclustered, unsent, forumpost, attachment, phantom, blob and delta tables are row-for-row identical to the previous code's. check-in: 48ff1f5084 user: [email protected] tags: feat/horizon-cones, trunk | |
| 21:17 |
test(verify): log peak heap in the corpus rebuild test
The rebuild budget is a wall-time and memory target, and the test only reported time. Sampling HeapInuse beside the run makes a regression in either visible from one -v run. check-in: 7cb4d1aa0d user: [email protected] tags: feat/horizon-cones, trunk | |
| 21:16 | docs(changelog): merges follow renames; Repo.Merge keeps deletions check-in: cb4284230b user: [email protected] tags: feat/horizon-cones, trunk | |
| 21:16 |
Merge branch 'wip/merge-renames' into feat/horizon-cones
Merges follow renames as fossil's merge does: its name pivot, find_filename_changes and file matching ported, so a file renamed on one side and edited on the other merges into the renamed file. Matches fossil 2.28 on 13 rename scenarios. Repo.Merge no longer brings back files the merge deleted. check-in: 4bccd3f803 user: [email protected] tags: feat/horizon-cones, trunk | |
| 21:16 |
fix(merge): follow renames as fossil's merge does
A file renamed on one side and edited on the other was merged as a deletion plus an addition: the edit was kept under the old name and the renamed copy arrived beside it, so fossil and go-libfossil left different trees and recorded different changes for the same merge. Port fossil's rename matching (merge.c merge_cmd): the name pivot nid (pivot_find(1) of M and V, rerun against P when they differ), the renames from nid to V, P and M (path.c find_filename_changes, one way, now path.FilenameChanges), and the fv table those fill (add_renames, the nid!=pid name fill-in, then the files of V, P and M) as merge.MatchFiles/FollowRenames. Checkout.Merge classifies each matched file as before and renames on disk and in vfile a file the merged-in version renamed and the checkout did not, after the other steps, moving swapped names together and setting aside a removed or deleted row that held the new name. Where fossil would rename over a kept tracked file or an untracked one, Merge refuses, as it already does for adds. Repo.Merge matches files the same way and commits the merged tree whole, recording the rename (FileToCommit.OldName); the whole-tree commit also stops it carrying forward a file the merge deleted. The CLI prints RENAME lines where fossil's merge prints them. check-in: 2c3a78cf5e user: [email protected] tags: feat/horizon-cones, trunk | |
| 21:04 | docs: the clone records the horizon cursor check-in: 22bbf792b3 user: [email protected] tags: feat/horizon-cones, trunk | |
| 21:04 |
conform(horizon): the cursor rows are covered
HZ-6.2-11: every reply to a request the origin classified carries the cursor; the operation-less round after a clone's last page answers no pull or clone card, so it is not a classified request, as in zit. HZ-6.2-12: a client records and sends the cursor whenever its transport names the origin; without one it announces in full, which the advisory cursor of §6.2.6 allows. check-in: a33cbbf951 user: [email protected] tags: feat/horizon-cones, trunk | |
| 21:03 |
Merge branch 'wip/clone-cursor' into feat/horizon-cones
Clone replies at a horizon carry the cursor and a horizon clone records it per origin (draft-fossil-horizon-00 §6.2.6), so the first exchange after a clone no longer announces the whole holding. Interoperates with zit both ways. check-in: 7e44f7107b user: [email protected] tags: feat/horizon-cones, trunk | |
| 21:03 |
feat(sync): hand out and record the horizon cursor on clone
The draft has an origin that classified a request emit the cursor in every reply, and a client that converged record the last reply's cursor with its artifact count per origin. A clone did neither, so the first exchange that sends after a horizon clone announced the whole holding. A clone reply now carries the position it served the clone sequence through: below the sequence it hands on, or the origin's whole position once it hands on none. A horizon clone that names its origin records the last cursor and its own count, as zit does; the HTTP transport names it for the public Clone as for Sync. check-in: 7d9536347a user: [email protected] tags: feat/horizon-cones, trunk | |
| 20:38 | docs(changelog): no public baseline option yet check-in: 0cd130a5ce user: [email protected] tags: feat/horizon-cones, trunk | |
| 20:37 | docs(changelog): merge bases as fossil chooses them check-in: d1e7ef2305 user: [email protected] tags: feat/horizon-cones, trunk | |
| 20:37 |
Merge branch 'wip/merge-pivot' into feat/horizon-cones
Merge bases chosen as fossil's pivot_find chooses them: the most recent common ancestor by check-in time, with the check-out's pending merges as further secondaries, in place of a bidirectional breadth-first search that picked a different base on criss-cross and skewed histories. Checked against fossil 2.28 on 114 queries and a byte-for-byte merge. check-in: 97e188b6ef user: [email protected] tags: feat/horizon-cones, trunk | |
| 20:37 |
fix(merge): read the pivot seed's mtime as a number under every driver
The ncruces driver reads event.mtime, declared DATETIME, as a time.Time, so the pivot search failed to scan its seed under -tags test_ncruces. coalesce() drops the declared type, as the parent query's already did. check-in: 89c6ae4aa6 user: [email protected] tags: feat/horizon-cones, trunk | |
| 20:12 |
test(conformance): check merge bases against fossil merge-base
The merge base is now fossil's pivot, so check it against fossil(1) on every run rather than against an expectation written once. Scripted graphs, loaded into both implementations, cover a skewed criss-cross (the newest common ancestor two edges away, not the one an edge away), a criss-cross at equal times in both load orders (the higher rid wins the tie), merges of merges and a query with two secondaries, and --ignore-merges. A merge of p into a checkout of s in the skewed graph writes the same bytes as fossil's merge and differs from a merge against the breadth-first base. TestMergeBaseFossilOracle builds six seeded random graphs of sixteen check-ins, with equal times and children older than their parents, and compares 114 merge-base questions, both directions, with and without --ignore-merges and with two secondaries, against fossil merge-base. check-in: 944b44faf6 user: [email protected] tags: feat/horizon-cones, trunk | |
| 20:12 |
fix(merge): choose merge bases as fossil's pivot_find does
go-libfossil picked a merge base with a bidirectional breadth-first walk, the common ancestor nearest by edge count. fossil's merge uses pivot.c's pivot_find: a queue ordered by pending DESC, mtime DESC that expands the newest pending check-in first and returns the first one with a child queued from the other side, the most recent common ancestor by event time. The two differ on criss-cross merges and skewed histories, so a merge here could diff against another base than fossil's and write other bytes. merge.FindPivot ports pivot_find in memory: (rid, side) rows with REPLACE re-pending, ties broken as a reverse scan of aqueue_idx1 yields them (rid, then side, descending), a missing event mtime counted as 0.0, ignoreMerges following primary links only, and the walk bound of the horizon merge-base query kept as ErrBound. FindCommonAncestor and FindCommonAncestorWithin now ask it. Call sites: - Checkout.Merge takes fossil's pid: the merged-in version primary, the checkout's version and every vmerge id=0 merge the secondaries (merge.c 1076-1083), and gains Baseline, fossil's --baseline. The name pivot nid (pivot_find(1), merge.c 1089-1096) feeds only add_renames and the rename fill-in (merge.c 1192-1257); Merge does not follow renames, so it is not ported. Update and stash use the checkout version as their pivot and pick no base. - Repo.Merge (branch merge) makes the source branch tip the primary. - The origin's horizon-mergebase answer makes the merged-in check-in primary and declines to answer from a walk that reached history it does not hold. - Deepen's local deepen point keeps a boundary target among C's ancestors as its own point, since pivot_find seeds from check-ins only. Horizon client rule (draft-fossil-horizon-00 §5.5, agreed for the draft): a common ancestor found in part of the graph need not be the one the whole graph gives, so a merge asks the origin whenever the walk reaches a check-in the store does not hold, not only when the base it finds is not held. MergeBaseCrossing takes that from Pivot.Unheld, and MergeContext merges over the origin's base as a baseline, deepening only when the store does not hold it. Changed expectations: the horizon merge fixtures (hzStruct's y into feat2, structuralMergeSource, TestBranchMergeRefusesBaseBelowHorizon) fork f below the horizon, merge c2 into f's line and f into c2's: fossil picks c2, not f. On that shape fossil 2.28 answers fossil merge-base y x -> pivot=52d5923b8d (c2) with f=8ac24055 also a common ancestor. The fixtures now add a check-in on f's line newer than the horizon, which fossil does pick, to keep a base below the horizon, and the crossing tests check both the held-base walk that crosses and the base below. check-in: a23c7b12a9 user: [email protected] tags: feat/horizon-cones, trunk | |
| 19:51 | docs(changelog): crossing the horizon check-in: 138ff77321 user: [email protected] tags: feat/horizon-cones, trunk | |
| 19:51 |
Merge branch 'wip/horizon-step6' into feat/horizon-cones
Appendix A step 6: the merge-base query (pragma horizon-mergebase), deepen (Repo.DeepenHorizon, repo cone deepen), merges and bisect that refuse or deepen across the horizon (ErrBelowHorizon, Checkout.MergeContext), the origin's push-time check of a branch cancellation, seed verification by deepening (Repo.VerifySeed, repo verify --seed), and symbolic names below the horizon resolving. Interoperates with zit at 9bf7ebfe0f. check-in: 19d7db48d4 user: [email protected] tags: feat/horizon-cones, trunk | |
| 18:31 |
feat: resolve a symbolic name on a check-in below the structural horizon
Section 5.5: a tag applied to a check-in below the horizon resolves to that check-in's hash, because the control artifact is held. The resolver's sym-tag step joins event, which a check-in below the horizon has no row in, so the name was unknown and an operation on it never reached the horizon diagnostic. Both the tag: form and a plain name now fall back, in a structural store only, to the check-in below the horizon the tag names; Repo.ResolveVersion, Checkout.ResolveVersion and the CLI share the resolver, and checking it out names the horizon and the deepen. check-in: 67bb246d16 user: [email protected] tags: feat/horizon-cones, trunk | |
| 18:30 |
Merge branch 'feat/horizon-cones' into wip/horizon-step6
# Conflicts: # conformance/requirements/horizon.tsv check-in: 75ed88e8e9 user: [email protected] tags: feat/horizon-cones, trunk | |
| 18:14 |
docs: deepen, merge across the horizon, the push check and seed verification
Record Appendix A step 6 in the horizon requirements table and on the sparse page: the API and commands, what each costs, and what HZ-5.5-1 still leaves out (a symbolic name on a check-in below C, and annotate walking back past C). check-in: 078df932f4 user: [email protected] tags: feat/horizon-cones, trunk | |
| 18:13 |
feat(bisect): refuse a good end below the structural horizon
Section 5.5 has a bisection whose good end lies below the horizon deepen to it before it begins. The path between the ends ran through a boundary phantom and the session went on as if the history were there; Next now refuses first, naming the horizon and the deepen that brings the good end, matching content.ErrBelowHorizon. The library session holds no peer, so the deepen is left to the caller. check-in: ad60dffec8 user: [email protected] tags: feat/horizon-cones, trunk | |
| 18:11 |
feat(horizon): verify the seed by deepening
Section 5.9 asks for a verification mode so trusting the seed is a choice with a known price. sync.VerifySeed cuts the structural horizon to the content depth at the same C, which fetches every manifest below C and with them the real tag artifacts and primary-parent chains, discards the seed, and compares what the store then derives at each entry with what the seed asserted. Repo.VerifySeed and 'repo verify --seed' expose it; the store stays a content-horizon store afterward, and a failed transfer puts the structural cone and seed back. zit 9bf7ebfe0f offers no such mode. check-in: 35db744ea7 user: [email protected] tags: feat/horizon-cones, trunk | |
| 18:00 |
feat(sync): refuse a push whose branch cancellation names a stale seed
A check-in that creates a branch cancels its parent's branch tag by the name its clone derived, and under a structural horizon that name came from the seed. Section 5.7 has the origin check it against its own derivation: the handler now looks at each check-in a push stored, once crosslinked, and when the parent is on another branch here it rolls the whole push back with an error naming the stale seed and the parent's branch, spelled as zit's pushSeedCancelFault spells it. A check-in is admitted when the push or the store holds a control artifact cancelling the parent's real branch on it, and so is one whose parent this origin cannot place. zit's check reads only the push for the correction; this one also reads the store, as the draft allows. Interop runs both ways against zit 9bf7ebfe0f. check-in: 8f89198ca1 user: [email protected] tags: feat/horizon-cones, trunk | |
| 17:53 |
feat(merge): refuse a base below the structural horizon, or deepen to it
Section 5.5 forbids merging against a base the store does not hold. In a structural store the walk can reach a boundary check-in as the base, or find none where both sides' ancestry leaves the store, and the merge then failed on a phantom with an error that never named the horizon. content.MergeBaseCrossing decides the crossing for checkout merges and branch merges alike, and the error names the horizon, the base and the deepen, matching libfossil.ErrBelowHorizon. Checkout.MergeContext is the remedy zit's merge applies: given a peer it asks the origin for the base, deepens to reach it and merges again over the real base. 'repo merge' takes the recovery flags and contacts the remote only when a merge crosses the horizon. check-in: d8458ad960 user: [email protected] tags: feat/horizon-cones, trunk | |
| 17:36 |
feat(horizon): deepen as a verb, discarding the seed for the old C
Section 5.8 makes deepen the universal remedy of the draft: name a C' that is an ancestor of C and synchronize at it. sync.Deepen finds C' as zit's deepenPoint does, the target itself when it is C's ancestor, else a common ancestor over the P cards the store holds or asked of the origin with the merge-base query; it refuses rather than move C off its own ancestry when none can be established. Repo.DeepenHorizon and 'repo cone deepen' expose it, and the crossing diagnostic now names that command. Moving a structural horizon through FetchCone now discards the held seed before the transfer, so the request offers none and the seed for C, applied at a later date, cannot shadow propagation from the seed for C'. A transfer that fails puts the old cone and seed back, as zit's deepen does: a store left structural without a seed derives no branch state for K. check-in: c11b3055fb user: [email protected] tags: feat/horizon-cones, trunk | |