This Day In History For 2027-08-24
1 Year Ago (more context)
|
2026-08-24
| ||
| 21:41 | • Edit [5a35874240]: Add propagating "closed". artifact: a64bdd8fda user: dmestas | |
| 21:41 | merge strata: stacked landings (#144) check-in: 2e060aa7c0 user: dmestas tags: trunk | |
| 20:23 |
landing: pin the stack-depth bound the indent trusts
The cycle panic was in the indent computation, and rewriting it made the panic go away without my being able to explain the arithmetic -- 32*2 into a 64-byte buffer should not overflow. This asserts the bound directly, so the renderer's assumption is checked rather than believed. closed check-in: 5a35874240 user: dmestas tags: strata | |
| 20:10 |
landing: cycles, unresolvable targets, and already-contained branches
Three holes found by walking the scenario space rather than the happy path. A retarget could make a cycle. Every segment in one then refuses forever, each waiting on the next, and 'ls' panicked outright computing an indent for a chain with no bottom. Cycles are now refused, direct and indirect. A retarget accepted a branch name that does not resolve, minting a permanent record aimed at nothing -- the check 'open' has always made before minting one. A branch already contained in its target reported 'did not merge cleanly; resolve and commit'. There was nothing to resolve: 'merge' had said 'already up to date'. The record could then only be closed by rejecting it, recording the opposite of what happened. It now settles as accepted with no merge, which is what is true. Reachable from a stack by landing the top segment first -- it carries the ones below it in. check-in: b450374bce user: dmestas tags: strata | |
| 19:17 |
landing: refuse a landing whose base was rejected or already landed
The ordering guard only looked for an OPEN base, so a rejected one slipped through and the accept SUCCEEDED -- 'accepted seg2 -> seg1', filing the work on a branch nobody would land. Nothing failed and nothing warned; the merge is fine, which is why only a rule catches it. Same hole for a base that already landed, reachable when the auto-retarget after that accept did not run. All three cases now refuse and name the retarget that fixes them. Documented the half of this that surprises: rejecting a landing rejects the proposal, not the code beneath a segment stacked on it. That branch descends from the rejected one and still carries its check-ins, so landing it on trunk lands them too. check-in: f91105346b user: dmestas tags: strata | |
| 19:10 | • Edit [1234842809]: Add propagating "closed". artifact: 11a11717fa user: dmestas | |
| 19:10 | • Edit [b82aacc805]: Add propagating "closed". artifact: e3d81004a7 user: dmestas | |
| 19:10 | • Edit [0d16bc966d]: Add propagating "closed". artifact: cfb8cc2950 user: dmestas | |
| 18:52 |
landing: close the landed leaf without propagating
The propagating form marked every segment ABOVE a landed one closed too: in a stack those check-ins descend from the landed tip, so propagation followed the DAG straight into work that was still open. The landing records stayed open and correct; only 'branch' and 'leaves' lied, which is exactly where you look for what still needs doing. Fossil's own propagating close is safe because it closes a check-in with no descendants. A landed segment is a leaf by the same-branch rule while still having children on the branch above it. check-in: d97b39b272 user: dmestas tags: strata | |
| 18:46 | merge: hardware crc32 and index v3 sorted directory (#143, #145) check-in: 2a676a5df1 user: dmestas tags: trunk | |
| 18:33 |
logstore: index v3 carries a sorted directory, so opening builds no hash map
Adopting an index inserted every entry into a StringHashMap before answering anything: 255,167 keys on a 302MB store, to serve the one or two lookups a tree listing makes. Measured at 5.5ms of a 24ms open by building the map twice and taking the delta. v3 appends entry_count little-endian u32 rids ordered by digest, and lookup binary-searches it -- ~18 comparisons against a quarter-million insertions. Opening tcl drops 24.0ms to 20.0ms; with the hardware crc32 change before it, 38.5ms to 20.0ms. Output byte-identical. The directory carries RIDS and the entry records stay in scan order. Sorting the entries themselves would renumber every rid, and ancestry, phantom snapshots and the sync walk all read that numbering. lookup consults the map first, then the directory: entries appended after adoption -- a tail resume past the index boundary, and every write on a scan-path store -- are only in the map. Every held-check that was reading index.get directly now goes through lookup, because intern runs during a tail resume and a miss there mints a duplicate entry for a digest already held. A v2 sidecar is refused and rewritten rather than adopted, as v1 was: adopting one would leave lookup with neither a map nor a directory and answer null for held artifacts. Pinned by a test that downgrades a v3 blob and re-checksums it. The tests pin what equality cannot: that the map is EMPTY after adoption and every digest still resolves, that a digest which is not held returns null rather than a neighbour -- the failure a binary search has and a map does not -- and that digests written out of sort order still resolve, so a directory that merely mirrored insertion order would fail. Closes #145 closed check-in: 0d16bc966d user: dmestas tags: index-v3-145 | |
| 18:16 |
landing: close the branch an accepted landing lands
Every landed branch stayed an open leaf, which was survivable one landing at a time and is not with stacks: a six-deep stack left seven leaves, and 'leaves' is how you find work that still needs doing. accept now writes fossil's propagating 'closed' tag on the branch it merged. reject does not -- a rejected branch is one the author may revise and propose again, and closing it would decide that for them. Best-effort and last, like the retargets: a leaf that stayed open is untidy, never incorrect, and the diagnostic names the command that fixes it. check-in: c9b0147381 user: dmestas tags: strata | |
| 17:53 |
crc32: fold with ARMv8's crc32 instruction where the target has it
Checksumming was half the cost of opening a repository: 18.8ms of a 37ms `ls` on a 302MB store, across the two sidecars (26.3MB derived-state image at 13.5ms, 9.7MB log index at 5.3ms). Both measured by doubling the work and taking the delta, so the number is the checksum and not its neighbours. ARMv8's crc32b/crc32x implement CRC-32/ISO-HDLC -- the same generator this file is already defined in terms of, in the same reflected convention -- so this is an instruction-count change and never a value change, which is what the module doc says every edit here must be. tcl opens in 24.5ms against 38.5ms, output byte-identical. aarch64 only, deliberately. x86_64's SSE4.2 crc32 instruction is CRC-32C/Castagnoli, a DIFFERENT generator; taking it because the mnemonic matches would orphan every store already written. x86_64 keeps the table path, which stays load-bearing rather than becoming a fallback nobody runs. The new test recomputes CRC-32 as a plain bit-at-a-time recurrence, independent of update and its tables, and holds both paths to it at every length 0..1024 -- all eight residues mod 8, including the lengths below the fold width. The two paths differ only in where the fold stops and the tail begins, so a divergence would appear at particular residues and nowhere else, which is the shape a spot check misses. Chunked updates are pinned too: the store checksums payloads in 4 KiB pieces, so a path correct only for single-shot input would corrupt exactly the largest artifacts. Refs #143 closed check-in: b82aacc805 user: dmestas tags: crc-hardware-143 | |
| 17:40 |
landing: stacked landings (strata)
A landing may target another branch that has its own open landing; that pair is a stack, read from the targets rather than recorded anywhere. No new card, no dependency field -- the edge is already implied. Stacks land bottom-up. accept refuses a landing whose target has its own open landing, and accepting one retargets whatever was stacked on it onto the branch it just landed on. That is the whole restack: a metadata write per child, because a segment's branch already descends from the one below it, so after that lands the merge sees only the segment's own change. Adds 'landing retarget', a target reply resolved by the same latest-wins rule as a state transition. ls indents a stack by depth; show names what a segment sits on, what sits on it, and when its base was rejected. Verified six deep on a shared file, which is the depth the design claim was previously only reasoned about. check-in: 3767f1eafc user: dmestas tags: strata | |
| 16:50 | merge index-borrow-143: adopted index entries slice the blob (#143) check-in: 3c3f936632 user: dmestas tags: trunk | |
| 16:41 |
logstore: adopted index entries slice the blob instead of copying it
Adopting a scan index duplicated every entry hash: 255,167 allocations and copies on a 348MB store to answer, typically, one lookup. The hashes are fixed-length slices of a buffer that was being freed at the end of the open, so the store now holds that buffer for its lifetime and the entries point into it. Opening tcl drops 44.0ms -> 37.5ms; smaller stores scale down with the index. openReadOnlyIndexed takes the blob UNCONDITIONALLY -- on success the store frees it in deinit, on every refusal it frees there. The first cut transferred only on success, both call sites looked right, and the second one double-freed inside commit's autosync. No type and no unit test caught it; only e2e did. A conditional transfer makes every call site restate the rule. Ownership is a prefix, not a per-entry flag: adopted entries come first and a tail resume appends owned ones after them, so borrowed_entries is the split point deinit and the batch rollback both read. The unit test pins that the hashes lie INSIDE the held blob, not merely that they compare equal -- equal bytes prove nothing about where they live, and a future dupe would restore the cost with every assertion still green. This is one part of #143. The CRC that issue names as the largest fixed cost measures 5.2ms of the 44ms (1,860 MB/s over the 9.7MB index), so it is not where the time goes and its integrity check should stay. Refs #143 closed check-in: 1234842809 user: dmestas tags: index-borrow-143 | |
| 16:08 | merge merge-dirty-tree: merge into a dirty working tree (#135) check-in: 944ee007fe user: dmestas tags: trunk | |
| 15:17 |
merge: merge into a dirty working tree instead of refusing it
The current side becomes the working tree, so a divergent path compares what the operator actually has rather than what the check-in says is on disk -- which is the disagreement B-merge-14 existed to avoid. A clean tree scans to no divergences and every side reads from the store exactly as before. Rollback needs no new machinery: captureMergeImages already reads the worktree, so an abandoned merge restores the uncommitted bytes. Also adds the fault-injection coverage the carried update path was missing: a carried update that fails mid-apply must restore the WORKING bytes, not the check-in's. Verified by mutation -- breaking the baseline overlay fails both tests. closed check-in: 0627425be0 user: dmestas tags: merge-dirty-tree | |
| 15:01 | • Edit [e845821a65]: Add propagating "closed". artifact: e8b5a1468d user: dmestas | |
| 14:47 | merge readpath-141: diff and artifact read through the image path (#141) check-in: 9bcc83b530 user: dmestas tags: trunk | |
| 14:36 |
diff and artifact: read through the image path, not a whole-store derive
Both opened `.full`, so each invocation re-derived the entire repository view before reading anything. The cost tracked the STORE, not the work: a one-line diff on a 302MB store took 0.33s, and cost the same whether the diff was 13 lines or 4126. diff read two file lists -- exactly what ls reads at 0.04s -- and artifact never touched the derived view at all, resolving through logStore alone. diff inherited `.full` by omission: it called Repository.open directly instead of going through main.openRepo, which is where every other read verb chooses a mode. Nothing failed, so nothing caught it. fossil 112MB 0.06s -> 0.01s tk 240MB 0.14s -> 0.03s sqlite 236MB 0.25s -> 0.03s tcl 302MB 0.33s -> 0.04s Output byte-identical on all four. readpath_test pins which verbs take which path, and pins the comment justifying each deliberate `.full` -- timeline reads the merge mark off adjacency, annotate walks ancestry. An unexplained `.full` is indistinguishable from this bug. grep is named there as an open question rather than pinned, since it also wires --no-cache, which has to move with the mode. The OpenOptions comment claimed ls, cat, info, branch and tag were `.full`. All five have been fast_read since #98; reading it was how this stayed hidden. Fixes #141 closed check-in: e845821a65 user: dmestas tags: readpath-141 | |
| 12:16 | • Edit [8951bc9d1e]: Add propagating "closed". artifact: ac28ea981b user: dmestas | |
| 12:16 | • Edit [cff154d675]: Add propagating "closed". artifact: 73ee926767 user: dmestas | |
| 12:16 | • Edit [8776c0e5bb]: Add propagating "closed". artifact: 39052a7e46 user: dmestas | |
| 12:16 | • Edit [78d56a2177]: Add propagating "closed". artifact: 6775bbb830 user: dmestas | |
| 12:16 | • Edit [baa15c3362]: Add propagating "closed". artifact: ff8809bb0f user: dmestas | |
| 12:16 | • Edit [23784ef673]: Add propagating "closed". artifact: 1ae386fbc5 user: dmestas | |
| 12:16 | • Edit [aff1d04310]: Add propagating "closed". artifact: 3057fcae0e user: dmestas | |
| 11:54 | • Edit [3e565c1caa]: Add propagating "closed". artifact: d548fdfe13 user: dmestas | |
| 11:31 | • Edit [7fcc9b5ee1]: Add propagating "closed". artifact: 5fffbfe85d user: dmestas | |
| 11:27 | merge update-dirty-tree-135: carry uncommitted work across the move (#135) check-in: 5f6402dd84 user: dmestas tags: trunk | |
| 11:15 | merge landing-title-138: first line of -m is the landing title (#138) check-in: da4fa8dcbe user: dmestas tags: trunk | |
| 11:06 |
landing open: the first line of -m is the title, the rest is the prose
The whole message went into the H card and the body both, so a multi-paragraph -m minted a multi-paragraph heading and carried the same text twice. An H card is one token (§4.7.7). The split is at the first newline, not the first blank line, so a two-line message cannot mint a two-line title either. ls and show read only a title's first line: landings minted before this are immutable, so a reader has to cope with them, not merely stop making more. A message with no title line is refused before the branch lookups and the store scan. Fixes #138 closed check-in: 3e565c1caa user: dmestas tags: landing-title-138 | |
| 10:16 |
update: carry uncommitted work across the move instead of refusing it
A dirty tree is now three-way merged (current check-in, working file, target) rather than rejected, so a branch tip that advanced under uncommitted work can be joined. Previously update, merge and stash all refused that state, leaving commit --allow-fork as the only exit -- the guard that keeps forks deliberate was manufacturing them. The fork guard itself is unchanged. applyCheckinWith scans the tree once and calls back for the merge policy; handing carries in instead would scan every clean update twice, which cost a 20k-file no-op update 63%. Paths that cannot merge either way (type change, edited here / deleted there) are named and refused before anything mutates. Fixes #135 closed check-in: 7fcc9b5ee1 user: dmestas tags: update-dirty-tree-135 | |
| 02:26 | merge merge-probe: mergeability for a caller-named pair (#134) check-in: 83734fcc3d user: dmestas tags: trunk | |
| 02:17 |
merge --probe: mergeability for a pair the caller names (#134)
landing show answers the same question but must find the record first, which is a full-store walk. A consumer with its own index already knows the record and both tips; --probe lets it ask about the pair and pay only for the merge. Release build on a 20MB store: 0.01s against 0.90s. Writes nothing, never autosyncs, and exits 0 whatever the answer -- 'has conflicts' is an answer, not a failure, so a caller reads the line rather than the status. Being a read it takes -R, and then --into is required because there is no checkout to supply the other side. The probe moves out of landing and next to the merge engine: one implementation, three callers. A real merge now refuses -R by name instead of failing as a discovery miss that tells you to pass the flag you just passed. closed check-in: aff1d04310 user: dmestas tags: merge-probe | |
| 01:35 | merge diff-ignore-ws: zit diff -w ignores whitespace-only changes (#130) check-in: f863b8d6ef user: dmestas tags: trunk | |
| 00:52 |
diff: -w ignores whitespace-only changes (#130)
Lines differing only in whitespace compare equal, so a reindent or a CRLF/LF normalization produces no hunks and drops the file from --brief entirely. Comparison only: a line that does survive is emitted with its original bytes. --brief reads content under -w, which it otherwise never does, and runs the same unified() the hunk path runs so the two cannot disagree about whether a file changed. A no-hunk result now claims 'mode changed' only on real byte equality -- with -w it also means a difference that was ignored. closed check-in: 23784ef673 user: dmestas tags: diff-ignore-ws | |
| 00:41 | merge ncard-rule: fossil's per-type N card write rule (#136) check-in: 7f9f9f8927 user: dmestas tags: trunk | |
| 00:31 |
N card: follow fossil's per-type write rule (#136)
landing hardcoded N text/plain with no way to say otherwise, overriding the text/x-fossil-wiki default of artifact-format 4.7.12 on every record it wrote. It now takes --mimetype and, minting a forum artifact, follows forum's rule. The wider bug was in what landing copied. Verified against fossil's source: wiki (wiki.c:2084) and forum (forum.c:1784) write N only when the mimetype is present, non-empty and NOT the default; technote (event.c:284) writes it whenever non-empty, with no suppression. zit wrote it unconditionally for all three, so an explicit --mimetype text/x-fossil-wiki minted different bytes, and a different artifact id, than fossil produces from the same input. The draft states the READ default, not when the card is written. Only the source shows the suppression, and only the source shows that the three types disagree -- so tests pin the divergence rather than tidying it away. closed check-in: baa15c3362 user: dmestas tags: ncard-rule | |
| 00:04 | • Edit [98ddd81b75]: Edit check-in comment. artifact: f264ba4fa3 user: dmestas | |