37 descendants and 50 ancestors of 7d9536347a06b914
|
2026-10-23
| ||
| 02:05 | conform(horizon): HZ-3.4-1 waits on a wire form, not on step 3 leaf 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: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: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: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 | |
| 17:28 |
feat(sync): answer and ask the horizon merge-base query
A structural clone cannot compute a merge base whose ancestry lies below its horizon, so draft-fossil-horizon-00 section 6.2.4 lets it ask the origin with pragma horizon-mergebase A B. The origin answers in its pull reply, ahead of the igots where zit places it, with the base or - for no common ancestor, and only for check-ins it holds. Section 8 bounds the walk: merge.FindCommonAncestorWithin carries the held-set walk's visited-node bound and the query goes unanswered past it. The client asks once, in the first request after the cursor, through SyncOpts.MergeBaseOf and reports the answer in the result. Interop runs both ways against zit 9bf7ebfe0f. check-in: 5612ac9771 user: [email protected] tags: feat/horizon-cones, trunk | |
| 17:22 | conform(horizon): HZ-5.4-1 covered; changelog for the horizon in history check-in: cf2509521e user: [email protected] tags: feat/horizon-cones, trunk | |
| 17:21 |
Merge branch 'wip/horizon-render' into feat/horizon-cones
History views render a labelled horizon boundary as the horizon (draft-fossil-horizon-00 §5.4): Repo.Ancestry ends there without an error, Repo.Timeline and LogEntry report it in HorizonParents, and repo timeline prints it as (horizon). An unlabelled absent parent renders as before. check-in: d0c44dbc25 user: [email protected] tags: feat/horizon-cones, trunk | |
| 17:21 |
feat(history): render a horizon boundary as the horizon
A parent the origin labelled below a structural horizon is the horizon itself (draft-fossil-horizon-00 5.4), but the history views showed it as a bare parent hash, and the ancestry walk tried to read an event for it. LogEntry and TimelineEntry gain HorizonParents, the subset of Parents that carries a boundary label; Ancestry ends at such a parent without an error; repo timeline prints it as "parent: <hash> (horizon)". An absent parent that is not labelled is transit and renders as before. check-in: ee3ccc7c67 user: [email protected] tags: feat/horizon-cones, trunk | |
| 17:02 | docs: the structural horizon in the changelog and the sparse page check-in: 43d6d7f88b user: [email protected] tags: feat/horizon-cones, trunk | |
| 17:01 |
Merge branch 'wip/horizon-structural' into feat/horizon-cones
The structural horizon (draft-fossil-horizon-00 Appendix A step 5): a store at structural depth elides manifests and nodes below the horizon and keeps boundary labels; the client sends horizon-structural and horizon-seed and holds the seed it is sent; the origin restricts manifests to the held set, labels the boundary, mints and sends the seed, and serves structural only when the seed is current. Interoperates with zit at 9bf7ebfe0f in both directions. check-in: 78ed31bf91 user: [email protected] tags: feat/horizon-cones, trunk | |
| 16:46 |
fix(sync): decide a structural store's depth only on a pull's reply
A push alone carries no horizon pragma, so its reply says nothing about how the horizon is served, and a structural store pushing to its origin fell back to the content horizon. The first reply decides only when the request pulled. Preconditions of the structural serving and receiving paths are asserted. check-in: 1abe24cd2b user: [email protected] tags: feat/horizon-cones, trunk | |
| 16:30 |
fix(sync): ask for what a peer announces to a structural store
A structural store elides a phantom nothing it holds names, and an igot makes exactly such a phantom, so a structural store serving a push never asked for the pushed check-in and a pull relied on the session alone to want it. A name the peer announced in the exchange is now asked for when nothing held names it, on both sides; a labelled boundary or a tag's below-horizon target stays elided. check-in: 26aff82ccc user: [email protected] tags: feat/horizon-cones, trunk | |
| 16:28 |
fix(content): take the nodes a structural origin withholds outside the cone
Composed with path members, a structural horizon keeps K's trees, but an origin may withhold their nodes outside the cone, as zit's does since 2b635ea5ce. Those nodes classified transit, so a clone from zit stalled waiting on them. A node the path members exclude is now elided under a structural horizon, as a path cone elides it; zit interop under cone src proves it both ways. check-in: 74ac2ea4c1 user: [email protected] tags: feat/horizon-cones, trunk | |
| 16:25 |
test(conformance): cover the structural horizon's requirements
Cases for the ordinary commit from a structural store (§5.7), the leaf verdict over one (§3.1), verification and crossing (§5.4, §5.5), and the requirements table updated to what the structural horizon now covers; what remains, with the Appendix A step that covers it, stays in the notes. check-in: d9e74912e8 user: [email protected] tags: feat/horizon-cones, trunk | |
| 16:25 |
feat: expose the structural horizon and name it where history ends
Horizon gains Structural, whose zero value is the content horizon, so a Horizon built before it means what it meant; clone and sync results say how many replies served the structural level and whether the store fell back. repo clone and repo cone store take --structural with --horizon. A structural store's history ends at its horizon, which the commands now say rather than leave bare phantoms (draft-fossil-horizon-00 §5.4, §5.5): repo info names the horizon and the check-ins below it, repo verify counts them apart from damage, and checkout, diff and annotate of a check-in below C fail naming the horizon and the deepen that reaches it. check-in: c61a17d9ab user: [email protected] tags: feat/horizon-cones, trunk | |
| 16:14 |
test(conformance): the structural horizon between go-libfossil and zit
zit clones and pulls at a structural horizon from go-libfossil's origin, and go-libfossil clones and pulls at one from zit serve. Each origin withholds the manifests below the horizon, labels the boundary and sends the seed; each client stays structural and derives the branch, leaf and tag state of the held set as a dense clone of the same origin does, including after a tag below the horizon moves the state at an entry and re-seeds it. zit holds the very seed go-libfossil mints. check-in: b76863738f user: [email protected] tags: feat/horizon-cones, trunk | |
| 16:12 |
feat(sync): serve and take the structural horizon
The origin now answers pragma horizon-structural (draft-fossil-horizon-00 §6.1, §6.2): it keeps only the manifests and nodes the held set reaches, labels every boundary hash at the head of each reply (§6.2.2), mints the seed for C and sends it, pragma ahead of its file card, when the client offers another name (§6.2.3), and advertises the structural level only then; a request it cannot place or seed is served as content. It refuses a seed offered to it and withholds a gimme'd manifest below the horizon. A structural store's requests carry horizon-structural and the seed it holds. It routes the announced card to seed.Hold after checking its name and shape, never storing or announcing it, records the labels, and applies the seed after each reply and after the closing link. A first reply that does not serve structural with a seed, or a seed failing provenance, makes it a content-horizon store for good, reported in the result, and the manifests below the horizon are fetched (§5.1, §5.2.4, §6.3). check-in: dc33bca3a6 user: [email protected] tags: feat/horizon-cones, trunk | |
| 16:12 |
fix(manifest): link a structural store's check-ins against absent parents
A sparse store defers a check-in whose parent it lacks, so that mlinks wait on a complete parent tree. Under a structural horizon (draft -fossil-horizon-00 §5.1) the parents below the horizon never arrive, so C and every entry of K stayed unlinked. An absent source no longer defers there; the check-in links against the phantom as a dense store links it. check-in: 79ec72f88e user: [email protected] tags: feat/horizon-cones, trunk | |
| 16:12 |
fix(horizon/seed): apply the seed only to linked check-ins
The seed's rows name their target as their source, and the crosslink treats a blob that sources a tagxref row as already linked. A sync stores an entry before it links it, so a seed applied in between left the entry unlinked for good: no event, no plink, no leaf. Apply now skips a target the store has not linked; the sync layer applies again after the round that links it. check-in: c7c0c130bd user: [email protected] tags: feat/horizon-cones, trunk | |
| 15:49 |
feat(content): hold and classify a structural horizon store
A structural-horizon clone (draft-fossil-horizon-00 §5.1) keeps only the manifests and nodes its held set reaches, so its phantoms need classes the content horizon never had: a parent the origin labelled below the horizon is elided, an unlabelled one stays transit because it may only be late (§5.3), and a phantom only a tag or cherry-pick names is a manifest below the horizon nothing in K reaches. SetStoreCone stops refusing the depth, and keeps the boundary labels beside the store cone, dropping them when the horizon moves since they name what lies below one C. check-in: 31b140507a user: [email protected] tags: feat/horizon-cones, trunk | |
| 15:33 |
Merge branch 'wip/horizon-docs' into feat/horizon-cones
A docs page for sparse cones and horizons: concepts, the library API, the CLI, interop with zit and fossil 2.28, and the limits as they stand. check-in: 441604df52 user: [email protected] tags: feat/horizon-cones, trunk | |
| 15:33 |
docs(site): state the leaves rule as the draft now defines it
The page described the leaves rule as each childless check-in's ancestry back to its meeting point with C, the rule before zit#265. The rule at the pinned draft holds every descendant of each meeting point, which is why it holds nearly all of fossil-scm.org's history: old unmerged branches fork near the root. check-in: 097153487d user: [email protected] tags: feat/horizon-cones, trunk | |
| 15:33 | docs(site): add sparse cones and horizons page check-in: 8be82ed25d user: [email protected] tags: feat/horizon-cones, trunk | |
| 15:27 | docs(changelog): the horizon seed check-in: 53f9af98e3 user: [email protected] tags: feat/horizon-cones, trunk | |
| 15:26 |
Merge branch 'wip/horizon-seed' into feat/horizon-cones
The structural horizon's seed (draft-fossil-horizon-00 §5.2): an origin mints a control artifact carrying the tags in force at every entry of the held set, a clone holds it outside the artifact store where no sync, clone or rebuild can reach it, and the derivation applies it so every check-in in K derives as it does densely. Byte-identical to zit's seed when the latest summarized event is on a whole second. Conflicts in the horizon suite and table kept both sides; the seed's zit test now probes for a horizon-capable zit like the other horizon interop tests. check-in: 12dcbdeed6 user: [email protected] tags: feat/horizon-cones, trunk | |
| 15:20 |
feat(horizon): write the seed's D card to the second, as zit does
deck.Marshal writes D to the millisecond, so a go-libfossil seed could never match zit's bytes or name even over identical state. Mint now writes D as YYYY-MM-DDTHH:MM:SS, keeping the round-up to the next whole second that Section 5.2.2's "no earlier than the latest tag event" needs, and recomputes Z over the body. Over whole-second tag events the seed is now byte-identical to zit's, which the interop test asserts for the full seed and its name. check-in: 038e50befb user: [email protected] tags: feat/horizon-cones, trunk | |
| 15:08 | docs(changelog): the horizon cursor check-in: 303e4c28b7 user: [email protected] tags: feat/horizon-cones, trunk | |
| 15:08 |
fix(sync): key the horizon cursor by the HTTP transport's URL
The cursor slice keeps a client's horizon cursor per origin, named by the internal SyncOpts.Origin, but the public Repo.Sync never set it, so a library or CLI sync kept no cursor and every request announced in full. The HTTP transport now names its peer by its URL with any password removed, as last-sync-url keeps it, and Sync passes that through. Other transports name no origin and keep announcing in full, which §6.2.6 allows. The test pulls with a credential-bearing URL and finds the cursor under the password-free one, and no config row holding the password. check-in: 331b540f1c user: [email protected] tags: feat/horizon-cones, trunk | |
| 15:06 | test(conformance): probe zit for horizon support in the cursor interop check-in: 8135205010 user: [email protected] tags: feat/horizon-cones, trunk | |
| 15:06 |
Merge branch 'wip/horizon-cursor' into feat/horizon-cones
The horizon cursor (draft-fossil-horizon-00 §6.2.6): an origin that classified a request answers pragma horizon-cursor with its position, a converged client records it per origin and cone and sends it back, and neither side announces again what the other already has. Interoperates with zit both ways. check-in: f704c6d58b user: [email protected] tags: feat/horizon-cones, trunk | |
| 15:06 | Merge branch 'feat/horizon-cones' into wip/horizon-cursor check-in: ec8b4c770b user: [email protected] tags: feat/horizon-cones, trunk | |
| 15:05 |
feat(sync): horizon cursor, origin and client
A horizon pull announced the client's whole holding with igot on every request, and the origin answered with its whole kept inventory: one card per artifact, both ways, on every pull. draft-fossil-horizon-00 §6.2.6 replaces both with a position. The origin values pragma horizon-cursor right after horizon-served in every reply to a horizon pull, as zit places it. Its position is rid order (the clone SEQNO sense), stopped short of the lowest phantom: a filled phantom keeps its old rid, so a cursor covering it would hide the arrival. Receiving a cursor, any reply that pulls announces nothing at or below it. A filled phantom that a check-in from an earlier receipt already named as parent is recorded as the reclassification position; a cursor below it is answered as if absent. A cursor beyond the origin's own position is ignored. The client keeps, per origin and cone, the last reply's cursor and the rid count the origin holds through, sends the cursor on every request, announces nothing on a pull-only request and only what it appended on one that sends. Sessions name their origin with SyncOpts.Origin; without it nothing is kept and every request announces in full. check-in: b61bd3475c user: [email protected] tags: feat/horizon-cones, trunk | |
| 14:48 | docs(changelog): the public horizon API check-in: 76d780fde1 user: [email protected] tags: feat/horizon-cones, trunk | |
| 14:48 |
test(conformance): skip horizon interop on a zit that predates horizons
TestZitHorizonInterop took whatever zit was on PATH. A zit release from trunk reports the same version as the horizon-rfc build but has no --horizon flag, so the test failed in zit's argument parser. The new zitHorizonBinary probes the binary for horizon support, as zitBinary already probes for send-submanifests, and skips it, or fails the run under REQUIRE_ZIT_BIN=1, with the reason. check-in: 27b85364f2 user: [email protected] tags: feat/horizon-cones, trunk | |
| 14:48 |
Merge branch 'wip/horizon-api' into feat/horizon-cones
The public horizon API in the shape approved: a horizon is part of Cone (NewHorizonCone, Cone.Horizon, Cone.WithHorizon), store cones carry it through SetStoreCone and SetStoreConeContext and the new CloneOpts.StoreCone, checkout cones refuse it with ErrHorizonCheckout, and the CLI takes --horizon and --leaves on repo clone and repo cone store and --no-horizon on repo cone store. check-in: 964b91f682 user: [email protected] tags: feat/horizon-cones, trunk | |
| 14:47 |
feat(horizon): expose content horizons in the public API and CLI
A store cone could carry a horizon only through internal/cone, so library users and the CLI had no way to clone into or edit a content-horizon store. Add Horizon, HorizonRule, NewHorizonCone, Cone.Horizon and Cone.WithHorizon over the internal cone (always the content cut), carry it through SetStoreCone/SetStoreConeContext (deepening fetches what the older check-in keeps) and CloneOpts.StoreCone, and add --horizon, --leaves, --no-horizon and --cone to repo clone and repo cone store. A horizon is a property of the store, not of a checkout (draft-fossil-horizon-00 3.2), so checkout cones carrying one are refused with ErrHorizonCheckout, and widening the store from a checkout cone keeps the store's horizon. check-in: e5904fc85b user: [email protected] tags: feat/horizon-cones, trunk | |
| 14:33 | docs(changelog): horizon serving check-in: b59ba60848 user: [email protected] tags: feat/horizon-cones, trunk | |
| 14:33 |
Merge branch 'wip/horizon-serve' into feat/horizon-cones
Appendix A step 3 without the cursor: the client sends pragma horizon, the origin computes the held set over its dense graph, sends only the blobs above the horizon, inflates crossing deltas, sends no cluster, answers horizon-served content and refuses what it cannot cover (§6.4). Against a horizon-ignorant origin the client requests what its clusters name, as transit (§4.5 after zit#266). Interoperates with zit both ways. check-in: 84be5ad824 user: [email protected] tags: feat/horizon-cones, trunk | |