2026-01-22 11:52.31: New job: test mirage/irmin https://github.com/mirage/irmin.git#refs/pull/2390/head (b344b2f5024aa807916d7980d9d67d5b6ab4efbd) (linux-x86_64:(lint-fmt))Base: ocaml/opam:debian-13-ocaml-4.08@sha256:7cdc2a1943ac1462f548be7816c6d02f14e605659f225027208abe04795ea500ocamlformat version: version 0.27.0 (from opam)To reproduce locally:git clone --recursive "https://github.com/mirage/irmin.git" && cd "irmin" && git fetch origin "refs/pull/2390/head" && git reset --hard b344b2f5cat > Dockerfile <<'END-OF-DOCKERFILE'FROM ocaml/opam:debian-13-ocaml-4.08@sha256:7cdc2a1943ac1462f548be7816c6d02f14e605659f225027208abe04795ea500USER 1000:1000RUN cd ~/opam-repository && (git cat-file -e 873cb18c37b308713d11ad3894c4bb78d73fb3e7 || git fetch origin master) && git reset -q --hard 873cb18c37b308713d11ad3894c4bb78d73fb3e7 && git log --no-decorate -n1 --oneline && opam update -uRUN opam depext -i duneWORKDIR /srcRUN opam depext -i ocamlformat=0.27.0COPY --chown=1000:1000 . /src/RUN opam exec -- dune build @fmt --ignore-promoted-rules || (echo "dune build @fmt failed"; exit 2)END-OF-DOCKERFILEdocker build .END-REPRO-BLOCK2026-01-22 11:52.31: Using cache hint "mirage/irmin-ocaml/opam:debian-13-ocaml-4.08@sha256:7cdc2a1943ac1462f548be7816c6d02f14e605659f225027208abe04795ea500-debian-13-4.08_opam-2.5-ocamlformat-873cb18c37b308713d11ad3894c4bb78d73fb3e7"2026-01-22 11:52.31: Using OBuilder spec:((from ocaml/opam:debian-13-ocaml-4.08@sha256:7cdc2a1943ac1462f548be7816c6d02f14e605659f225027208abe04795ea500)(user (uid 1000) (gid 1000))(run (cache (opam-archives (target /home/opam/.opam/download-cache)))(network host)(shell "cd ~/opam-repository && (git cat-file -e 873cb18c37b308713d11ad3894c4bb78d73fb3e7 || git fetch origin master) && git reset -q --hard 873cb18c37b308713d11ad3894c4bb78d73fb3e7 && git log --no-decorate -n1 --oneline && opam update -u"))(run (cache (opam-archives (target /home/opam/.opam/download-cache)))(network host)(shell "opam depext -i dune"))(workdir /src)(run (cache (opam-archives (target /home/opam/.opam/download-cache)))(network host)(shell "opam depext -i ocamlformat=0.27.0"))(copy (src .) (dst /src/))(run (shell "opam exec -- dune build @fmt --ignore-promoted-rules || (echo \"dune build @fmt failed\"; exit 2)")))2026-01-22 11:52.31: Waiting for resource in pool OCluster2026-01-22 11:52.31: Waiting for worker…2026-01-22 11:57.17: Got resource from pool OClusterBuilding on asteria.caelum.ci.devAll commits already cachedHEAD is now at b344b2f502 Add benchmark documentation and visualization(from ocaml/opam:debian-13-ocaml-4.08@sha256:7cdc2a1943ac1462f548be7816c6d02f14e605659f225027208abe04795ea500)2026-01-22 11:57.18 ---> using "4c1f60dc0cc5644c6a4c05cf3315bdbf9cc478b70e443b212a1220385bab8bba" from cache/: (user (uid 1000) (gid 1000))/: (run (cache (opam-archives (target /home/opam/.opam/download-cache)))(network host)(shell "cd ~/opam-repository && (git cat-file -e 873cb18c37b308713d11ad3894c4bb78d73fb3e7 || git fetch origin master) && git reset -q --hard 873cb18c37b308713d11ad3894c4bb78d73fb3e7 && git log --no-decorate -n1 --oneline && opam update -u"))From https://github.com/ocaml/opam-repository* branch master -> FETCH_HEADa6b2f19780..5bcf75c1ac master -> origin/master873cb18c37 Merge pull request #29216 from shonfeder/release-dune-3.21.0<><> Updating package repositories ><><><><><><><><><><><><><><><><><><><><><><>[default] Initialiseddefault (at git+file:///home/opam/opam-repository):[INFO] opam 2.1 and 2.2 include many performance and security improvements over 2.0; please consider upgrading (https://opam.ocaml.org/doc/Install.html)Everything as up-to-date as possible (run with --verbose to show unavailable upgrades).However, you may "opam upgrade" these packages explicitly, which will ask permission to downgrade or uninstall the conflicting packages.Nothing to do.# Run eval $(opam env) to update the current shell environment2026-01-22 11:57.18 ---> using "72a7cd495abee3d0659c15140c96fff42d035222fc9b794ad453f1a5292cd6f1" from cache/: (run (cache (opam-archives (target /home/opam/.opam/download-cache)))(network host)(shell "opam depext -i dune"))# Detecting depexts using vars: arch=x86_64, os=linux, os-distribution=debian, os-family=debian# No extra OS packages requirements found.# All required OS packages found.# Now letting opam install the packagesThe following actions will be performed:- install dune 3.21.0<><> Gathering sources ><><><><><><><><><><><><><><><><><><><><><><><><><><><><>[dune.3.21.0] found in cache<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><>-> installed dune.3.21.0Done.# Run eval $(opam env) to update the current shell environment2026-01-22 11:57.18 ---> using "3b5aee51bb4636e6dab95deb6944478b896d0b06f021712424b773f7d70073ac" from cache/: (workdir /src)/src: (run (cache (opam-archives (target /home/opam/.opam/download-cache)))(network host)(shell "opam depext -i ocamlformat=0.27.0"))# Detecting depexts using vars: arch=x86_64, os=linux, os-distribution=debian, os-family=debian# No extra OS packages requirements found.# All required OS packages found.# Now letting opam install the packagesThe following actions will be performed:- install sexplib0 v0.14.0 [required by base]- install ocamlbuild 0.16.1 [required by fpath, astring, uuseg]- install cmdliner 1.3.0 [required by ocamlformat]- install either 1.0.0 [required by ocamlformat-lib]- install menhirLib 20250912 [required by ocamlformat-lib]- install csexp 1.5.2 [required by ocamlformat]- install dune-build-info 3.21.0 [required by ocamlformat-lib]- install camlp-streams 5.0.1 [required by ocamlformat-lib]- install seq base [required by re]- install menhirSdk 20250912 [required by ocamlformat-lib]- install fix 20250919 [required by ocamlformat-lib]- install menhirCST 20250912 [required by menhir]- install ocamlfind 1.9.8 [required by ocp-indent, astring, fpath, uuseg]- install ocaml-version 4.0.3 [required by ocamlformat-lib]- install dune-configurator 3.21.0 [required by base]- install re 1.11.0 [required by ocamlformat]- install menhir 20250912 [required by ocamlformat-lib]- install topkg 1.1.1 [required by fpath, astring, uuseg]- install ocp-indent 1.9.0 [required by ocamlformat-lib]- install base v0.14.3 [required by ocamlformat-lib]- install uutf 1.0.4 [required by ocamlformat-lib]- install astring 0.8.5 [required by ocamlformat-lib]- install stdio v0.14.0 [required by ocamlformat-lib]- install uucp 15.0.0 [required by uuseg]- install fpath 0.7.3 [required by ocamlformat-lib]- install uuseg 15.0.0 [required by ocamlformat-lib]- install ocamlformat-lib 0.27.0 [required by ocamlformat]- install ocamlformat 0.27.0===== 28 to install =====<><> Gathering sources ><><><><><><><><><><><><><><><><><><><><><><><><><><><><>[astring.0.8.5] found in cache[base.v0.14.3] found in cache[camlp-streams.5.0.1] found in cache[cmdliner.1.3.0] found in cache[csexp.1.5.2] found in cache[dune-build-info.3.21.0] found in cache[dune-configurator.3.21.0] found in cache[either.1.0.0] found in cache[fix.20250919] found in cache[fpath.0.7.3] found in cache[menhir.20250912] found in cache[menhirCST.20250912] found in cache[menhirLib.20250912] found in cache[menhirSdk.20250912] found in cache[ocaml-version.4.0.3] found in cache[ocamlbuild.0.16.1] found in cache[ocamlfind.1.9.8] found in cache[ocamlformat.0.27.0] found in cache[ocamlformat-lib.0.27.0] found in cache[ocp-indent.1.9.0] found in cache[re.1.11.0] found in cache[sexplib0.v0.14.0] found in cache[stdio.v0.14.0] found in cache[topkg.1.1.1] found in cache[uucp.15.0.0] found in cache[uuseg.15.0.0] found in cache[uutf.1.0.4] found in cache<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><>-> installed seq.base-> installed camlp-streams.5.0.1-> installed cmdliner.1.3.0-> installed csexp.1.5.2-> installed either.1.0.0-> installed fix.20250919-> installed menhirCST.20250912-> installed menhirLib.20250912-> installed menhirSdk.20250912-> installed ocaml-version.4.0.3-> installed sexplib0.v0.14.0-> installed re.1.11.0-> installed dune-build-info.3.21.0-> installed ocamlfind.1.9.8-> installed dune-configurator.3.21.0-> installed ocamlbuild.0.16.1-> installed ocp-indent.1.9.0-> installed topkg.1.1.1-> installed base.v0.14.3-> installed astring.0.8.5-> installed uutf.1.0.4-> installed stdio.v0.14.0-> installed menhir.20250912-> installed fpath.0.7.3-> installed uucp.15.0.0-> installed uuseg.15.0.0-> installed ocamlformat-lib.0.27.0-> installed ocamlformat.0.27.0Done.<><> ocp-indent.1.9.0 installed successfully ><><><><><><><><><><><><><><><><><>=> This package requires additional configuration for use in editors. Install package 'user-setup', or manually:* for Emacs, add these lines to ~/.emacs:(add-to-list 'load-path "/home/opam/.opam/4.08/share/emacs/site-lisp")(require 'ocp-indent)* for Vim, add this line to ~/.vimrc:set rtp^="/home/opam/.opam/4.08/share/ocp-indent/vim"# Run eval $(opam env) to update the current shell environment2026-01-22 11:57.18 ---> using "2ae43ae662b95641fa2bf73d980051b9145f20314d85ad107c6a3257b77df4e6" from cache/src: (copy (src .) (dst /src/))2026-01-22 11:57.21 ---> saved as "65c0da25eed5111c56beed8c806b2283ee08e5ce1aeb497edcca1b3e033837bb"/src: (run (shell "opam exec -- dune build @fmt --ignore-promoted-rules || (echo \"dune build @fmt failed\"; exit 2)"))Warning: Invalid documentation comment:File "src/irmin-pack/layout.ml", line 97, characters 27-50:'{v ... v}' (verbatim text) should begin on its own line.Warning: Invalid documentation comment:File "src/irmin-pack/layout.ml", line 97, characters 50-51:Paragraph should begin on its own line.Warning: Invalid documentation comment:File "test/irmin-graphql/common.mli", line 88, character 16 to line 92, character 6:'{[...]}' (code block) should begin on its own line.Warning: Invalid documentation comment:File "test/irmin-graphql/common.mli", line 98, character 16 to line 102, character 6:'{[...]}' (code block) should begin on its own line.Warning: Invalid documentation comment:File "test/irmin-graphql/common.mli", line 104, character 24 to line 108, character 6:'{[...]}' (code block) should begin on its own line.Warning: Invalid documentation comment:File "test/irmin-graphql/common.mli", line 114, character 16 to line 118, character 6:'{[...]}' (code block) should begin on its own line.File "src/irmin/irmin.ml", line 1, characters 0-0:diff --git a/_build/default/src/irmin/irmin.ml b/_build/default/src/irmin/.formatted/irmin.mlindex 6750ee5..ce15365 100644--- a/_build/default/src/irmin/irmin.ml+++ b/_build/default/src/irmin/.formatted/irmin.ml@@ -181,8 +181,8 @@ structendmodule Of_backend = Store.Make-module Tree = Tree+module type Tree = Tree.Smodule type S = Store.SFile "src/irmin/tree_intf.ml", line 1, characters 0-0:diff --git a/_build/default/src/irmin/tree_intf.ml b/_build/default/src/irmin/.formatted/tree_intf.mlindex 6c2b48f..80cec03 100644--- a/_build/default/src/irmin/tree_intf.ml+++ b/_build/default/src/irmin/.formatted/tree_intf.ml@@ -413,7 +413,8 @@ module type S = sigval reset_counters : unit -> unitval inspect :- t -> [ `Contents | `Node of [ `Map | `Key | `Value | `Portable_dirty | `Pruned ] ]+ t ->+ [ `Contents | `Node of [ `Map | `Key | `Value | `Portable_dirty | `Pruned ] ](** [inspect t] is similar to {!kind}, with additional state information fornodes. It is primarily useful for debugging and testing.@@ -432,8 +433,8 @@ module type Sigs = sigval set_inline_contents_enabled : bool -> unit(** [set_inline_contents_enabled b] controls whether small contents areinlined directly in nodes. When [true], contents smaller than the- configured threshold will be inlined. Default is [false]. This is a- global setting that should be set before creating stores. *)+ configured threshold will be inlined. Default is [false]. This is a global+ setting that should be set before creating stores. *)val set_inline_contents_max_bytes : int -> unit(** [set_inline_contents_max_bytes n] sets the maximum serialized size inFile "src/irmin-pack/conf.mli", line 1, characters 0-0:diff --git a/_build/default/src/irmin-pack/conf.mli b/_build/default/src/irmin-pack/.formatted/conf.mliindex 6d3ffc0..7ead362 100644--- a/_build/default/src/irmin-pack/conf.mli+++ b/_build/default/src/irmin-pack/.formatted/conf.mli@@ -135,14 +135,16 @@ val inline_contents : Irmin.Backend.Conf.t -> boolWhen enabled, content values smaller than 16 bytes (serialized) are storeddirectly within the parent node entry rather than as separate pack entries.- This reduces storage overhead and improves read performance for small values.+ This reduces storage overhead and improves read performance for small+ values.Note: Enabling this option changes the hash computation of nodes, makingstores incompatible with stores created with inlining disabled forhash-based comparisons.- See the {{:./doc/irmin-pack/design/inline_contents.md}inline contents design- doc} for more details. *)+ See the+ {{:./doc/irmin-pack/design/inline_contents.md}inline contents design doc}+ for more details. *)val switch : Irmin.Backend.Conf.t -> Eio.Switch.t(** Eio switch *)File "src/irmin/irmin.mli", line 1, characters 0-0:diff --git a/_build/default/src/irmin/irmin.mli b/_build/default/src/irmin/.formatted/irmin.mliindex 2f57bf9..6259c34 100644--- a/_build/default/src/irmin/irmin.mli+++ b/_build/default/src/irmin/.formatted/irmin.mli@@ -82,8 +82,8 @@ module Tree : sigval set_inline_contents_enabled : bool -> unit(** [set_inline_contents_enabled b] controls whether small contents areinlined directly in nodes. When [true], contents smaller than the- configured threshold will be inlined. Default is [false]. This is a- global setting that should be set before creating stores. *)+ configured threshold will be inlined. Default is [false]. This is a global+ setting that should be set before creating stores. *)val set_inline_contents_max_bytes : int -> unit(** [set_inline_contents_max_bytes n] sets the maximum serialized size in@@ -93,13 +93,14 @@ module Tree : sigval get_inline_contents_max_bytes : unit -> int(** [get_inline_contents_max_bytes ()] returns the current threshold. *)- module Make (B : Backend.S) : Tree_intf.S- with type path = B.Node.Path.t- and type step = B.Node.Path.step- and type metadata = B.Node.Metadata.t- and type contents = B.Contents.value- and type contents_key = B.Contents.Key.t- and type hash = B.Hash.t+ module Make (B : Backend.S) :+ Tree_intf.S+ with type path = B.Node.Path.t+ and type step = B.Node.Path.step+ and type metadata = B.Node.Metadata.t+ and type contents = B.Contents.value+ and type contents_key = B.Contents.Key.t+ and type hash = B.Hash.tendmodule Metadata = MetadataFile "src/irmin-pack/inode_intf.ml", line 1, characters 0-0:diff --git a/_build/default/src/irmin-pack/inode_intf.ml b/_build/default/src/irmin-pack/.formatted/inode_intf.mlindex 8d54e52..f2d4d6f 100644--- a/_build/default/src/irmin-pack/inode_intf.ml+++ b/_build/default/src/irmin-pack/.formatted/inode_intf.ml@@ -118,10 +118,12 @@ module type Compress = sigtype address = Offset of pack_offset | Hash of hashtype ptr = { index : dict_key; hash : address }type tree = { depth : dict_key; length : dict_key; entries : ptr list }+type value =| Contents of name * address * metadata| Contents_inlined_value of name * string * metadata| Node of name * address+type v = Values of value list | Tree of treetype v1 = { mutable length : int; v : v }File "src/irmin/node.ml", line 1, characters 0-0:diff --git a/_build/default/src/irmin/node.ml b/_build/default/src/irmin/.formatted/node.mlindex d2cb36e..bcd103d 100644--- a/_build/default/src/irmin/node.ml+++ b/_build/default/src/irmin/.formatted/node.ml@@ -182,7 +182,8 @@ struct| Node n -> (n.name, `Node (n.node, n.inlined))| Contents c -> (c.name, `Contents (c.contents, Metadata.default))| Contents_m c -> (c.name, `Contents (c.contents, c.metadata))- | Contents_inlined_value c -> (c.name, `Contents_inlined (c.value, c.metadata))+ | Contents_inlined_value c ->+ (c.name, `Contents_inlined (c.value, c.metadata))| Node_hash _ | Contents_hash _ | Contents_m_hash _| Contents_inlined_hash _ ->(* Not reachable after [Portable.of_node]. See invariant on {!entry}. *)@@ -214,7 +215,8 @@ struct(c.name, `Contents (Contents_key.to_hash c.contents, c.metadata))| Contents_hash c -> (c.name, `Contents (c.contents, Metadata.default))| Contents_m_hash c -> (c.name, `Contents (c.contents, c.metadata))- | Contents_inlined_value c -> (c.name, `Contents_inlined (c.value, c.metadata))+ | Contents_inlined_value c ->+ (c.name, `Contents_inlined (c.value, c.metadata))| Contents_inlined _c -> assert false| Contents_inlined_hash _c -> assert falseFile "test/irmin-pack/gen_always.ml", line 1, characters 0-0:diff --git a/_build/default/test/irmin-pack/gen_always.ml b/_build/default/test/irmin-pack/.formatted/gen_always.mlindex d315877..58696da 100644--- a/_build/default/test/irmin-pack/gen_always.ml+++ b/_build/default/test/irmin-pack/.formatted/gen_always.ml@@ -43,7 +43,7 @@ let info = Store.Info.emptylet generate ~sw ~fs =let path = "version_3_always_new" inrm_dir path;- let rw = Store.Repo.v (config ~sw ~fs (Eio.Path.(fs / path))) in+ let rw = Store.Repo.v (config ~sw ~fs Eio.Path.(fs / path)) in(* Create tree matching the original structure:borphan | b01 <- n01 <- n0 <- c0@@ -61,11 +61,17 @@ let generate ~sw ~fs =let k_b01 = Store.Backend.Contents.add bstore "b01" in(* Create n01 node with step-b01 -> b01 *)- let n01 = Store.Backend.Node.Val.of_list [ ("step-b01", `Contents (k_b01, ())) ] [] in+ let n01 =+ Store.Backend.Node.Val.of_list+ [ ("step-b01", `Contents (k_b01, ())) ]+ []+ inlet k_n01 = Store.Backend.Node.add nstore n01 in(* Create n0 (root) node with step-n01 -> n01 *)- let n0 = Store.Backend.Node.Val.of_list [ ("step-n01", `Node (k_n01, [])) ] [] in+ let n0 =+ Store.Backend.Node.Val.of_list [ ("step-n01", `Node (k_n01, [])) ] []+ inlet k_n0 = Store.Backend.Node.add nstore n0 in(* Create commit *)@@ -81,7 +87,8 @@ let generate ~sw ~fs =let hex =String.to_seq hash_bytes|> Seq.map (fun c -> Printf.sprintf "%02x" (Char.code c))- |> List.of_seq |> String.concat ""+ |> List.of_seq+ |> String.concat ""inPrintf.printf "Commit hash (hex): %s\n" hex);File "test/irmin-pack/gen_minimal.ml", line 1, characters 0-0:diff --git a/_build/default/test/irmin-pack/gen_minimal.ml b/_build/default/test/irmin-pack/.formatted/gen_minimal.mlindex e9de76b..0da9c6b 100644--- a/_build/default/test/irmin-pack/gen_minimal.ml+++ b/_build/default/test/irmin-pack/.formatted/gen_minimal.ml@@ -43,7 +43,7 @@ let info = Store.Info.emptylet generate ~sw ~fs =let path = "version_3_minimal_new" inrm_dir path;- let rw = Store.Repo.v (config ~sw ~fs (Eio.Path.(fs / path))) in+ let rw = Store.Repo.v (config ~sw ~fs Eio.Path.(fs / path)) in(* Create tree matching the original structure:borphan | b01 <- n01 <- n0 <- c0@@ -61,11 +61,17 @@ let generate ~sw ~fs =let k_b01 = Store.Backend.Contents.add bstore "b01" in(* Create n01 node with step-b01 -> b01 *)- let n01 = Store.Backend.Node.Val.of_list [ ("step-b01", `Contents (k_b01, ())) ] [] in+ let n01 =+ Store.Backend.Node.Val.of_list+ [ ("step-b01", `Contents (k_b01, ())) ]+ []+ inlet k_n01 = Store.Backend.Node.add nstore n01 in(* Create n0 (root) node with step-n01 -> n01 *)- let n0 = Store.Backend.Node.Val.of_list [ ("step-n01", `Node (k_n01, [])) ] [] in+ let n0 =+ Store.Backend.Node.Val.of_list [ ("step-n01", `Node (k_n01, [])) ] []+ inlet k_n0 = Store.Backend.Node.add nstore n0 in(* Create commit *)@@ -81,7 +87,8 @@ let generate ~sw ~fs =let hex =String.to_seq hash_bytes|> Seq.map (fun c -> Printf.sprintf "%02x" (Char.code c))- |> List.of_seq |> String.concat ""+ |> List.of_seq+ |> String.concat ""inPrintf.printf "Commit hash (hex): %s\n" hex);File "test/irmin-pack/gen_gced.ml", line 1, characters 0-0:diff --git a/_build/default/test/irmin-pack/gen_gced.ml b/_build/default/test/irmin-pack/.formatted/gen_gced.mlindex a0f6aea..9162f26 100644--- a/_build/default/test/irmin-pack/gen_gced.ml+++ b/_build/default/test/irmin-pack/.formatted/gen_gced.ml@@ -42,7 +42,7 @@ let info = Store.Info.emptylet generate ~domain_mgr ~sw ~fs =let path = "version_3_minimal_gced_new" inrm_dir path;- let rw = Store.Repo.v (config ~sw ~fs (Eio.Path.(fs / path))) in+ let rw = Store.Repo.v (config ~sw ~fs Eio.Path.(fs / path)) in(* Create tree matching the original structure:borphan | b01 <- n01 <- n0 <- c0@@ -50,39 +50,49 @@ let generate ~domain_mgr ~sw ~fs =borphan is orphan content that is added to the content store butNOT part of the tree. The tree only has step-n01/step-b01 -> "b01" *)-- let k_c0 = Store.Backend.Repo.batch rw (fun bstore nstore cstore ->- (* First, add the orphan content directly to the content store *)- let _ = Store.Backend.Contents.add bstore "borphan" in-- (* Add b01 content *)- let k_b01 = Store.Backend.Contents.add bstore "b01" in-- (* Create n01 node with step-b01 -> b01 *)- let n01 = Store.Backend.Node.Val.of_list [ ("step-b01", `Contents (k_b01, ())) ] [] in- let k_n01 = Store.Backend.Node.add nstore n01 in-- (* Create n0 (root) node with step-n01 -> n01 *)- let n0 = Store.Backend.Node.Val.of_list [ ("step-n01", `Node (k_n01, [])) ] [] in- let k_n0 = Store.Backend.Node.add nstore n0 in-- (* Create commit *)- let c0 = Store.Backend.Commit.Val.v ~parents:[] ~info ~node:k_n0 in- let k_c0 = Store.Backend.Commit.add cstore c0 in-- Printf.printf "Commit key type: %s\n"- (Irmin.Type.to_string Store.Backend.Commit.Key.t k_c0);- let hash = Store.Backend.Commit.Key.to_hash k_c0 in- Printf.printf "Commit hash (type): %s\n"- (Irmin.Type.to_string Store.Hash.t hash);- let hash_bytes = Irmin.Type.(unstage (to_bin_string Store.Hash.t)) hash in- let hex =- String.to_seq hash_bytes- |> Seq.map (fun c -> Printf.sprintf "%02x" (Char.code c))- |> List.of_seq |> String.concat ""- in- Printf.printf "Commit hash (hex): %s\n" hex;- k_c0) in+ let k_c0 =+ Store.Backend.Repo.batch rw (fun bstore nstore cstore ->+ (* First, add the orphan content directly to the content store *)+ let _ = Store.Backend.Contents.add bstore "borphan" in++ (* Add b01 content *)+ let k_b01 = Store.Backend.Contents.add bstore "b01" in++ (* Create n01 node with step-b01 -> b01 *)+ let n01 =+ Store.Backend.Node.Val.of_list+ [ ("step-b01", `Contents (k_b01, ())) ]+ []+ in+ let k_n01 = Store.Backend.Node.add nstore n01 in++ (* Create n0 (root) node with step-n01 -> n01 *)+ let n0 =+ Store.Backend.Node.Val.of_list [ ("step-n01", `Node (k_n01, [])) ] []+ in+ let k_n0 = Store.Backend.Node.add nstore n0 in++ (* Create commit *)+ let c0 = Store.Backend.Commit.Val.v ~parents:[] ~info ~node:k_n0 in+ let k_c0 = Store.Backend.Commit.add cstore c0 in++ Printf.printf "Commit key type: %s\n"+ (Irmin.Type.to_string Store.Backend.Commit.Key.t k_c0);+ let hash = Store.Backend.Commit.Key.to_hash k_c0 in+ Printf.printf "Commit hash (type): %s\n"+ (Irmin.Type.to_string Store.Hash.t hash);+ let hash_bytes =+ Irmin.Type.(unstage (to_bin_string Store.Hash.t)) hash+ in+ let hex =+ String.to_seq hash_bytes+ |> Seq.map (fun c -> Printf.sprintf "%02x" (Char.code c))+ |> List.of_seq+ |> String.concat ""+ in+ Printf.printf "Commit hash (hex): %s\n" hex;+ k_c0)+ in(* Run GC with c0 as target to remove borphan *)Printf.printf "Running GC with c0 as target...\n";File "test/irmin-pack/test_inline_contents.ml", line 1, characters 0-0:diff --git a/_build/default/test/irmin-pack/test_inline_contents.ml b/_build/default/test/irmin-pack/.formatted/test_inline_contents.mlindex 8a450de..e8ae0f6 100644--- a/_build/default/test/irmin-pack/test_inline_contents.ml+++ b/_build/default/test/irmin-pack/.formatted/test_inline_contents.ml@@ -42,18 +42,22 @@ let config ~sw ~fs ?(readonly = false) ?(fresh = true) ~inline_contents root =let info = S.Info.empty-(** Test that data can be stored and retrieved correctly with inlining disabled *)+(** Test that data can be stored and retrieved correctly with inlining disabled+*)let test_without_inlining ~fs () =let root = root_no_inline ~fs inrm_dir root;Eio.Switch.run @@ fun sw ->let repo =- S.Repo.v (config ~sw ~fs ~readonly:false ~fresh:true ~inline_contents:false root)+ S.Repo.v+ (config ~sw ~fs ~readonly:false ~fresh:true ~inline_contents:false root)in(* Create a tree with small and large contents *)let tree = S.Tree.empty () in- let tree = S.Tree.add tree [ "small" ] "abc" in (* Small content, < 16 bytes *)- let tree = S.Tree.add tree [ "large" ] (String.make 100 'x') in (* Large content *)+ let tree = S.Tree.add tree [ "small" ] "abc" in+ (* Small content, < 16 bytes *)+ let tree = S.Tree.add tree [ "large" ] (String.make 100 'x') in+ (* Large content *)(* Create a commit *)let commit = S.Commit.v repo ~parents:[] ~info tree inlet hash = S.Commit.hash commit in@@ -63,21 +67,28 @@ let test_without_inlining ~fs () =let small = S.Tree.find tree' [ "small" ] inlet large = S.Tree.find tree' [ "large" ] inAlcotest.(check (option string)) "small content" (Some "abc") small;- Alcotest.(check (option string)) "large content" (Some (String.make 100 'x')) large;+ Alcotest.(check (option string))+ "large content"+ (Some (String.make 100 'x'))+ large;S.Repo.close repo-(** Test that data can be stored and retrieved correctly with inlining enabled *)+(** Test that data can be stored and retrieved correctly with inlining enabled+*)let test_with_inlining ~fs () =let root = root_with_inline ~fs inrm_dir root;Eio.Switch.run @@ fun sw ->let repo =- S.Repo.v (config ~sw ~fs ~readonly:false ~fresh:true ~inline_contents:true root)+ S.Repo.v+ (config ~sw ~fs ~readonly:false ~fresh:true ~inline_contents:true root)in(* Create a tree with small and large contents *)let tree = S.Tree.empty () in- let tree = S.Tree.add tree [ "small" ] "abc" in (* Small content, < 16 bytes *)- let tree = S.Tree.add tree [ "large" ] (String.make 100 'x') in (* Large content *)+ let tree = S.Tree.add tree [ "small" ] "abc" in+ (* Small content, < 16 bytes *)+ let tree = S.Tree.add tree [ "large" ] (String.make 100 'x') in+ (* Large content *)(* Create a commit *)let commit = S.Commit.v repo ~parents:[] ~info tree inlet hash = S.Commit.hash commit in@@ -87,10 +98,14 @@ let test_with_inlining ~fs () =let small = S.Tree.find tree' [ "small" ] inlet large = S.Tree.find tree' [ "large" ] inAlcotest.(check (option string)) "small content" (Some "abc") small;- Alcotest.(check (option string)) "large content" (Some (String.make 100 'x')) large;+ Alcotest.(check (option string))+ "large content"+ (Some (String.make 100 'x'))+ large;S.Repo.close repo-(** Test that the same data produces the same content hash regardless of inlining *)+(** Test that the same data produces the same content hash regardless of+ inlining *)let test_content_equivalence ~fs () =let root_no_inline = root_equiv_no ~fs inlet root_inline = root_equiv_yes ~fs in@@ -100,7 +115,8 @@ let test_content_equivalence ~fs () =(* Create store without inlining *)let repo1 =S.Repo.v- (config ~sw ~fs ~readonly:false ~fresh:true ~inline_contents:false root_no_inline)+ (config ~sw ~fs ~readonly:false ~fresh:true ~inline_contents:false+ root_no_inline)inlet tree1 = S.Tree.empty () inlet tree1 = S.Tree.add tree1 [ "a" ] "small" in@@ -111,7 +127,8 @@ let test_content_equivalence ~fs () =(* Create store with inlining *)let repo2 =S.Repo.v- (config ~sw ~fs ~readonly:false ~fresh:true ~inline_contents:true root_inline)+ (config ~sw ~fs ~readonly:false ~fresh:true ~inline_contents:true+ root_inline)inlet tree2 = S.Tree.empty () inlet tree2 = S.Tree.add tree2 [ "a" ] "small" in@@ -122,28 +139,49 @@ let test_content_equivalence ~fs () =(* Verify data is the same when read back *)let repo1 =S.Repo.v- (config ~sw ~fs ~readonly:true ~fresh:false ~inline_contents:false root_no_inline)+ (config ~sw ~fs ~readonly:true ~fresh:false ~inline_contents:false+ root_no_inline)inlet repo2 =S.Repo.v- (config ~sw ~fs ~readonly:true ~fresh:false ~inline_contents:true root_inline)+ (config ~sw ~fs ~readonly:true ~fresh:false ~inline_contents:true+ root_inline)+ in+ let tree1' =+ S.Commit.of_hash repo1 (S.Commit.hash commit1)+ |> Option.get+ |> S.Commit.tree+ in+ let tree2' =+ S.Commit.of_hash repo2 (S.Commit.hash commit2)+ |> Option.get+ |> S.Commit.treein- let tree1' = S.Commit.of_hash repo1 (S.Commit.hash commit1) |> Option.get |> S.Commit.tree in- let tree2' = S.Commit.of_hash repo2 (S.Commit.hash commit2) |> Option.get |> S.Commit.tree in(* Verify contents are identical *)- Alcotest.(check (option string)) "a" (S.Tree.find tree1' [ "a" ]) (S.Tree.find tree2' [ "a" ]);- Alcotest.(check (option string)) "b" (S.Tree.find tree1' [ "b" ]) (S.Tree.find tree2' [ "b" ]);- Alcotest.(check (option string)) "c/d" (S.Tree.find tree1' [ "c"; "d" ]) (S.Tree.find tree2' [ "c"; "d" ]);+ Alcotest.(check (option string))+ "a"+ (S.Tree.find tree1' [ "a" ])+ (S.Tree.find tree2' [ "a" ]);+ Alcotest.(check (option string))+ "b"+ (S.Tree.find tree1' [ "b" ])+ (S.Tree.find tree2' [ "b" ]);+ Alcotest.(check (option string))+ "c/d"+ (S.Tree.find tree1' [ "c"; "d" ])+ (S.Tree.find tree2' [ "c"; "d" ]);S.Repo.close repo1;S.Repo.close repo2-(** Test that verifies small content is actually inlined in the node structure *)+(** Test that verifies small content is actually inlined in the node structure+*)let test_inlining_structure ~fs () =let root = Eio.Path.(fs / "_build" / "test-inline-structure") inrm_dir root;Eio.Switch.run @@ fun sw ->let repo =- S.Repo.v (config ~sw ~fs ~readonly:false ~fresh:true ~inline_contents:true root)+ S.Repo.v+ (config ~sw ~fs ~readonly:false ~fresh:true ~inline_contents:true root)in(* Create a tree with small content that should be inlined *)let tree = S.Tree.empty () in@@ -151,10 +189,18 @@ let test_inlining_structure ~fs () =- 1-byte variant tag for the Contents.t encoding- 1-byte varint length prefix for the stringSo raw content must be < 46 bytes (45 or less) to be inlined. *)- let tree = S.Tree.add tree [ "tiny" ] "x" in (* 1 byte raw -> 3 bytes serialized -> inlined *)- let tree = S.Tree.add tree [ "small" ] "hello" in (* 5 bytes raw -> 7 bytes serialized -> inlined *)- let tree = S.Tree.add tree [ "medium" ] "0123456789abc0123456789abc0123456789abc012345" in (* 45 bytes raw -> 47 bytes serialized -> inlined *)- let tree = S.Tree.add tree [ "large" ] "0123456789abc0123456789abc0123456789abc0123456" in (* 46 bytes raw -> 48 bytes serialized -> NOT inlined *)+ let tree = S.Tree.add tree [ "tiny" ] "x" in+ (* 1 byte raw -> 3 bytes serialized -> inlined *)+ let tree = S.Tree.add tree [ "small" ] "hello" in+ (* 5 bytes raw -> 7 bytes serialized -> inlined *)+ let tree =+ S.Tree.add tree [ "medium" ] "0123456789abc0123456789abc0123456789abc012345"+ in+ (* 45 bytes raw -> 47 bytes serialized -> inlined *)+ let tree =+ S.Tree.add tree [ "large" ] "0123456789abc0123456789abc0123456789abc0123456"+ in+ (* 46 bytes raw -> 48 bytes serialized -> NOT inlined *)(* Commit to persist the tree *)let commit = S.Commit.v repo ~parents:[] ~info tree inlet _hash = S.Commit.hash commit in@@ -162,7 +208,8 @@ let test_inlining_structure ~fs () =let commit' = S.Commit.of_hash repo (S.Commit.hash commit) |> Option.get inlet tree' = S.Commit.tree commit' in(* Get the root node using to_backend_node *)- let root_node = match S.Tree.destruct tree' with+ let root_node =+ match S.Tree.destruct tree' with| `Node (n, _inlined) -> S.to_backend_node n| `Contents _ -> Alcotest.fail "Expected a node"in@@ -171,17 +218,17 @@ let test_inlining_structure ~fs () =(* Count inlined vs non-inlined contents *)let inlined_count = ref 0 inlet non_inlined_count = ref 0 in- List.iter (fun (step, value) ->- match value with- | `Contents_inlined (bytes, _) ->- [%log.debug "Inlined content at %s: %S" step bytes];- incr inlined_count- | `Contents _ ->- [%log.debug "Non-inlined content at %s" step];- incr non_inlined_count- | `Node _ ->- [%log.debug "Node at %s" step]- ) entries;+ List.iter+ (fun (step, value) ->+ match value with+ | `Contents_inlined (bytes, _) ->+ [%log.debug "Inlined content at %s: %S" step bytes];+ incr inlined_count+ | `Contents _ ->+ [%log.debug "Non-inlined content at %s" step];+ incr non_inlined_count+ | `Node _ -> [%log.debug "Node at %s" step])+ entries;(* Verify: 3 entries should be inlined (tiny, small, medium), 1 should not (large) *)Alcotest.(check int) "inlined count" 3 !inlined_count;Alcotest.(check int) "non-inlined count" 1 !non_inlined_count;File "src/irmin/store.ml", line 1, characters 0-0:diff --git a/_build/default/src/irmin/store.ml b/_build/default/src/irmin/.formatted/store.mlindex ce5aa75..668441e 100644--- a/_build/default/src/irmin/store.ml+++ b/_build/default/src/irmin/.formatted/store.ml@@ -939,10 +939,7 @@ module Make (B : Backend.S) = structlet mem_tree t k = tree t |> fun tree -> Tree.mem_tree tree klet find_all t k = tree t |> fun tree -> Tree.find_all tree klet find t k = tree t |> fun tree -> Tree.find tree k-- let get t k =- tree t |> fun tree -> Tree.get tree k-+ let get t k = tree t |> fun tree -> Tree.get tree klet find_tree t k = tree t |> fun tree -> Tree.find_tree tree klet get_tree t k = tree t |> fun tree -> Tree.get_tree tree kFile "test/irmin-pack/test_existing_stores.ml", line 1, characters 0-0:diff --git a/_build/default/test/irmin-pack/test_existing_stores.ml b/_build/default/test/irmin-pack/.formatted/test_existing_stores.mlindex 00d49c3..a3519b5 100644--- a/_build/default/test/irmin-pack/test_existing_stores.ml+++ b/_build/default/test/irmin-pack/.formatted/test_existing_stores.ml@@ -356,9 +356,9 @@ end(** Test that verifies mixed V2/V3 inode entries in a pack file.- This test opens an existing store with V2 inodes, adds new entries- (which will be V3), and verifies that both V2 and V3 entries coexist- and can be read correctly.+ This test opens an existing store with V2 inodes, adds new entries (which+ will be V3), and verifies that both V2 and V3 entries coexist and can be+ read correctly.We use version_2_to_3_always store because it uses always indexing strategywhich indexes inodes (unlike minimal strategy used by version_3_minimal). *)@@ -422,8 +422,7 @@ module Test_mixed_v2_v3 = struct(* The archived store should have V2 inodes (created before V3 introduction) *)Alcotest.(check bool)- "Initial store should have V2 inodes"- (initial_v2 > 0) true;+ "Initial store should have V2 inodes" (initial_v2 > 0) true;(* Add new content - this will create V3 inodes *)let tree = S.Commit.tree commit in@@ -450,8 +449,7 @@ module Test_mixed_v2_v3 = struct"Should still have V2 inodes (old entries preserved)"(final_v2 >= initial_v2) true;Alcotest.(check bool)- "Should have new V3 inodes (from new writes)"- (final_v3 > initial_v3) true;+ "Should have new V3 inodes (from new writes)" (final_v3 > initial_v3) true;(* Verify we can still read the original commit via its hash *)let repo = S.Repo.v conf in@@ -464,7 +462,7 @@ module Test_mixed_v2_v3 = struct[%log.app"Mixed V2/V3 test passed: V2=%d V3=%d inodes coexist and are readable"- final_v2 final_v3]+ final_v2 final_v3]endlet tests ~fs ~domain_mgr =File "src/irmin-server/unix/server.ml", line 1, characters 0-0:diff --git a/_build/default/src/irmin-server/unix/server.ml b/_build/default/src/irmin-server/unix/.formatted/server.mlindex 276f0f4..6ebee01 100644--- a/_build/default/src/irmin-server/unix/server.ml+++ b/_build/default/src/irmin-server/unix/.formatted/server.ml@@ -239,8 +239,7 @@ module Make (Codec : Conn.Codec.S) (Store : Irmin.Generic_key.S) = structlet path = Store.Path.rcons prefix path inlet kind = Store.Tree.kind tree Store.Path.empty inmatch kind with- | Some `Contents ->- Some (path, "contents", Store.Tree.hash tree)+ | Some `Contents -> Some (path, "contents", Store.Tree.hash tree)| Some `Node -> Some (path, "node", Store.Tree.hash tree)| None -> None)keysFile "test/irmin-pack/bench_inline/distribution.ml", line 1, characters 0-0:diff --git a/_build/default/test/irmin-pack/bench_inline/distribution.ml b/_build/default/test/irmin-pack/bench_inline/.formatted/distribution.mlindex 7d7630f..09ff2d0 100644--- a/_build/default/test/irmin-pack/bench_inline/distribution.ml+++ b/_build/default/test/irmin-pack/bench_inline/.formatted/distribution.ml@@ -5,7 +5,7 @@ type t =| Bimodal of { small : int; large : int; small_ratio : float }| Log_normal of { mu : float; sigma : float; min : int; max : int }| Zipfian of { min : int; max : int; exponent : float; cdf : float array }- | Fixed of int list (* Cycle through fixed sizes *)+ | Fixed of int list (* Cycle through fixed sizes *)let uniform ~min ~max = Uniform { min; max }let bimodal ~small ~large ~small_ratio = Bimodal { small; large; small_ratio }@@ -16,14 +16,14 @@ let fixed sizes = Fixed sizeslet zipfian ~min ~max ~exponent =let n = max - min + 1 in(* Precompute CDF for efficient sampling *)- let weights = Array.init n (fun i ->- 1.0 /. (float_of_int (i + 1) ** exponent)- ) in+ let weights =+ Array.init n (fun i -> 1.0 /. (float_of_int (i + 1) ** exponent))+ inlet total = Array.fold_left ( +. ) 0.0 weights inlet cdf = Array.make n 0.0 inlet acc = ref 0.0 infor i = 0 to n - 1 do- acc := !acc +. weights.(i) /. total;+ acc := !acc +. (weights.(i) /. total);cdf.(i) <- !accdone;Zipfian { min; max; exponent; cdf }@@ -41,8 +41,7 @@ let binary_search_cdf cdf u =if lo >= hi then loelselet mid = (lo + hi) / 2 in- if cdf.(mid) < u then go (mid + 1) hi- else go lo mid+ if cdf.(mid) < u then go (mid + 1) hi else go lo midingo 0 (Array.length cdf - 1)@@ -53,7 +52,7 @@ let sample_one = functionif Random.float 1.0 < small_ratio then small else large| Log_normal { mu; sigma; min; max } ->let z = random_normal () in- let x = exp (mu +. sigma *. z) in+ let x = exp (mu +. (sigma *. z)) inlet size = int_of_float x inInt.max min (Int.min max size)| Zipfian { min; cdf; _ } ->@@ -64,8 +63,7 @@ let sample_one = functionlet n = List.length sizes inList.nth sizes (Random.int n)-let sample ~n dist =- Array.init n (fun _ -> sample_one dist)+let sample ~n dist = Array.init n (fun _ -> sample_one dist)(* Statistics *)let mean arr =@@ -83,8 +81,7 @@ let describe arr =Array.sort Int.compare sorted;let n = Array.length sorted in{||}- ^ Printf.sprintf "n=%d, min=%d, p50=%d, p90=%d, p99=%d, max=%d, mean=%.1f"- n+ ^ Printf.sprintf "n=%d, min=%d, p50=%d, p90=%d, p99=%d, max=%d, mean=%.1f" nsorted.(0)sorted.(n / 2)sorted.(n * 9 / 10)File "test/irmin-pack/bench_inline/main.ml", line 1, characters 0-0:diff --git a/_build/default/test/irmin-pack/bench_inline/main.ml b/_build/default/test/irmin-pack/bench_inline/.formatted/main.mlindex 7961315..dbba6dd 100644--- a/_build/default/test/irmin-pack/bench_inline/main.ml+++ b/_build/default/test/irmin-pack/bench_inline/.formatted/main.ml@@ -3,24 +3,26 @@let () = Random.init 42(* All available distributions *)-let distributions_list = [- ("uniform-small", Distribution.all_small);- ("uniform-large", Distribution.all_large);- ("around-threshold", Distribution.around_threshold);- ("mostly-small", Distribution.mostly_small);- ("mostly-large", Distribution.mostly_large);- ("log-normal-small", Distribution.log_normal_small);- ("log-normal-medium", Distribution.log_normal_medium);- ("zipfian-small", Distribution.zipfian_small);- ("zipfian-medium", Distribution.zipfian_medium);- ("zipfian-steep", Distribution.zipfian_steep);-]+let distributions_list =+ [+ ("uniform-small", Distribution.all_small);+ ("uniform-large", Distribution.all_large);+ ("around-threshold", Distribution.around_threshold);+ ("mostly-small", Distribution.mostly_small);+ ("mostly-large", Distribution.mostly_large);+ ("log-normal-small", Distribution.log_normal_small);+ ("log-normal-medium", Distribution.log_normal_medium);+ ("zipfian-small", Distribution.zipfian_small);+ ("zipfian-medium", Distribution.zipfian_medium);+ ("zipfian-steep", Distribution.zipfian_steep);+ ](* Distribution argument *)let distribution =- let doc = Printf.sprintf- "Size distribution. One of: %s"- (String.concat ", " (List.map fst distributions_list)) in+ let doc =+ Printf.sprintf "Size distribution. One of: %s"+ (String.concat ", " (List.map fst distributions_list))+ inCmdliner.Arg.(value& opt (enum distributions_list) Distribution.around_threshold@@ -39,10 +41,7 @@ let n_contents =& info [ "n"; "contents" ] ~docv:"N" ~doc:"Number of contents to create")let inline_contents =- Cmdliner.Arg.(- value- & flag- & info [ "inline" ] ~doc:"Enable inline contents")+ Cmdliner.Arg.(value & flag & info [ "inline" ] ~doc:"Enable inline contents")let inline_threshold =Cmdliner.Arg.(@@ -55,7 +54,8 @@ let n_reads =Cmdliner.Arg.(value& opt int 1_000- & info [ "reads" ] ~docv:"N" ~doc:"Number of read operations for latency measurement")+ & info [ "reads" ] ~docv:"N"+ ~doc:"Number of read operations for latency measurement")let runs =Cmdliner.Arg.(@@ -64,19 +64,22 @@ let runs =& info [ "runs" ] ~docv:"N" ~doc:"Number of benchmark iterations")let run_all_flag =- Cmdliner.Arg.(- value- & flag- & info [ "all" ] ~doc:"Run all distributions")+ Cmdliner.Arg.(value & flag & info [ "all" ] ~doc:"Run all distributions")let csv_header =- Cmdliner.Arg.(- value- & flag- & info [ "header" ] ~doc:"Print CSV header")+ Cmdliner.Arg.(value & flag & info [ "header" ] ~doc:"Print CSV header")-let config distribution n_contents inline_contents inline_threshold n_reads runs =- Bench.{ distribution; n_contents; inline_contents; inline_threshold; n_reads; runs }+let config distribution n_contents inline_contents inline_threshold n_reads runs+ =+ Bench.+ {+ distribution;+ n_contents;+ inline_contents;+ inline_threshold;+ n_reads;+ runs;+ }(* Single distribution run *)let run_single csv_header dist_name config =@@ -92,59 +95,101 @@ let run_all n_contents inline_threshold n_reads runs =let distributions = distributions_list inBench.print_csv_header ();Eio_main.run @@ fun env ->- List.iter (fun (dist_name, distribution) ->- (* First run without inlining *)- let config = Bench.{ distribution; n_contents; inline_contents = false;- inline_threshold; n_reads; runs } in- let result = Bench.run ~fs:env#fs ~config in- Bench.print_csv_row ~dist_name ~config result;- (* Then run with inlining *)- let config = Bench.{ config with inline_contents = true } in- let result = Bench.run ~fs:env#fs ~config in- Bench.print_csv_row ~dist_name ~config result- ) distributions+ List.iter+ (fun (dist_name, distribution) ->+ (* First run without inlining *)+ let config =+ Bench.+ {+ distribution;+ n_contents;+ inline_contents = false;+ inline_threshold;+ n_reads;+ runs;+ }+ in+ let result = Bench.run ~fs:env#fs ~config in+ Bench.print_csv_row ~dist_name ~config result;+ (* Then run with inlining *)+ let config = Bench.{ config with inline_contents = true } in+ let result = Bench.run ~fs:env#fs ~config in+ Bench.print_csv_row ~dist_name ~config result)+ distributions(* Run threshold optimization: test multiple thresholds on selected distributions *)let run_optimize n_contents n_reads runs =Logs.set_level None;let thresholds = [ 8; 16; 24; 32; 48; 64; 96; 128 ] in(* Use representative distributions *)- let distributions = [- ("zipfian-steep", Distribution.zipfian_steep);- ("zipfian-small", Distribution.zipfian_small);- ("log-normal-small", Distribution.log_normal_small);- ("mostly-small", Distribution.mostly_small);- ] in+ let distributions =+ [+ ("zipfian-steep", Distribution.zipfian_steep);+ ("zipfian-small", Distribution.zipfian_small);+ ("log-normal-small", Distribution.log_normal_small);+ ("mostly-small", Distribution.mostly_small);+ ]+ inBench.print_csv_header ();Eio_main.run @@ fun env ->- List.iter (fun (dist_name, distribution) ->- (* Baseline: no inlining *)- let config = Bench.{ distribution; n_contents; inline_contents = false;- inline_threshold = 0; n_reads; runs } in- let result = Bench.run ~fs:env#fs ~config in- Bench.print_csv_row ~dist_name ~config result;- (* Test each threshold *)- List.iter (fun inline_threshold ->- let config = Bench.{ distribution; n_contents; inline_contents = true;- inline_threshold; n_reads; runs } in+ List.iter+ (fun (dist_name, distribution) ->+ (* Baseline: no inlining *)+ let config =+ Bench.+ {+ distribution;+ n_contents;+ inline_contents = false;+ inline_threshold = 0;+ n_reads;+ runs;+ }+ inlet result = Bench.run ~fs:env#fs ~config in- Bench.print_csv_row ~dist_name ~config result- ) thresholds- ) distributions+ Bench.print_csv_row ~dist_name ~config result;+ (* Test each threshold *)+ List.iter+ (fun inline_threshold ->+ let config =+ Bench.+ {+ distribution;+ n_contents;+ inline_contents = true;+ inline_threshold;+ n_reads;+ runs;+ }+ in+ let result = Bench.run ~fs:env#fs ~config in+ Bench.print_csv_row ~dist_name ~config result)+ thresholds)+ distributionslet cmd_run =- let run csv_header dist_name distribution n_contents inline_contents inline_threshold n_reads runs all =- if all then- run_all n_contents inline_threshold n_reads runs+ let run csv_header dist_name distribution n_contents inline_contents+ inline_threshold n_reads runs all =+ if all then run_all n_contents inline_threshold n_reads runselse- run_single csv_header dist_name (config distribution n_contents inline_contents inline_threshold n_reads runs)+ run_single csv_header dist_name+ (config distribution n_contents inline_contents inline_threshold n_reads+ runs)inlet doc = "Run inline contents benchmark" inCmdliner.Cmd.v(Cmdliner.Cmd.info "run" ~doc)- Cmdliner.Term.(const run $ csv_header $ distribution_name $ distribution- $ n_contents $ inline_contents $ inline_threshold- $ n_reads $ runs $ run_all_flag)+ Cmdliner.Term.(+ const run+ $ csv_header+ $ distribution_name+ $ distribution+ $ n_contents+ $ inline_contents+ $ inline_threshold+ $ n_reads+ $ runs+ $ run_all_flag)let cmd_optimize =let doc = "Find optimal inlining threshold by testing multiple values" in@@ -157,7 +202,8 @@ let sweep_threshold =Cmdliner.Arg.(value& opt int 16- & info [ "threshold"; "t" ] ~docv:"BYTES" ~doc:"Inlining threshold to test around")+ & info [ "threshold"; "t" ] ~docv:"BYTES"+ ~doc:"Inlining threshold to test around")let sweep_range =Cmdliner.Arg.(@@ -172,22 +218,46 @@ let run_sweep n_contents n_reads runs threshold range =Printf.printf "size,inline,store_bytes,write_ms,read_p50_us,read_p99_us\n";Eio_main.run @@ fun env ->for size = threshold - range to threshold + range do- let distribution = Distribution.fixed [size] in- List.iter (fun inline_contents ->- let config = Bench.{ distribution; n_contents; inline_contents;- inline_threshold = threshold; n_reads; runs } in- let (store_size, write_time, p50, p99, _p999, _inlined, _non_inlined, _sizes) =- Bench.run ~fs:env#fs ~config in- Printf.printf "%d,%b,%d,%.2f,%.2f,%.2f\n"- size inline_contents store_size write_time p50 p99- ) [ false; true ]+ let distribution = Distribution.fixed [ size ] in+ List.iter+ (fun inline_contents ->+ let config =+ Bench.+ {+ distribution;+ n_contents;+ inline_contents;+ inline_threshold = threshold;+ n_reads;+ runs;+ }+ in+ let ( store_size,+ write_time,+ p50,+ p99,+ _p999,+ _inlined,+ _non_inlined,+ _sizes ) =+ Bench.run ~fs:env#fs ~config+ in+ Printf.printf "%d,%b,%d,%.2f,%.2f,%.2f\n" size inline_contents+ store_size write_time p50 p99)+ [ false; true ]donelet cmd_sweep =let doc = "Sweep content sizes around inlining threshold" inCmdliner.Cmd.v(Cmdliner.Cmd.info "sweep" ~doc)- Cmdliner.Term.(const run_sweep $ n_contents $ n_reads $ runs $ sweep_threshold $ sweep_range)+ Cmdliner.Term.(+ const run_sweep+ $ n_contents+ $ n_reads+ $ runs+ $ sweep_threshold+ $ sweep_range)let cmds = [ cmd_run; cmd_optimize; cmd_sweep ]File "test/irmin-pack/bench_inline/bench.ml", line 1, characters 0-0:diff --git a/_build/default/test/irmin-pack/bench_inline/bench.ml b/_build/default/test/irmin-pack/bench_inline/.formatted/bench.mlindex a8e7fd1..6c7d499 100644--- a/_build/default/test/irmin-pack/bench_inline/bench.ml+++ b/_build/default/test/irmin-pack/bench_inline/.formatted/bench.ml@@ -3,41 +3,42 @@module S = Irmin_tezos.Storetype config = {- n_contents : int; (* Number of contents to create *)+ n_contents : int; (* Number of contents to create *)distribution : Distribution.t;inline_contents : bool;- inline_threshold : int; (* Max serialized bytes for inlining *)- n_reads : int; (* Number of read operations for latency measurement *)- runs : int; (* Number of benchmark runs *)+ inline_threshold : int; (* Max serialized bytes for inlining *)+ n_reads : int; (* Number of read operations for latency measurement *)+ runs : int; (* Number of benchmark runs *)}type metrics = {store_size_bytes : int;write_time_ms : float;- read_latencies_us : float array; (* Individual read latencies in microseconds *)+ read_latencies_us : float array;+ (* Individual read latencies in microseconds *)inlined_count : int;non_inlined_count : int;- content_sizes : int array; (* Actual sizes generated *)+ content_sizes : int array; (* Actual sizes generated *)}-let default_config = {- n_contents = 10_000;- distribution = Distribution.around_threshold;- inline_contents = false;- inline_threshold = 48;- n_reads = 1_000;- runs = 5;-}+let default_config =+ {+ n_contents = 10_000;+ distribution = Distribution.around_threshold;+ inline_contents = false;+ inline_threshold = 48;+ n_reads = 1_000;+ runs = 5;+ }let root fs = Eio.Path.(fs / "_build" / "bench-inline")--let reset_env ~fs () =- Eio.Path.rmtree ~missing_ok:true (root fs)-+let reset_env ~fs () = Eio.Path.rmtree ~missing_ok:true (root fs)let info () = S.Info.emptylet open_repo ~sw ~fs ~fresh ~readonly ~inline_contents () =- let conf = Irmin_pack.Conf.init ~sw ~fs ~fresh ~readonly ~inline_contents (root fs) in+ let conf =+ Irmin_pack.Conf.init ~sw ~fs ~fresh ~readonly ~inline_contents (root fs)+ inS.Repo.v conf(* Generate random content of given size *)@@ -47,22 +48,24 @@ let make_content size =(* Generate paths and contents based on distribution *)let generate ~config =let sizes = Distribution.sample ~n:config.n_contents config.distribution in- let contents = Array.mapi (fun i size ->- let path = [ Printf.sprintf "content_%06d" i ] in- let value = make_content size in- (path, value, size)- ) sizes in+ let contents =+ Array.mapi+ (fun i size ->+ let path = [ Printf.sprintf "content_%06d" i ] in+ let value = make_content size in+ (path, value, size))+ sizes+ incontents(* Measure directory size recursively *)let rec dir_size path =if Sys.is_directory path thenlet entries = Sys.readdir path in- Array.fold_left (fun acc entry ->- acc + dir_size (Filename.concat path entry)- ) 0 entries- else- (Unix.stat path).Unix.st_size+ Array.fold_left+ (fun acc entry -> acc + dir_size (Filename.concat path entry))+ 0 entries+ else (Unix.stat path).Unix.st_size(* Count inlined vs non-inlined contents by examining the backend *)let count_inlined repo =@@ -72,17 +75,19 @@ let count_inlined repo =let main = S.main repo inlet head = S.Head.get main inlet tree = S.Commit.tree head in- let root_node = match S.Tree.destruct tree with+ let root_node =+ match S.Tree.destruct tree with| `Node (n, _) -> S.to_backend_node n| `Contents _ -> failwith "Expected root to be a node"inlet entries = S.Backend.Node.Val.list root_node in- List.iter (fun (_step, value) ->- match value with- | `Contents_inlined _ -> incr inlined- | `Contents _ -> incr non_inlined- | `Node _ -> ()- ) entries;+ List.iter+ (fun (_step, value) ->+ match value with+ | `Contents_inlined _ -> incr inlined+ | `Contents _ -> incr non_inlined+ | `Node _ -> ())+ entries;(!inlined, !non_inlined)(* Run a single benchmark iteration *)@@ -95,14 +100,18 @@ let run_one ~sw ~fs ~config =Irmin.Tree.set_inline_contents_max_bytes config.inline_threshold;(* Write phase *)- let repo = open_repo ~sw ~fs ~fresh:true ~readonly:false- ~inline_contents:config.inline_contents () in+ let repo =+ open_repo ~sw ~fs ~fresh:true ~readonly:false+ ~inline_contents:config.inline_contents ()+ inlet main = S.main repo inlet t0 = Unix.gettimeofday () in- let tree = Array.fold_left (fun tree (path, value, _size) ->- S.Tree.add tree path value- ) (S.Tree.empty ()) contents in+ let tree =+ Array.fold_left+ (fun tree (path, value, _size) -> S.Tree.add tree path value)+ (S.Tree.empty ()) contents+ inS.set_tree_exn ~info main [] tree;let t1 = Unix.gettimeofday () inlet write_time_ms = 1000.0 *. (t1 -. t0) in@@ -116,34 +125,47 @@ let run_one ~sw ~fs ~config =let store_size_bytes = dir_size store_path in(* Read phase - reopen in readonly mode *)- let repo = open_repo ~sw ~fs ~fresh:false ~readonly:true- ~inline_contents:config.inline_contents () in+ let repo =+ open_repo ~sw ~fs ~fresh:false ~readonly:true+ ~inline_contents:config.inline_contents ()+ inlet main = S.main repo inlet head = S.Head.get main inlet tree = S.Commit.tree head in(* Sample random reads and measure latency *)let n_contents = Array.length contents in- let read_latencies_us = Array.init config.n_reads (fun _ ->- let idx = Random.int n_contents in- let path, expected_value, _ = contents.(idx) in- let t0 = Unix.gettimeofday () in- let value = S.Tree.find tree path in- let t1 = Unix.gettimeofday () in- (match value with- | Some v when Bytes.equal v expected_value -> ()- | Some v ->- Printf.eprintf "Warning: value mismatch at %s: expected %d bytes, got %d bytes\n"- (String.concat "/" path) (Bytes.length expected_value) (Bytes.length v)- | None ->- Printf.eprintf "Warning: missing value at %s\n" (String.concat "/" path));- 1_000_000.0 *. (t1 -. t0)- ) in+ let read_latencies_us =+ Array.init config.n_reads (fun _ ->+ let idx = Random.int n_contents in+ let path, expected_value, _ = contents.(idx) in+ let t0 = Unix.gettimeofday () in+ let value = S.Tree.find tree path in+ let t1 = Unix.gettimeofday () in+ (match value with+ | Some v when Bytes.equal v expected_value -> ()+ | Some v ->+ Printf.eprintf+ "Warning: value mismatch at %s: expected %d bytes, got %d bytes\n"+ (String.concat "/" path)+ (Bytes.length expected_value)+ (Bytes.length v)+ | None ->+ Printf.eprintf "Warning: missing value at %s\n"+ (String.concat "/" path));+ 1_000_000.0 *. (t1 -. t0))+ inS.Repo.close repo;- { store_size_bytes; write_time_ms; read_latencies_us;- inlined_count; non_inlined_count; content_sizes }+ {+ store_size_bytes;+ write_time_ms;+ read_latencies_us;+ inlined_count;+ non_inlined_count;+ content_sizes;+ }(* Compute latency percentiles *)let latency_percentiles latencies =@@ -156,7 +178,6 @@ let latency_percentiles latencies =(* Run benchmark with multiple iterations *)let run ~fs ~config =Eio.Switch.run @@ fun sw ->-(* Warm-up run *)let _ = run_one ~sw ~fs ~config in@@ -173,28 +194,36 @@ let run ~fs ~config =let median_store_size = store_sizes.(config.runs / 2) in(* Combine all read latencies from all runs *)- let all_latencies = Array.concat (Array.to_list- (Array.map (fun r -> r.read_latencies_us) results)) in+ let all_latencies =+ Array.concat+ (Array.to_list (Array.map (fun r -> r.read_latencies_us) results))+ inlet p50, p99, p999 = latency_percentiles all_latencies in(* Use first result for counts (should be same across runs) *)let r0 = results.(0) in- (median_store_size, median_write_time, p50, p99, p999,- r0.inlined_count, r0.non_inlined_count, r0.content_sizes)+ ( median_store_size,+ median_write_time,+ p50,+ p99,+ p999,+ r0.inlined_count,+ r0.non_inlined_count,+ r0.content_sizes )(* Print results as CSV row *)let print_csv_header () =- Printf.printf "distribution,threshold,n_contents,store_bytes,write_ms,read_p50_us,read_p99_us,read_p999_us,inlined,non_inlined,size_stats\n"--let print_csv_row ~dist_name ~config (store_size, write_time, p50, p99, p999, inlined, non_inlined, sizes) =- let threshold_str = if config.inline_contents then string_of_int config.inline_threshold else "off" in- Printf.printf "%s,%s,%d,%d,%.2f,%.2f,%.2f,%.2f,%d,%d,\"%s\"\n"- dist_name- threshold_str- config.n_contents- store_size- write_time- p50 p99 p999- inlined non_inlined+ Printf.printf+ "distribution,threshold,n_contents,store_bytes,write_ms,read_p50_us,read_p99_us,read_p999_us,inlined,non_inlined,size_stats\n"++let print_csv_row ~dist_name ~config+ (store_size, write_time, p50, p99, p999, inlined, non_inlined, sizes) =+ let threshold_str =+ if config.inline_contents then string_of_int config.inline_threshold+ else "off"+ in+ Printf.printf "%s,%s,%d,%d,%.2f,%.2f,%.2f,%.2f,%d,%d,\"%s\"\n" dist_name+ threshold_str config.n_contents store_size write_time p50 p99 p999 inlined+ non_inlined(Distribution.describe sizes)File "bench/irmin-pack/bench_inlined_contents.ml", line 1, characters 0-0:diff --git a/_build/default/bench/irmin-pack/bench_inlined_contents.ml b/_build/default/bench/irmin-pack/.formatted/bench_inlined_contents.mlindex d6c89ef..37b9ded 100644--- a/_build/default/bench/irmin-pack/bench_inlined_contents.ml+++ b/_build/default/bench/irmin-pack/.formatted/bench_inlined_contents.ml@@ -36,9 +36,15 @@ let time_it name f =(ms, result)(* Content generators *)-let small_content i = Bytes.of_string (Printf.sprintf "v%d" (i mod 1000)) (* 2-4 bytes, will be inlined *)-let medium_content i = Bytes.of_string (Printf.sprintf "value_%06d" i) (* 12 bytes, will be inlined *)-let large_content i = Bytes.of_string (Printf.sprintf "large_content_value_%010d" i) (* 30 bytes, NOT inlined *)+let small_content i = Bytes.of_string (Printf.sprintf "v%d" (i mod 1000))++(* 2-4 bytes, will be inlined *)+let medium_content i = Bytes.of_string (Printf.sprintf "value_%06d" i)+(* 12 bytes, will be inlined *)++let large_content i =+ Bytes.of_string (Printf.sprintf "large_content_value_%010d" i)+(* 30 bytes, NOT inlined *)type content_size = Small | Medium | Large@@ -60,63 +66,65 @@ type config = {content_size : content_size;}-let _default_config = {- num_entries = 1000;- num_commits = 10;- content_size = Small;-}+let _default_config =+ { num_entries = 1000; num_commits = 10; content_size = Small }(* Run a single benchmark *)let run_benchmark ~sw ~fs ~inline_contents ~config root =Eio.Path.rmtree ~missing_ok:true root;let store_config =Irmin_pack.config ~sw ~fs ~fresh:true- ~indexing_strategy:Irmin_pack.Indexing_strategy.minimal- ~inline_contents root+ ~indexing_strategy:Irmin_pack.Indexing_strategy.minimal ~inline_contents+ rootinlet repo = Store.Repo.v store_config in(* Benchmark: Write phase *)let write_time, commits =- time_it (Printf.sprintf "Write (%d commits x %d entries)"- config.num_commits config.num_entries)- (fun () ->- let commits = ref [] in- for commit_idx = 0 to config.num_commits - 1 do- let tree = Store.Tree.empty () in- let tree =- let rec add_entries tree i =- if i >= config.num_entries then tree- else- let key = [ Printf.sprintf "dir%d" (i / 100);- Printf.sprintf "file%d" i ] in- let value = content_of_size config.content_size- (commit_idx * config.num_entries + i) in- let tree = Store.Tree.add tree key value in- add_entries tree (i + 1)- in- add_entries tree 0- in- let commit = Store.Commit.v repo ~parents:[] ~info tree in- commits := commit :: !commits- done;- !commits)+ time_it+ (Printf.sprintf "Write (%d commits x %d entries)" config.num_commits+ config.num_entries) (fun () ->+ let commits = ref [] in+ for commit_idx = 0 to config.num_commits - 1 do+ let tree = Store.Tree.empty () in+ let tree =+ let rec add_entries tree i =+ if i >= config.num_entries then tree+ else+ let key =+ [+ Printf.sprintf "dir%d" (i / 100); Printf.sprintf "file%d" i;+ ]+ in+ let value =+ content_of_size config.content_size+ ((commit_idx * config.num_entries) + i)+ in+ let tree = Store.Tree.add tree key value in+ add_entries tree (i + 1)+ in+ add_entries tree 0+ in+ let commit = Store.Commit.v repo ~parents:[] ~info tree in+ commits := commit :: !commits+ done;+ !commits)in(* Benchmark: Read phase - read all contents from last commit *)let read_time, () =- time_it (Printf.sprintf "Read (%d entries)" config.num_entries)- (fun () ->- match commits with- | [] -> ()- | commit :: _ ->- let tree = Store.Commit.tree commit in- for i = 0 to config.num_entries - 1 do- let key = [ Printf.sprintf "dir%d" (i / 100);- Printf.sprintf "file%d" i ] in- let _ = Store.Tree.find tree key in- ()- done)+ time_it (Printf.sprintf "Read (%d entries)" config.num_entries) (fun () ->+ match commits with+ | [] -> ()+ | commit :: _ ->+ let tree = Store.Commit.tree commit in+ for i = 0 to config.num_entries - 1 do+ let key =+ [ Printf.sprintf "dir%d" (i / 100); Printf.sprintf "file%d" i ]+ in+ let _ = Store.Tree.find tree key in+ ()+ done)in(* Get storage size *)@@ -131,7 +139,8 @@ let run ~sw ~fs ~config =let root_with_inline = Eio.Path.(fs / "_bench" / "inline-yes") inFmt.pr "@.=== Benchmark: %s contents, %d commits x %d entries ===@.@."- (string_of_size config.content_size) config.num_commits config.num_entries;+ (string_of_size config.content_size)+ config.num_commits config.num_entries;Fmt.pr "--- Without inlining ---@.";let no_inline_write, no_inline_read, no_inline_size =@@ -153,8 +162,9 @@ let run ~sw ~fs ~config =((with_inline_read -. no_inline_read) /. no_inline_read *. 100.0);Fmt.pr "Store size: %d MB (no inline) vs %d MB (inline) [%.1f%%]@."no_inline_size with_inline_size- (Float.of_int (with_inline_size - no_inline_size) /.- Float.of_int (max 1 no_inline_size) *. 100.0);+ (Float.of_int (with_inline_size - no_inline_size)+ /. Float.of_int (max 1 no_inline_size)+ *. 100.0);()(* Command line interface *)@@ -169,7 +179,9 @@ let num_commits =Arg.(value & opt int 10 & info [ "c"; "num-commits" ] ~doc)let content_size =- let doc = "Content size: small (2-4 bytes), medium (12 bytes), or large (30 bytes)" in+ let doc =+ "Content size: small (2-4 bytes), medium (12 bytes), or large (30 bytes)"+ inlet sizes = [ ("small", Small); ("medium", Medium); ("large", Large) ] inArg.(value & opt (enum sizes) Small & info [ "s"; "size" ] ~doc)@@ -186,6 +198,7 @@ let main () num_entries num_commits content_size =let cmd =let doc = "Benchmark inline contents performance" inlet info = Cmd.info "bench-inlined-contents" ~doc in- Cmd.v info Term.(const main $ const () $ num_entries $ num_commits $ content_size)+ Cmd.v info+ Term.(const main $ const () $ num_entries $ num_commits $ content_size)let () = exit (Cmd.eval cmd)File "src/irmin/tree.ml", line 1, characters 0-0:diff --git a/_build/default/src/irmin/tree.ml b/_build/default/src/irmin/.formatted/tree.mlindex 704dd05..28aea87 100644--- a/_build/default/src/irmin/tree.ml+++ b/_build/default/src/irmin/.formatted/tree.ml@@ -510,7 +510,8 @@ module Make (P : Backend.S) = structtype portable = Portable.t [@@deriving irmin ~equal ~pp](* [elt] is a tree *)- type elt = [ `Node of t * Contents.t list | `Contents of Contents.t * Metadata.t ]+ type elt =+ [ `Node of t * Contents.t list | `Contents of Contents.t * Metadata.t ]and update = Add of elt | Removeand updatemap = update StepMap.t@@ -1370,10 +1371,8 @@ module Make (P : Backend.S) = struct| `pruned ]Scan.t)with- | Map m ->- of_map m- | Repo_value (repo, v) ->- of_value repo v+ | Map m -> of_map m+ | Repo_value (repo, v) -> of_value repo v| Repo_key (repo, k) ->let v = value_of_key ~cache t repo k inlet v = get_ok ctx v in@@ -1383,15 +1382,13 @@ module Make (P : Backend.S) = struct| Some (Add v) -> Some v| Some Remove -> None| None -> of_value repo v)- | Portable p ->- of_portable p+ | Portable p -> of_portable p| Portable_dirty (p, um) -> (match StepMap.find_opt step um with| Some (Add v) -> Some v| Some Remove -> None| None -> of_portable p)- | Pruned h ->- pruned_hash_exn ctx h+ | Pruned h -> pruned_hash_exn ctx hinmatch Atomic.get t.info.findv_cache with| None -> of_t ()@@ -1716,21 +1713,15 @@ module Make (P : Backend.S) = struct| `pruned ]Scan.t)with- | Map m ->- of_map m- | Repo_value (repo, v) ->- of_value repo v StepMap.empty+ | Map m -> of_map m+ | Repo_value (repo, v) -> of_value repo v StepMap.empty| Repo_key (repo, k) ->let v = value_of_key ~cache:true t repo k |> get_ok "update" inof_value repo v StepMap.empty- | Value_dirty (repo, v, um) ->- of_value repo v um- | Portable p ->- of_portable p StepMap.empty- | Portable_dirty (p, um) ->- of_portable p um- | Pruned h ->- pruned_hash_exn "update" h+ | Value_dirty (repo, v, um) -> of_value repo v um+ | Portable p -> of_portable p StepMap.empty+ | Portable_dirty (p, um) -> of_portable p um+ | Pruned h -> pruned_hash_exn "update" hlet remove t step = update t step Removelet add t step v = update t step (Add v)@@ -1832,8 +1823,7 @@ module Make (P : Backend.S) = struct[@@deriving irmin ~equal]type t =- [ `Node of node * Contents.t list- | `Contents of Contents.t * Metadata.t ]+ [ `Node of node * Contents.t list | `Contents of Contents.t * Metadata.t ][@@deriving irmin]let to_backend_node n =@@ -2017,8 +2007,7 @@ module Make (P : Backend.S) = struct[%log.debug "Tree.seq %a" pp_path path];sub ~cache "seq.sub" t path |> function| None -> Seq.empty- | Some n ->- Node.seq ?offset ?length ~cache n |> get_ok "seq"+ | Some n -> Node.seq ?offset ?length ~cache n |> get_ok "seq"let list t ?offset ?length ?(cache = true) path =seq t ?offset ?length ~cache path |> List.of_seq@@ -2286,7 +2275,8 @@ module Make (P : Backend.S) = structlet to_bin = Type.(unstage (to_bin_string P.Contents.Val.t)) inlet bytes = to_bin v in(* Add 2 bytes for variant tag and length prefix overhead *)- if String.length bytes + 2 < !inline_contents_max_bytes then Some bytes+ if String.length bytes + 2 < !inline_contents_max_bytes then+ Some byteselse Nonein@@ -2316,8 +2306,8 @@ module Make (P : Backend.S) = struct| Some k -> Some (step, `Contents (k, m))| None ->assertion_failure- "Encountered child contents value with uncached key \- during export:@,\+ "Encountered child contents value with uncached \+ key during export:@,\@ @[%a@]"dump v)))in@@ -2347,14 +2337,15 @@ module Make (P : Backend.S) = struct| Add (`Contents (c, m) as v) -> ((* Check if contents should be inlined at export time *)match should_inline_contents c with- | Some bytes -> P.Node.Val.add acc k (`Contents_inlined (bytes, m))+ | Some bytes ->+ P.Node.Val.add acc k (`Contents_inlined (bytes, m))| None -> (match Contents.cached_key c with| Some ptr -> P.Node.Val.add acc k (`Contents (ptr, m))| None ->assertion_failure- "Encountered child contents value 3 with uncached key \- during export:@,\+ "Encountered child contents value 3 with uncached \+ key during export:@,\@ @[%a@]"dump v)))updates v@@ -2676,8 +2667,7 @@ module Make (P : Backend.S) = structlet rec concrete : type r. concrete -> (t or_empty, r) cont =fun t k ->match t with- | `Contents (c, m) ->- k (Non_empty (of_contents ~metadata:m c))+ | `Contents (c, m) -> k (Non_empty (of_contents ~metadata:m c))| `Tree childs ->tree StepMap.empty childs (function| Empty -> k Empty@@ -2758,8 +2748,7 @@ module Make (P : Backend.S) = structmatch t with| `Node (n, il) ->`Node (Node.hash ~cache n, List.map (Contents.hash ~cache) il)- | `Contents (c, m) ->- `Contents (Contents.hash ~cache c, m)+ | `Contents (c, m) -> `Contents (Contents.hash ~cache c, m)let stats ?(force = false) (t : t) =let cache = true inFile "dune", line 1, characters 0-0:diff --git a/_build/default/dune b/_build/default/.formatted/duneindex ed29bf8..34f5fc8 100644--- a/_build/default/dune+++ b/_build/default/.formatted/dune@@ -8,5 +8,12 @@(files README.md)(package irmin-cli)(deps %{bin:irmin})- (libraries irmin irmin-cli irmin-git irmin-git.unix eio eio_main eio.unix+ (libraries+ irmin+ irmin-cli+ irmin-git+ irmin-git.unix+ eio+ eio_main+ eio.unixlwt_eio))File "src/irmin-pack-tools/tezos_explorer/show.ml", line 1, characters 0-0:diff --git a/_build/default/src/irmin-pack-tools/tezos_explorer/show.ml b/_build/default/src/irmin-pack-tools/tezos_explorer/.formatted/show.mlindex 186b71a..c85f91b 100644--- a/_build/default/src/irmin-pack-tools/tezos_explorer/show.ml+++ b/_build/default/src/irmin-pack-tools/tezos_explorer/.formatted/show.ml@@ -595,8 +595,10 @@ let show_inode c (inode : Files.Inode.compress) =| Contents_inlined_value (n, bytes, ()) ->let img1 = string A.(fg lightred ++ st bold) "Contents (inlined):" inlet img2 = name n in- let content = strf ~attr:A.(fg lightwhite) "%d bytes" (String.length bytes) in- ( img1 <-> (void 2 0 <|> (img2 <-> content)), [] )+ let content =+ strf ~attr:A.(fg lightwhite) "%d bytes" (String.length bytes)+ in+ (img1 <-> (void 2 0 <|> (img2 <-> content)), [])| Node (n, addr) ->let node, node_button = addr_show addr inlet img1 = string A.(fg lightred ++ st bold) "Node:" inFile "src/irmin-pack/inode.ml", line 1, characters 0-0:diff --git a/_build/default/src/irmin-pack/inode.ml b/_build/default/src/irmin-pack/.formatted/inode.mlindex d6d6e25..75a79af 100644--- a/_build/default/src/irmin-pack/inode.ml+++ b/_build/default/src/irmin-pack/.formatted/inode.ml@@ -2459,10 +2459,7 @@ struct~mem:(Pack.unsafe_mem t) vlet hash_exn = Val.hash_exn-- let add t v =- save t v-+ let add t v = save t vlet equal_hash = Irmin.Type.(unstage (equal H.t))let check_hash expected got =File "src/irmin-test/store.ml", line 1, characters 0-0:diff --git a/_build/default/src/irmin-test/store.ml b/_build/default/src/irmin-test/.formatted/store.mlindex 2360957..ed2714d 100644--- a/_build/default/src/irmin-test/store.ml+++ b/_build/default/src/irmin-test/.formatted/store.ml@@ -2390,9 +2390,7 @@ module Make (S : Generic_key) = structlet node_b =S.Tree.destruct tree- |> ( function- | `Contents _ -> assert false- | `Node (n, _il) -> n )+ |> ( function `Contents _ -> assert false | `Node (n, _il) -> n )|> S.to_backend_nodeinlet node_ph = pre_hash_of S.Backend.Node.Val.t node_b indune build @fmt failed"/usr/bin/env" "bash" "-c" "opam exec -- dune build @fmt --ignore-promoted-rules || (echo "dune build @fmt failed"; exit 2)" failed with exit status 22026-01-22 11:57.23: Job failed: Failed: Build failed