diff --git a/src/irmin-chunk/irmin_chunk.ml b/src/irmin-chunk/irmin_chunk.ml index 050bae281e..1e4a5798e5 100644 --- a/src/irmin-chunk/irmin_chunk.ml +++ b/src/irmin-chunk/irmin_chunk.ml @@ -122,9 +122,9 @@ struct type key = CA.key type value = V.t - type t = { + type 'a t = { chunking : [`Max | `Best_fit]; - db : CA.t; (* An handler to the underlying database. *) + db : 'a CA.t; (* An handler to the underlying database. *) chunk_size : int; (* the size of chunks. *) max_children: int; (* the maximum number of children a node can have. *) max_data : int; (* the maximum length (in bytes) of data stored in one @@ -205,6 +205,8 @@ struct CA.v config >|= fun db -> { chunking; db; chunk_size; max_children; max_data } + let batch t f = CA.batch t.db (fun db -> f { t with db }) + let find_leaves t key = AO.find t.db key >>= function | None -> Lwt.return [] diff --git a/src/irmin-fs/irmin_fs.ml b/src/irmin-fs/irmin_fs.ml index 09a15cdec4..d12fe2fb35 100644 --- a/src/irmin-fs/irmin_fs.ml +++ b/src/irmin-fs/irmin_fs.ml @@ -56,7 +56,7 @@ struct type value = V.t - type t = { + type 'a t = { path: string; } @@ -69,6 +69,9 @@ struct IO.mkdir path >|= fun () -> { path } + let cast t = (t :> [`Read | `Write] t) + let batch t f = f (cast t) + let file_of_key { path; _ } key = path / S.file_of_key (Irmin.Type.to_string K.t key) @@ -149,7 +152,7 @@ struct module RO = Read_only_ext(IO)(S)(K)(V) module W = Irmin.Private.Watch.Make(K)(V) - type t = { t: RO.t; w: W.t } + type t = { t: unit RO.t; w: W.t } type key = RO.key type value = RO.value type watch = W.watch * (unit -> unit Lwt.t) diff --git a/src/irmin-git/irmin_git.ml b/src/irmin-git/irmin_git.ml index e5922c1e31..b1f42affb2 100644 --- a/src/irmin-git/irmin_git.ml +++ b/src/irmin-git/irmin_git.ml @@ -101,7 +101,7 @@ module Make_private module Content_addressable (V: V) = struct - type t = G.t + type 'a t = G.t type key = H.t type value = V.t @@ -847,6 +847,8 @@ module Make_ext let node_t t = contents_t t, t.g let commit_t t = node_t t, t.g + let batch t f = f (contents_t t) (node_t t) (commit_t t) + type config = { root : string; dot_git: string option; @@ -984,7 +986,7 @@ module Content_addressable (G: Git.S) (V: Irmin.Type.S) = struct let state t = M.repo_of_git t >|= fun r -> M.Private.Repo.contents_t r - type t = G.t + type 'a t = G.t type key = X.key type value = X.value let with_state f t x = state t >>= fun t -> f t x diff --git a/src/irmin-git/irmin_git.mli b/src/irmin-git/irmin_git.mli index 941bd78185..5164068a94 100644 --- a/src/irmin-git/irmin_git.mli +++ b/src/irmin-git/irmin_git.mli @@ -33,7 +33,7 @@ val dot_git: string option Irmin.Private.Conf.key module Content_addressable (G: Git.S) (V: Irmin.Type.S) : Irmin.CONTENT_ADDRESSABLE_STORE - with type t = G.t + with type 'a t = G.t and type key = G.Hash.t and type value = V.t diff --git a/src/irmin-http/irmin_http.ml b/src/irmin-http/irmin_http.ml index 49b0781929..428986c923 100644 --- a/src/irmin-http/irmin_http.ml +++ b/src/irmin-http/irmin_http.ml @@ -193,7 +193,7 @@ struct module HTTP = Helper (Client) - type t = { uri: Uri.t; item: string; items: string; ctx: Client.ctx option } + type 'a t = { uri: Uri.t; item: string; items: string; ctx: Client.ctx option } let uri t = t.uri let item t = t.item let items t = t.items @@ -216,6 +216,12 @@ struct if Cohttp.Response.status r = `Not_found then Lwt.return_false else Lwt.return_true) + let cast t = (t :> [`Read|`Write] t) + + let batch t f = + (* TODO:cache the writes locally and send everything in one batch *) + f (cast t) + let v ?ctx uri item items = Lwt.return { uri; item; items; ctx } @@ -250,7 +256,7 @@ module RW (Client: Cohttp_lwt.S.Client) let empty_cache () = { stop = fun () -> (); } - type t = { t: RO.t; w: W.t; keys: cache; glob: cache } + type t = { t: unit RO.t; w: W.t; keys: cache; glob: cache } let get t = HTTP.call `GET (RO.uri t.t) t.t.ctx let put t = HTTP.call `PUT (RO.uri t.t) t.t.ctx @@ -385,13 +391,15 @@ module Make struct module X = struct module Hash = H - module XContents = struct - module Key = H - module Val = C - include AO(Client)(H)(C) - let v ?ctx config = v ?ctx config "blob" "blobs" + module Contents = struct + module X = struct + module Key = H + module Val = C + include AO(Client)(H)(C) + end + include Irmin.Contents.Store(X) + let v ?ctx config = X.v ?ctx config "blob" "blobs" end - module Contents = Irmin.Contents.Store(XContents) module Node = struct module X = struct module Key = H @@ -421,9 +429,9 @@ struct module Repo = struct type t = { config: Irmin.config; - contents: Contents.t; - node: Node.t; - commit: Commit.t; + contents: [`Read] Contents.t; + node: [`Read] Node.t; + commit: [`Read] Commit.t; branch: Branch.t; } let branch_t t = t.branch @@ -431,10 +439,18 @@ struct let node_t t = t.node let contents_t t = t.contents + let batch t f = + Contents.X.batch t.contents @@ fun contents_t -> + Node.X.batch (snd t.node) @@ fun node_t -> + Commit.X.batch (snd t.commit) @@ fun commit_t -> + let node_t = contents_t, node_t in + let commit_t = node_t, commit_t in + f contents_t node_t commit_t + let v config = let uri = get_uri config in let ctx = Client.ctx () in - XContents.v ?ctx uri >>= fun contents -> + Contents.v ?ctx uri >>= fun contents -> Node.v ?ctx uri >>= fun node -> Commit.v ?ctx uri >>= fun commit -> Branch.v ?ctx uri >|= fun branch -> diff --git a/src/irmin-http/irmin_http_server.ml b/src/irmin-http/irmin_http_server.ml index 94a70a3e3d..d71ee17bed 100644 --- a/src/irmin-http/irmin_http_server.ml +++ b/src/irmin-http/irmin_http_server.ml @@ -56,7 +56,11 @@ module Make (HTTP: Cohttp_lwt.S.Server) (S: Irmin.S) = struct let err = Fmt.strf "Parse error %S: %s" str e in Wm.respond ~body:(`String err) 400 rd - module Content_addressable (S: Irmin.CONTENT_ADDRESSABLE_STORE) + module Content_addressable + (S: sig + include Irmin.CONTENT_ADDRESSABLE_STORE + val batch: P.Repo.t -> ([`Read|`Write] t -> 'a Lwt.t) -> 'a Lwt.t + end) (K: Irmin.Type.S with type t = S.key) (V: Irmin.Type.S with type t = S.value) = struct @@ -66,7 +70,7 @@ module Make (HTTP: Cohttp_lwt.S.Server) (S: Irmin.S) = struct | Ok key -> f key | Error _ -> Wm.respond 404 rd - class items db = object + class items repo = object inherit resource method! allowed_methods rd = Wm.continue [`POST] rd @@ -82,6 +86,7 @@ module Make (HTTP: Cohttp_lwt.S.Server) (S: Irmin.S) = struct match Irmin.Type.of_string V.t body with | Error e -> parse_error rd body e | Ok body -> + S.batch repo @@ fun db -> S.add db body >>= fun new_id -> let resp_body = `String (Irmin.Type.to_string K.t new_id) in Wm.continue true { rd with Wm.Rd.resp_body } @@ -289,9 +294,21 @@ module Make (HTTP: Cohttp_lwt.S.Server) (S: Irmin.S) = struct end - module Blob = Content_addressable(P.Contents)(P.Contents.Key)(P.Contents.Val) - module Tree = Content_addressable(P.Node)(P.Node.Key)(P.Node.Val) - module Commit = Content_addressable(P.Commit)(P.Commit.Key)(P.Commit.Val) + module Blob = Content_addressable(struct + include P.Contents + let batch t f = P.Repo.batch t @@ fun x _ _ -> f x + end)(P.Contents.Key)(P.Contents.Val) + + module Tree = Content_addressable(struct + include P.Node + let batch t f = P.Repo.batch t @@ fun _ x _ -> f x + end)(P.Node.Key)(P.Node.Val) + + module Commit = Content_addressable(struct + include P.Commit + let batch t f = P.Repo.batch t @@ fun _ _ x -> f x + end)(P.Commit.Key)(P.Commit.Val) + module Branch = Atomic_write(P.Branch)(P.Branch.Key)(P.Branch.Val) type repo = S.Repo.t @@ -303,11 +320,11 @@ module Make (HTTP: Cohttp_lwt.S.Server) (S: Irmin.S) = struct let commit = P.Repo.commit_t db in let branch = P.Repo.branch_t db in let routes = [ - ("/blobs" , fun () -> new Blob.items blob); + ("/blobs" , fun () -> new Blob.items db); ("/blob/:id" , fun () -> new Blob.item blob); - ("/trees" , fun () -> new Tree.items tree); + ("/trees" , fun () -> new Tree.items db); ("/tree/:id" , fun () -> new Tree.item tree); - ("/commits" , fun () -> new Commit.items commit); + ("/commits" , fun () -> new Commit.items db); ("/commit/:id", fun () -> new Commit.item commit); ("/branches" , fun () -> new Branch.items branch); ("/branch/*" , fun () -> new Branch.item branch); diff --git a/src/irmin-mem/irmin_mem.ml b/src/irmin-mem/irmin_mem.ml index 89ab9723cd..db360d3d55 100644 --- a/src/irmin-mem/irmin_mem.ml +++ b/src/irmin-mem/irmin_mem.ml @@ -28,10 +28,13 @@ module Read_only (K: Irmin.Type.S) (V: Irmin.Type.S) = struct type key = K.t type value = V.t - type t = { mutable t: value KMap.t } + type 'a t = { mutable t: value KMap.t } let map = { t = KMap.empty } let v _config = Lwt.return map + let cast t = (t :> [`Read | `Write] t) + let batch t f = f (cast t) + let pp_key = Irmin.Type.pp K.t let find { t; _ } key = @@ -62,7 +65,7 @@ module Atomic_write (K: Irmin.Type.S) (V: Irmin.Type.S) = struct module W = Irmin.Private.Watch.Make(K)(V) module L = Irmin.Private.Lock.Make(K) - type t = { t: RO.t; w: W.t; lock: L.t } + type t = { t: unit RO.t; w: W.t; lock: L.t } type key = RO.key type value = RO.value type watch = W.watch diff --git a/src/irmin-test/store.ml b/src/irmin-test/store.ml index 61b6c0a2e9..583e54f2c5 100644 --- a/src/irmin-test/store.ml +++ b/src/irmin-test/store.ml @@ -60,8 +60,12 @@ module Make (S: S) = struct let v1 = long_random_string let v2 = "" - let kv1 ~repo = P.Contents.add (P.Repo.contents_t repo) v1 - let kv2 ~repo = P.Contents.add (P.Repo.contents_t repo) v2 + let with_contents repo f = P.Repo.batch repo (fun t _ _ -> f t) + let with_node repo f = P.Repo.batch repo (fun _ t _ -> f t) + let with_commit repo f = P.Repo.batch repo (fun _ _ t -> f t) + + let kv1 ~repo = with_contents repo (fun t -> P.Contents.add t v1) + let kv2 ~repo = with_contents repo (fun t -> P.Contents.add t v2) let normal x = `Contents (x, S.Metadata.default) let b1 = "foo" @@ -69,22 +73,24 @@ module Make (S: S) = struct let n1 ~repo = kv1 ~repo >>= fun kv1 -> - Graph.v (g repo) ["x", normal kv1] + with_node repo (fun t -> Graph.v t ["x", normal kv1]) let n2 ~repo = n1 ~repo >>= fun kn1 -> - Graph.v (g repo) ["b", `Node kn1] + with_node repo (fun t -> Graph.v t ["b", `Node kn1]) let n3 ~repo = n2 ~repo >>= fun kn2 -> - Graph.v (g repo) ["a", `Node kn2] + with_node repo (fun t -> Graph.v t ["a", `Node kn2]) let n4 ~repo = n1 ~repo >>= fun kn1 -> kv2 ~repo >>= fun kv2 -> - Graph.v (g repo) ["x", normal kv2] >>= fun kn4 -> - Graph.v (g repo) ["b", `Node kn1; "c", `Node kn4] >>= fun kn5 -> - Graph.v (g repo) ["a", `Node kn5] + with_node repo (fun t -> Graph.v t ["x", normal kv2]) + >>= fun kn4 -> + with_node repo (fun t -> Graph.v t ["b", `Node kn1; "c", `Node kn4]) + >>= fun kn5 -> + with_node repo (fun t -> Graph.v t ["a", `Node kn5]) let r1 ~repo = n2 ~repo >>= fun kn2 -> @@ -161,18 +167,18 @@ module Make (S: S) = struct let check_key = check P.Contents.Key.t in let check_val = check (T.option S.contents_t) in kv2 ~repo >>= fun kv2 -> - P.Contents.add t v2 >>= fun k2' -> + with_contents repo (fun t -> P.Contents.add t v2) >>= fun k2' -> check_key "kv2" kv2 k2'; P.Contents.find t k2' >>= fun v2' -> check_val "v2" (Some v2) v2'; - P.Contents.add t v2 >>= fun k2'' -> + with_contents repo (fun t -> P.Contents.add t v2) >>= fun k2'' -> check_key "kv2" kv2 k2''; kv1 ~repo >>= fun kv1 -> - P.Contents.add t v1 >>= fun k1' -> + with_contents repo (fun t -> P.Contents.add t v1) >>= fun k1' -> check_key "kv1" kv1 k1'; - P.Contents.add t v1 >>= fun k1'' -> + with_contents repo (fun t -> P.Contents.add t v1) >>= fun k1'' -> check_key "kv1" kv1 k1''; P.Contents.find t kv1 >>= fun v1' -> check_val "v1" (Some v1) v1'; @@ -193,29 +199,29 @@ module Make (S: S) = struct let check_val = check (T.option Graph.value_t) in (* Create a node containing t1 -x-> (v1) *) - Graph.v g ["x", normal kv1] >>= fun k1 -> - Graph.v g ["x", normal kv1] >>= fun k1' -> + with_node repo (fun g -> Graph.v g ["x", normal kv1]) >>= fun k1 -> + with_node repo (fun g -> Graph.v g ["x", normal kv1]) >>= fun k1' -> check_key "k1.1" k1 k1'; P.Node.find n k1 >>= fun t1 -> - P.Node.add n (get t1) >>= fun k1''-> + with_node repo (fun n -> P.Node.add n (get t1)) >>= fun k1''-> check_key "k1.2" k1 k1''; (* Create the node t2 -b-> t1 -x-> (v1) *) - Graph.v g ["b", `Node k1] >>= fun k2 -> - Graph.v g ["b", `Node k1] >>= fun k2' -> + with_node repo (fun g -> Graph.v g ["b", `Node k1]) >>= fun k2 -> + with_node repo (fun g -> Graph.v g ["b", `Node k1]) >>= fun k2' -> check_key "k2.1" k2 k2'; P.Node.find n k2 >>= fun t2 -> - P.Node.add n (get t2) >>= fun k2''-> + with_node repo (fun n -> P.Node.add n (get t2)) >>= fun k2''-> check_key "k2.2" k2 k2''; Graph.find g k2 ["b"] >>= fun k1''' -> check_val "k1.3" (Some (`Node k1)) k1'''; (* Create the node t3 -a-> t2 -b-> t1 -x-> (v1) *) - Graph.v g ["a", `Node k2] >>= fun k3 -> - Graph.v g ["a", `Node k2] >>= fun k3' -> + with_node repo (fun g -> Graph.v g ["a", `Node k2]) >>= fun k3 -> + with_node repo (fun g -> Graph.v g ["a", `Node k2]) >>= fun k3' -> check_key "k3.1" k3 k3'; P.Node.find n k3 >>= fun t3 -> - P.Node.add n (get t3) >>= fun k3''-> + with_node repo (fun n -> P.Node.add n (get t3)) >>= fun k3''-> check_key "k3.2" k3 k3''; Graph.find g k3 ["a"] >>= fun k2'' -> check_val "k2.3" (Some (`Node k2)) k2''; @@ -235,10 +241,12 @@ module Make (S: S) = struct (* Create the node t6 -a-> t5 -b-> t1 -x-> (v1) \-c-> t4 -x-> (v2) *) kv2 ~repo >>= fun kv2 -> - Graph.v g ["x", normal kv2] >>= fun k4 -> - Graph.v g ["b", `Node k1; "c", `Node k4] >>= fun k5 -> - Graph.v g ["a", `Node k5] >>= fun k6 -> - Graph.update g k3 ["a";"c";"x"] (normal kv2) >>= fun k6' -> + with_node repo (fun g -> Graph.v g ["x", normal kv2]) >>= fun k4 -> + with_node repo (fun g -> Graph.v g ["b", `Node k1; "c", `Node k4]) + >>= fun k5 -> + with_node repo (fun g -> Graph.v g ["a", `Node k5]) >>= fun k6 -> + with_node repo (fun g -> Graph.update g k3 ["a";"c";"x"] (normal kv2)) + >>= fun k6' -> P.Node.find n k6' >>= fun n6' -> P.Node.find n k6 >>= fun n6 -> check T.(option P.Node.Val.t) "node n6" n6 n6'; @@ -252,26 +260,26 @@ module Make (S: S) = struct else names := s :: !names ) all in - Graph.v g [] >>= fun n0 -> + with_node repo (fun g -> Graph.v g []) >>= fun n0 -> - Graph.update g n0 ["b"] (`Node n0) >>= fun n1 -> - Graph.update g n1 ["a"] (`Node n0) >>= fun n2 -> - Graph.update g n2 ["a"] (`Node n0) >>= fun n3 -> + with_node repo (fun g -> Graph.update g n0 ["b"] (`Node n0)) >>= fun n1 -> + with_node repo (fun g -> Graph.update g n1 ["a"] (`Node n0)) >>= fun n2 -> + with_node repo (fun g -> Graph.update g n2 ["a"] (`Node n0)) >>= fun n3 -> assert_no_duplicates "1" n3 >>= fun () -> - Graph.update g n0 ["a"] (`Node n0) >>= fun n1 -> - Graph.update g n1 ["b"] (`Node n0) >>= fun n2 -> - Graph.update g n2 ["a"] (`Node n0) >>= fun n3 -> + with_node repo (fun g -> Graph.update g n0 ["a"] (`Node n0)) >>= fun n1 -> + with_node repo (fun g -> Graph.update g n1 ["b"] (`Node n0)) >>= fun n2 -> + with_node repo (fun g -> Graph.update g n2 ["a"] (`Node n0)) >>= fun n3 -> assert_no_duplicates "2" n3 >>= fun () -> - Graph.update g n0 ["b"] (normal kv1) >>= fun n1 -> - Graph.update g n1 ["a"] (normal kv1) >>= fun n2 -> - Graph.update g n2 ["a"] (normal kv1) >>= fun n3 -> + with_node repo (fun g -> Graph.update g n0 ["b"] (normal kv1)) >>= fun n1 -> + with_node repo (fun g -> Graph.update g n1 ["a"] (normal kv1)) >>= fun n2 -> + with_node repo (fun g -> Graph.update g n2 ["a"] (normal kv1)) >>= fun n3 -> assert_no_duplicates "3" n3 >>= fun () -> - Graph.update g n0 ["a"] (normal kv1) >>= fun n1 -> - Graph.update g n1 ["b"] (normal kv1) >>= fun n2 -> - Graph.update g n2 ["b"] (normal kv1) >>= fun n3 -> + with_node repo (fun g -> Graph.update g n0 ["a"] (normal kv1)) >>= fun n1 -> + with_node repo (fun g -> Graph.update g n1 ["b"] (normal kv1)) >>= fun n2 -> + with_node repo (fun g -> Graph.update g n2 ["b"] (normal kv1)) >>= fun n3 -> assert_no_duplicates "4" n3 >>= fun () -> Lwt.return_unit @@ -287,29 +295,29 @@ module Make (S: S) = struct in kv1 ~repo >>= fun kv1 -> - let g = g repo and h = h repo and c = P.Repo.commit_t repo in + let h = h repo and c = P.Repo.commit_t repo in let check_val = check (T.option P.Commit.Val.t) in let check_key = check P.Commit.Key.t in let check_keys = checks P.Commit.Key.t in (* t3 -a-> t2 -b-> t1 -x-> (v1) *) - Graph.v g ["x", normal kv1] >>= fun kt1 -> - Graph.v g ["a", `Node kt1] >>= fun kt2 -> - Graph.v g ["b", `Node kt2] >>= fun kt3 -> + with_node repo (fun g -> Graph.v g ["x", normal kv1]) >>= fun kt1 -> + with_node repo (fun g -> Graph.v g ["a", `Node kt1]) >>= fun kt2 -> + with_node repo (fun g -> Graph.v g ["b", `Node kt2]) >>= fun kt3 -> (* r1 : t2 *) - let with_info n fn = fn h ~info:(info n) in - with_info 3 @@ History.v ~node:kt2 ~parents:[] >>= fun (kr1, _) -> - with_info 3 @@ History.v ~node:kt2 ~parents:[] >>= fun (kr1',_) -> + let with_info n fn = with_commit repo (fun h -> fn h ~info:(info n)) in + with_info 3 (History.v ~node:kt2 ~parents:[]) >>= fun (kr1, _) -> + with_info 3 (History.v ~node:kt2 ~parents:[]) >>= fun (kr1',_) -> P.Commit.find c kr1 >>= fun t1 -> P.Commit.find c kr1' >>= fun t1' -> check_val "t1" t1 t1'; check_key "kr1" kr1 kr1'; (* r1 -> r2 : t3 *) - with_info 4 @@ History.v ~node:kt3 ~parents:[kr1] >>= fun (kr2, _) -> - with_info 4 @@ History.v ~node:kt3 ~parents:[kr1] >>= fun (kr2',_) -> + with_info 4 (History.v ~node:kt3 ~parents:[kr1]) >>= fun (kr2, _) -> + with_info 4 (History.v ~node:kt3 ~parents:[kr1]) >>= fun (kr2',_) -> check_key "kr2" kr2 kr2'; History.closure h ~min:[] ~max:[kr1] >>= fun kr1s -> @@ -695,14 +703,15 @@ module Make (S: S) = struct (* merge contents *) - let v = P.Repo.contents_t repo in - Irmin.Merge.f (P.Contents.merge v) - ~old:(old (Some kv1)) (Some kv1) (Some kv1) + with_contents repo (fun v -> + Irmin.Merge.f (P.Contents.merge v) + ~old:(old (Some kv1)) (Some kv1) (Some kv1)) >>= fun kv1' -> check_result "merge kv1" (Ok (Some kv1)) kv1'; - Irmin.Merge.f (P.Contents.merge v) - ~old:(old (Some kv1)) (Some kv1) (Some kv2) + with_contents repo (fun v -> + Irmin.Merge.f (P.Contents.merge v) + ~old:(old (Some kv1)) (Some kv1) (Some kv2)) >>= fun kv2' -> check_result "merge kv2" (Ok (Some kv2)) kv2'; @@ -711,22 +720,24 @@ module Make (S: S) = struct let g = g repo in (* The empty node *) - Graph.v g [] >>= fun k0 -> + with_node repo (fun g -> Graph.v g []) >>= fun k0 -> (* Create the node t1 -x-> (v1) *) - Graph.v g ["x", normal kv1] >>= fun k1 -> + with_node repo (fun g -> Graph.v g ["x", normal kv1]) >>= fun k1 -> (* Create the node t2 -b-> t1 -x-> (v1) *) - Graph.v g ["b", `Node k1] >>= fun k2 -> + with_node repo (fun g -> Graph.v g ["b", `Node k1]) >>= fun k2 -> (* Create the node t3 -c-> t1 -x-> (v1) *) - Graph.v g ["c", `Node k1] >>= fun k3 -> + with_node repo (fun g -> Graph.v g ["c", `Node k1]) >>= fun k3 -> (* Should create the node: t4 -b-> t1 -x-> (v1) \c/ *) - Irmin.Merge.(f @@ P.Node.merge g) - ~old:(old (Some k0)) (Some k2) (Some k3) >>= fun k4 -> + with_node repo (fun g -> + Irmin.Merge.(f @@ P.Node.merge g) + ~old:(old (Some k0)) (Some k2) (Some k3)) + >>= fun k4 -> merge_exn "k4" k4 >>= fun k4 -> let k4 = match k4 with Some k -> k | None -> failwith "k4" in @@ -742,26 +753,29 @@ module Make (S: S) = struct Irmin.Info.v ~date:i ~author:"test" "Test commit" in - let h = h repo and c = P.Repo.commit_t repo in - let with_info n fn = fn h ~info:(info n) in + let c = P.Repo.commit_t repo in + let with_info n fn = with_commit repo (fun h -> fn h ~info:(info n)) in - with_info 0 @@ History.v ~node:k0 ~parents:[] >>= fun (kr0, _) -> - with_info 1 @@ History.v ~node:k2 ~parents:[kr0] >>= fun (kr1, _) -> - with_info 2 @@ History.v ~node:k3 ~parents:[kr0] >>= fun (kr2, _) -> + with_info 0 (History.v ~node:k0 ~parents:[]) >>= fun (kr0, _) -> + with_info 1 (History.v ~node:k2 ~parents:[kr0]) >>= fun (kr1, _) -> + with_info 2 (History.v ~node:k3 ~parents:[kr0]) >>= fun (kr2, _) -> with_info 3 (fun h ~info -> - Irmin.Merge.f @@ History.merge h ~info:(fun () -> info) - ) ~old:(old kr0) kr1 kr2 >>= fun kr3 -> + Irmin.Merge.f (History.merge h ~info:(fun () -> info)) + ~old:(old kr0) kr1 kr2) + >>= fun kr3 -> merge_exn "kr3" kr3 >>= fun kr3 -> with_info 4 (fun h ~info -> - Irmin.Merge.f @@ History.merge h ~info:(fun () -> info) - ) ~old:(old kr2) kr2 kr3 >>= fun kr3_id' -> + Irmin.Merge.f (History.merge h ~info:(fun () -> info)) + ~old:(old kr2) kr2 kr3) + >>= fun kr3_id' -> merge_exn "kr3_id'" kr3_id' >>= fun kr3_id' -> check S.Hash.t "kr3 id with immediate parent'" kr3 kr3_id'; with_info 5 (fun h ~info -> - Irmin.Merge.f @@ History.merge h ~info:(fun () -> info) - ) ~old:(old kr0) kr0 kr3 >>= fun kr3_id -> + Irmin.Merge.f (History.merge h ~info:(fun () -> info)) + ~old:(old kr0) kr0 kr3) + >>= fun kr3_id -> merge_exn "kr3_id" kr3_id >>= fun kr3_id -> check S.Hash.t "kr3 id with old parent" kr3 kr3_id; @@ -1429,7 +1443,9 @@ module Make (S: S) = struct let v = v2 in let t = P.Repo.contents_t repo in let write = - write (fun _i -> P.Contents.add t v >>= fun _ -> Lwt.return_unit) + write (fun _i -> + with_contents repo (fun t -> P.Contents.add t v) >>= fun _ -> + Lwt.return_unit) in let read = read diff --git a/src/irmin/commit.ml b/src/irmin/commit.ml index 93d716edef..17d9015322 100644 --- a/src/irmin/commit.ml +++ b/src/irmin/commit.ml @@ -60,14 +60,15 @@ module Store = struct module Node = N - type t = N.t * S.t + + type 'a t = 'a N.t * 'a S.t type key = S.key type value = S.value let add (_, t) = S.add t let mem (_, t) = S.mem t let find (_, t) = S.find t - let merge_node (n, _) = Merge.f (N.merge n) + let merge_node (t, _) = Merge.f (N.merge t) let pp_key = Type.pp S.Key.t @@ -128,8 +129,7 @@ module History (S: S.COMMIT_STORE) = struct type commit = S.key type node = S.Node.key - - type t = S.t + type 'a t = 'a S.t type v = S.Val.t let commit_t = S.Key.t diff --git a/src/irmin/commit.mli b/src/irmin/commit.mli index d9bbb8b16c..b00e535027 100644 --- a/src/irmin/commit.mli +++ b/src/irmin/commit.mli @@ -29,14 +29,14 @@ module Store and type node = N.key end): S.COMMIT_STORE - with type t = N.t * C.t + with type 'a t = 'a N.t * 'a C.t and type key = C.key and type value = C.value and module Key = C.Key and module Val = C.Val module History (C: S.COMMIT_STORE): - S.COMMIT_HISTORY with type t = C.t + S.COMMIT_HISTORY with type 'a t = 'a C.t and type v = C.Val.t and type node = C.Node.key and type commit = C.key diff --git a/src/irmin/contents.mli b/src/irmin/contents.mli index 72b4798003..392c7a336c 100644 --- a/src/irmin/contents.mli +++ b/src/irmin/contents.mli @@ -46,6 +46,6 @@ module Store module Val: S.CONTENTS with type t = value end): S.CONTENTS_STORE - with type t = C.t + with type 'a t = 'a C.t and type key = C.key and type value = C.value diff --git a/src/irmin/irmin.ml b/src/irmin/irmin.ml index a8133842ad..459bf9919e 100644 --- a/src/irmin/irmin.ml +++ b/src/irmin/irmin.ml @@ -43,14 +43,19 @@ end module type APPEND_ONLY_STORE = sig - include S.READ_ONLY_STORE - val add: t -> key -> value -> unit Lwt.t + type 'a t + type key + type value + val mem : [> `Read] t -> key -> bool Lwt.t + val find: [> `Read] t -> key -> value option Lwt.t + val add: [> `Write] t -> key -> value -> unit Lwt.t end module type APPEND_ONLY_STORE_MAKER = functor (K: Type.S) (V: Type.S) -> sig include APPEND_ONLY_STORE with type key = K.t and type value = V.t - val v: Conf.t -> t Lwt.t + val batch: [`Read] t -> ([`Read | `Write] t -> 'a Lwt.t) -> 'a Lwt.t + val v: Conf.t -> [`Read] t Lwt.t end module Content_addressable (AO: APPEND_ONLY_STORE_MAKER) @@ -97,58 +102,82 @@ struct module X = struct module Hash = H - module XContents = struct - include CA(H)(C) + module Val = Contents.String + module Values = CA (Hash)(Val) + module Make (V: Type.S) = struct module Key = H - module Val = C + type 'a t = 'a Values.t + type key = Key.t + type value = V.t + let add t v = Values.add t (Type.encode_bin V.t v) + let find t k = Values.find t k >|= function + | None -> None + | Some v -> match Type.decode_bin V.t v with + | Ok v -> Some v + | _ -> None + let mem t k = + (* normally we should also check that [find] returns a value + of the right type, but type mismatch here is not supposed to + happen. *) + Values.mem t k end - module Contents = Contents.Store(XContents) + module Contents = struct + module CA = struct + module Val = C + include Make(Val) + end + include Contents.Store(CA) + end + module Node = struct module CA = struct - module Key = H module Val = N - include CA (Key)(Val) + include Make(Val) end include Node.Store(Contents)(P)(M)(CA) - let v = CA.v end + module Commit = struct module CA = struct - module Key = H module Val = CT - include CA (Key)(Val) + include Make(Val) end include Commit.Store(Node)(CA) - let v = CA.v end + module Branch = struct module Key = B module Val = H include AW (Key)(Val) end + module Slice = Slice.Make(Contents)(Node)(Commit) module Sync = Sync.None(H)(B) module Repo = struct + type t = { config: Conf.t; - contents: Contents.t; - node: Node.t; - commit: Commit.t; + values: [`Read] Values.t; branch: Branch.t; } + + let contents_t t = t.values + let node_t t = t.values, t.values + let commit_t t = (t.values, t.values), t.values let branch_t t = t.branch - let commit_t t = t.commit - let node_t t = t.node - let contents_t t = t.contents + + let batch t f = + Values.batch t.values @@ fun t -> + let contents_t = t in + let node_t = contents_t, t in + let commit_t = node_t, t in + f contents_t node_t commit_t let v config = - XContents.v config >>= fun contents -> - Node.v config >>= fun node -> - Commit.v config >>= fun commit -> - Branch.v config >|= fun branch -> - let node = contents, node in - let commit = node, commit in - { contents; node; commit; branch; config } + Values.v config >>= fun values -> + Branch.v config >|= fun branch -> + { values; branch; config } + end end include Store.Make(X) @@ -170,7 +199,6 @@ end module Of_private = Store.Make -module type READ_ONLY_STORE = S.READ_ONLY_STORE module type CONTENT_ADDRESSABLE_STORE = S.CONTENT_ADDRESSABLE_STORE module type ATOMIC_WRITE_STORE = S.ATOMIC_WRITE_STORE module type TREE = S.TREE diff --git a/src/irmin/irmin.mli b/src/irmin/irmin.mli index b9b99e690a..13137cdff5 100644 --- a/src/irmin/irmin.mli +++ b/src/irmin/irmin.mli @@ -795,13 +795,18 @@ type 'a diff = 'a Diff.t are provided by various backends. *) -(** Read-only backend stores. *) -module type READ_ONLY_STORE = sig +(** Content-addressable backend store. *) +module type CONTENT_ADDRESSABLE_STORE = sig - (** {1 Read-only stores} *) + (** {1 Content-addressable stores} - type t - (** The type for read-only backend stores. *) + Content-addressable stores are store where it is possible to read + and add new values. Keys are derived from the values raw contents + and hence are deterministic. *) + + type 'a t + (** The type for content-addressable backend stores. The ['a] + phantom type carries information about the store mutability. *) type key (** The type for keys. *) @@ -809,27 +814,14 @@ module type READ_ONLY_STORE = sig type value (** The type for raw values. *) - val mem: t -> key -> bool Lwt.t + val mem: [> `Read] t -> key -> bool Lwt.t (** [mem t k] is true iff [k] is present in [t]. *) - val find: t -> key -> value option Lwt.t + val find: [> `Read] t -> key -> value option Lwt.t (** [find t k] is [Some v] if [k] is associated to [v] in [t] and [None] is [k] is not present in [t]. *) -end - -(** Content-addressable backend store. *) -module type CONTENT_ADDRESSABLE_STORE = sig - - (** {1 Content-addressable stores} - - Content-addressable stores are store where it is possible to read - and add new values. Keys are derived from the values raw contents - and hence are deterministic. *) - - include READ_ONLY_STORE - - val add: t -> value -> key Lwt.t + val add: [> `Write] t -> value -> key Lwt.t (** Write the contents of a value to the store. It's the responsibility of the content-addressable store to generate a consistent key. *) @@ -844,9 +836,24 @@ module type APPEND_ONLY_STORE = sig Append-onlye stores are store where it is possible to read and add new values. *) - include READ_ONLY_STORE + type 'a t + (** The type for append-only backend stores. The ['a] + phantom type carries information about the store mutability. *) - val add: t -> key -> value -> unit Lwt.t + type key + (** The type for keys. *) + + type value + (** The type for raw values. *) + + val mem: [> `Read] t -> key -> bool Lwt.t + (** [mem t k] is true iff [k] is present in [t]. *) + + val find: [> `Read] t -> key -> value option Lwt.t + (** [find t k] is [Some v] if [k] is associated to [v] in [t] and + [None] is [k] is not present in [t]. *) + + val add: [> `Write] t -> key -> value -> unit Lwt.t (** Write the contents of a value to the store. *) end @@ -859,7 +866,21 @@ module type ATOMIC_WRITE_STORE = sig Atomic-write stores are stores where it is possible to read, update and remove elements, with atomically guarantees. *) - include READ_ONLY_STORE + type t + (** The type for atomic-write backend stores. *) + + type key + (** The type for keys. *) + + type value + (** The type for raw values. *) + + val mem: t -> key -> bool Lwt.t + (** [mem t k] is true iff [k] is present in [t]. *) + + val find: t -> key -> value option Lwt.t + (** [find t k] is [Some v] if [k] is associated to [v] in [t] and + [None] is [k] is not present in [t]. *) val set: t -> key -> value -> unit Lwt.t (** [set t k v] replaces the contents of [k] by [v] in [t]. If [k] @@ -1116,7 +1137,7 @@ module Contents: sig include CONTENT_ADDRESSABLE_STORE - val merge: t -> key option Merge.t + val merge: [`Read | `Write] t -> key option Merge.t (** [merge t] lifts the merge functions defined on contents values to contents key. The merge function will: {e (i)} read the values associated with the given keys, {e (ii)} use the merge @@ -1140,7 +1161,7 @@ module Contents: sig module Key: Hash.S with type t = key module Val: S with type t = value end): - STORE with type t = S.t + STORE with type 'a t = 'a S.t and type key = S.key and type value = S.value @@ -1541,7 +1562,7 @@ module Private: sig module Path: Path.S (** [Path] provides base functions on node paths. *) - val merge: t -> key option Merge.t + val merge: [`Read | `Write] t -> key option Merge.t (** [merge] is the 3-way merge function for nodes keys. *) module Key: Hash.S with type t = key @@ -1574,7 +1595,7 @@ module Private: sig and type contents = C.key and type step = P.step end): - STORE with type t = C.t * S.t + STORE with type 'a t = 'a C.t * 'a S.t and type key = S.key and type value = S.value and module Path = P @@ -1589,7 +1610,7 @@ module Private: sig (** {1 Node Graphs} *) - type t + type 'a t (** The type for store handles. *) type metadata @@ -1612,30 +1633,30 @@ module Private: sig type value = [ `Node of node | `Contents of contents * metadata ] (** The type for store values. *) - val empty: t -> node Lwt.t + val empty: [> `Write] t -> node Lwt.t (** The empty node. *) - val v: t -> (step * value) list -> node Lwt.t + val v: [> `Write] t -> (step * value) list -> node Lwt.t (** [v t n] is a new node containing [n]. *) - val list: t -> node -> (step * value) list Lwt.t + val list: [> `Read] t -> node -> (step * value) list Lwt.t (** [list t n] is the contents of the node [n]. *) - val find: t -> node -> path -> value option Lwt.t + val find: [> `Read] t -> node -> path -> value option Lwt.t (** [find t n p] is the contents of the path [p] starting form [n]. *) - val update: t -> node -> path -> value -> node Lwt.t + val update: [`Read | `Write] t -> node -> path -> value -> node Lwt.t (** [update t n p v] is the node [x] such that [find t x p] is [Some v] and it behaves the same [n] for other operations. *) - val remove: t -> node -> path -> node Lwt.t + val remove: [`Read | `Write] t -> node -> path -> node Lwt.t (** [remove t n path] is the node [x] such that [find t x] is [None] and it behhaves then same as [n] for other operations. *) - val closure: t -> min:node list -> max:node list -> node list Lwt.t + val closure: [> `Read] t -> min:node list -> max:node list -> node list Lwt.t (** [closure t ~min ~max] is the transitive closure [c] of [t]'s nodes such that: @@ -1671,7 +1692,7 @@ module Private: sig end module Graph (S: STORE): GRAPH - with type t = S.t + with type 'a t = 'a S.t and type contents = S.Contents.key and type metadata = S.Val.metadata and type node = S.key @@ -1741,7 +1762,7 @@ module Private: sig include CONTENT_ADDRESSABLE_STORE - val merge: t -> info:Info.f -> key option Merge.t + val merge: [`Read | `Write] t -> info:Info.f -> key option Merge.t (** [merge] is the 3-way merge function for commit keys. *) module Key: Hash.S with type t = key @@ -1765,7 +1786,7 @@ module Private: sig and type commit = key and type node = N.key end): - STORE with type t = N.t * S.t + STORE with type 'a t = 'a N.t * 'a S.t and type key = S.key and type value = S.value and module Key = S.Key @@ -1781,7 +1802,7 @@ module Private: sig (** {1 Commit History} *) - type t + type 'a t (** The type for store handles. *) type node @@ -1793,28 +1814,28 @@ module Private: sig type v (** The type for commit objects. *) - val v: t -> node:node -> parents:commit list -> info:Info.t -> + val v: [> `Write] t -> node:node -> parents:commit list -> info:Info.t -> (commit * v) Lwt.t (** Create a new commit. *) - val parents: t -> commit -> commit list Lwt.t + val parents: [> `Read] t -> commit -> commit list Lwt.t (** Get the commit parents. Commits form a append-only, fully functional, partial-order data-structure: every commit carries the list of its immediate predecessors. *) - val merge: t -> info:Info.f -> commit Merge.t + val merge: [`Read | `Write] t -> info:Info.f -> commit Merge.t (** [merge t] is the 3-way merge function for commit. *) - val lcas: t -> ?max_depth:int -> ?n:int -> commit -> commit -> + val lcas: [> `Read] t -> ?max_depth:int -> ?n:int -> commit -> commit -> (commit list, [`Max_depth_reached | `Too_many_lcas]) result Lwt.t (** Find the lowest common ancestors {{:http://en.wikipedia.org/wiki/Lowest_common_ancestor}lca} between two commits. *) - val lca: t -> info:Info.f -> ?max_depth:int -> ?n:int -> commit list -> - (commit option, Merge.conflict) result Lwt.t + val lca: [`Read | `Write] t -> info:Info.f -> ?max_depth:int -> ?n:int -> + commit list -> (commit option, Merge.conflict) result Lwt.t (** Compute the lowest common ancestors ancestor of a list of commits by recursively calling {!lcas} and merging the results. @@ -1825,12 +1846,14 @@ module Private: sig error. *) val three_way_merge: - t -> info:Info.f -> ?max_depth:int -> ?n:int -> commit -> commit -> + [`Read | `Write] t -> info:Info.f -> ?max_depth:int -> ?n:int -> + commit -> commit -> (commit, Merge.conflict) result Lwt.t (** Compute the {!lcas} of the two commit and 3-way merge the result. *) - val closure: t -> min:commit list -> max:commit list -> commit list Lwt.t + val closure: [> `Read] t -> min:commit list -> max:commit list -> + commit list Lwt.t (** Same as {{!Private.Node.GRAPH.closure}GRAPH.closure} but for the history graph. *) @@ -1843,7 +1866,7 @@ module Private: sig (** Build a commit history. *) module History (S: STORE): HISTORY - with type t = S.t + with type 'a t = 'a S.t and type node = S.Node.key and type commit = S.key @@ -1985,10 +2008,15 @@ module Private: sig module Repo: sig type t val v: config -> t Lwt.t - val contents_t: t -> Contents.t - val node_t: t -> Node.t - val commit_t: t -> Commit.t + val contents_t: t -> [`Read] Contents.t + val node_t: t -> [`Read] Node.t + val commit_t: t -> [`Read] Commit.t val branch_t: t -> Branch.t + val batch: t -> + ([`Read | `Write] Contents.t -> + [`Read | `Write] Node.t -> + [`Read | `Write] Commit.t + -> 'a Lwt.t) -> 'a Lwt.t end (** URI-based low-level sync. *) @@ -3323,7 +3351,11 @@ sig include APPEND_ONLY_STORE with type key = K.t and type value = V.t - val v: config -> t Lwt.t + val batch: [`Read] t -> ([`Read | `Write] t -> 'a Lwt.t) -> 'a Lwt.t + (** [batch t f] applies the writes in [f] in a separate batch. The + exact guarantees depends on the backends. *) + + val v: config -> [`Read] t Lwt.t (** [v config] is a function returning fresh store handles, with the configuration [config], which is provided by the backend. *) end @@ -3336,7 +3368,11 @@ sig include CONTENT_ADDRESSABLE_STORE with type key = K.t and type value = V.t - val v: config -> t Lwt.t + val batch: [`Read] t -> ([`Read | `Write] t -> 'a Lwt.t) -> 'a Lwt.t + (** [batch t f] applies the writes in [f] in a separate batch. The + exact guarantees depends on the backends. *) + + val v: config -> [`Read] t Lwt.t (** [v config] is a function returning fresh store handles, with the configuration [config], which is provided by the backend. *) end @@ -3344,11 +3380,15 @@ end module Content_addressable (S: APPEND_ONLY_STORE_MAKER) (K: Hash.S) (V: Type.S): sig include CONTENT_ADDRESSABLE_STORE - with type t = S(K)(V).t + with type 'a t = 'a S(K)(V).t and type key = K.t and type value = V.t - val v: config -> t Lwt.t + val batch: [`Read] t -> ([`Read | `Write] t -> 'a Lwt.t) -> 'a Lwt.t + (** [batch t f] applies the writes in [f] in a separate batch. The + exact guarantees depends on the backends. *) + + val v: config -> [`Read] t Lwt.t (** [v config] is a function returning fresh store handles, with the configuration [config], which is provided by the backend. *) end diff --git a/src/irmin/node.ml b/src/irmin/node.ml index 47ba24d953..6f8408682d 100644 --- a/src/irmin/node.ml +++ b/src/irmin/node.ml @@ -132,7 +132,7 @@ struct module Path = P module Metadata = M - type t = C.t * S.t + type 'a t = 'a C.t * 'a S.t type key = S.key type value = S.value @@ -227,7 +227,7 @@ module Graph (S: S.NODE_STORE) = struct type contents = Contents.t type node = S.key type path = Path.t - type t = S.t + type 'a t = 'a S.t type value = [ `Contents of contents * metadata | `Node of node ] diff --git a/src/irmin/node.mli b/src/irmin/node.mli index 1e669da0d7..b3d464b253 100644 --- a/src/irmin/node.mli +++ b/src/irmin/node.mli @@ -39,7 +39,7 @@ module Store and type contents = C.key and type step = P.step end): - S.NODE_STORE with type t = C.t * N.t + S.NODE_STORE with type 'a t = 'a C.t * 'a N.t and type key = N.key and type value = N.value and module Path = P @@ -48,7 +48,7 @@ module Store and module Val = N.Val module Graph (N: S.NODE_STORE): - S.NODE_GRAPH with type t = N.t + S.NODE_GRAPH with type 'a t = 'a N.t and type contents = N.Contents.key and type metadata = N.Val.metadata and type node = N.key diff --git a/src/irmin/s.ml b/src/irmin/s.ml index 95550fb07f..86db643fef 100644 --- a/src/irmin/s.ml +++ b/src/irmin/s.ml @@ -44,23 +44,20 @@ module type CONTENTS = sig val merge: t option Merge.t end -module type READ_ONLY_STORE = sig - type t +module type CONTENT_ADDRESSABLE_STORE = sig + type 'a t type key type value - val mem: t -> key -> bool Lwt.t - val find: t -> key -> value option Lwt.t -end - -module type CONTENT_ADDRESSABLE_STORE = sig - include READ_ONLY_STORE - val add: t -> value -> key Lwt.t + val mem : [> `Read] t -> key -> bool Lwt.t + val find: [> `Read] t -> key -> value option Lwt.t + val add : [> `Write] t -> value -> key Lwt.t end module type CONTENT_ADDRESSABLE_STORE_MAKER = functor (K: HASH) (V: Type.S) -> sig include CONTENT_ADDRESSABLE_STORE with type key = K.t and type value = V.t - val v: Conf.t -> t Lwt.t + val batch: [`Read] t -> ([`Read | `Write] t -> 'a Lwt.t) -> 'a Lwt.t + val v: Conf.t -> [`Read] t Lwt.t end module type METADATA = sig @@ -71,7 +68,7 @@ end module type CONTENTS_STORE = sig include CONTENT_ADDRESSABLE_STORE - val merge: t -> key option Merge.t + val merge: [`Read | `Write] t -> key option Merge.t module Key: HASH with type t = key module Val: CONTENTS with type t = value end @@ -99,20 +96,20 @@ module type NODE = sig end module type NODE_GRAPH = sig - type t + type 'a t type metadata type contents type node type step type path type value = [ `Node of node | `Contents of contents * metadata ] - val empty: t -> node Lwt.t - val v: t -> (step * value) list -> node Lwt.t - val list: t -> node -> (step * value) list Lwt.t - val find: t -> node -> path -> value option Lwt.t - val update: t -> node -> path -> value -> node Lwt.t - val remove: t -> node -> path -> node Lwt.t - val closure: t -> min:node list -> max:node list -> node list Lwt.t + val empty: [> `Write] t -> node Lwt.t + val v: [> `Write] t -> (step * value) list -> node Lwt.t + val list: [> `Read] t -> node -> (step * value) list Lwt.t + val find: [> `Read] t -> node -> path -> value option Lwt.t + val update: [`Read | `Write] t -> node -> path -> value -> node Lwt.t + val remove: [`Read | `Write] t -> node -> path -> node Lwt.t + val closure: [> `Read] t -> min:node list -> max:node list -> node list Lwt.t val metadata_t: metadata Type.t val contents_t: contents Type.t val node_t: node Type.t @@ -124,7 +121,7 @@ end module type NODE_STORE = sig include CONTENT_ADDRESSABLE_STORE module Path: PATH - val merge: t -> key option Merge.t + val merge: [`Read | `Write] t -> key option Merge.t module Key: HASH with type t = key module Metadata: METADATA module Val: NODE @@ -153,7 +150,7 @@ end module type COMMIT_STORE = sig include CONTENT_ADDRESSABLE_STORE - val merge: t -> info:Info.f -> key option Merge.t + val merge: [`Read| `Write] t -> info:Info.f -> key option Merge.t module Key: HASH with type t = key module Val: COMMIT with type t = value @@ -162,20 +159,21 @@ module type COMMIT_STORE = sig end module type COMMIT_HISTORY = sig - type t + type 'a t type node type commit type v - val v: t -> node:node -> parents:commit list -> info:Info.t -> (commit * v) Lwt.t - val parents: t -> commit -> commit list Lwt.t - val merge: t -> info:Info.f -> commit Merge.t - val lcas: t -> ?max_depth:int -> ?n:int -> commit -> commit -> + val v: [> `Write] t -> node:node -> parents:commit list -> info:Info.t -> + (commit * v) Lwt.t + val parents: [> `Read] t -> commit -> commit list Lwt.t + val merge: [`Read | `Write] t -> info:Info.f -> commit Merge.t + val lcas: [> `Read] t -> ?max_depth:int -> ?n:int -> commit -> commit -> (commit list, [`Max_depth_reached | `Too_many_lcas]) result Lwt.t - val lca: t -> info:Info.f -> ?max_depth:int -> ?n:int -> commit list -> + val lca: [`Read | `Write] t -> info:Info.f -> ?max_depth:int -> ?n:int -> commit list -> (commit option, Merge.conflict) result Lwt.t - val three_way_merge: t -> info:Info.f -> ?max_depth:int -> ?n:int -> + val three_way_merge: [`Read | `Write] t -> info:Info.f -> ?max_depth:int -> ?n:int -> commit -> commit -> (commit, Merge.conflict) result Lwt.t - val closure: t -> min:commit list -> max:commit list -> commit list Lwt.t + val closure: [> `Read] t -> min:commit list -> max:commit list -> commit list Lwt.t val commit_t: commit Type.t end @@ -203,7 +201,11 @@ end (** Read-write stores. *) module type ATOMIC_WRITE_STORE = sig - include READ_ONLY_STORE + type t + type key + type value + val mem: t -> key -> bool Lwt.t + val find: t -> key -> value option Lwt.t val set: t -> key -> value -> unit Lwt.t val test_and_set: t -> key -> test:value option -> set:value option -> bool Lwt.t @@ -261,10 +263,15 @@ module type PRIVATE = sig module Repo: sig type t val v: Conf.t -> t Lwt.t - val contents_t: t -> Contents.t - val node_t: t -> Node.t - val commit_t: t -> Commit.t + val contents_t: t -> [`Read] Contents.t + val node_t: t -> [`Read] Node.t + val commit_t: t -> [`Read] Commit.t val branch_t: t -> Branch.t + val batch: t -> + ([`Read | `Write] Contents.t -> + [`Read | `Write] Node.t -> + [`Read | `Write] Commit.t + -> 'a Lwt.t) -> 'a Lwt.t end module Sync: sig include SYNC diff --git a/src/irmin/store.ml b/src/irmin/store.ml index 3965fe2259..667cb2f199 100644 --- a/src/irmin/store.ml +++ b/src/irmin/store.ml @@ -77,14 +77,15 @@ module Make (P: S.PRIVATE) = struct let compare_hash = Type.compare Hash.t let v r ~info ~parents tree = + P.Repo.batch r @@ fun contents_t node_t commit_t -> let parents = List.rev_map (fun c -> c.h) parents in let parents = List.sort compare_hash parents in (match tree with - | `Node n -> Tree.export r n + | `Node n -> Tree.export r contents_t node_t n | `Contents _ -> Lwt.fail_invalid_arg "cannot add contents at the root") >>= fun node -> let v = P.Commit.Val.v ~info ~node ~parents in - P.Commit.add (P.Repo.commit_t r) v >|= fun h -> + P.Commit.add commit_t v >|= fun h -> { r; h; v } let node t = P.Commit.Val.node t.v @@ -214,23 +215,13 @@ module Make (P: S.PRIVATE) = struct let import_error fmt = Fmt.kstrf (fun x -> Lwt.fail (Import_error x)) fmt let import t s = - let aux (type k) (type v) - name - (type s) - (module S: S.CONTENT_ADDRESSABLE_STORE - with type t = s and type key = k and type value = v) - (dk: k Type.t) - fn - (s:t -> s) - = - fn (fun (k, v) -> - S.add (s t) v >>= fun k' -> - if not (Type.equal dk k k') then ( - import_error "%s import error: expected %a, got %a" - name Type.(pp dk) k Type.(pp dk) k' - ) - else Lwt.return_unit - ) + let aux name add dk (k, v) = + add v >>= fun k' -> + if not (Type.equal dk k k') then ( + import_error "%s import error: expected %a, got %a" + name Type.(pp dk) k Type.(pp dk) k' + ) + else Lwt.return_unit in let contents = ref [] in let nodes = ref [] in @@ -240,15 +231,19 @@ module Make (P: S.PRIVATE) = struct | `Node n -> nodes := n :: !nodes; Lwt.return_unit | `Commit c -> commits := c :: !commits; Lwt.return_unit ) >>= fun () -> + P.Repo.batch t @@ fun contents_t node_t commit_t -> Lwt.catch (fun () -> - aux "Contents" (module P.Contents) P.Contents.Key.t - (fun f -> Lwt_list.iter_s f !contents) contents_t + Lwt_list.iter_p + (aux "Contents" (P.Contents.add contents_t) P.Contents.Key.t) + !contents >>= fun () -> - aux "Node" (module P.Node) P.Node.Key.t - (fun f -> Lwt_list.iter_s f !nodes) node_t + Lwt_list.iter_p + (aux "Node" (P.Node.add node_t) P.Node.Key.t) + !nodes >>= fun () -> - aux "Commit" (module P.Commit) P.Commit.Key.t - (fun f -> Lwt_list.iter_s f !commits) commit_t + Lwt_list.iter_p + (aux "Commit" (P.Commit.add commit_t) P.Commit.Key.t) + !commits >|= fun () -> Ok ()) (function @@ -463,8 +458,9 @@ module Make (P: S.PRIVATE) = struct (* Merge two commits: - Search for common ancestors - Perform recursive 3-way merges *) - let three_way_merge t ?max_depth ?n c1 c2 = - H.three_way_merge (history_t t) ?max_depth ?n c1.Commit.h c2.Commit.h + let three_way_merge t ?max_depth ?n ~info c1 c2 = + P.Repo.batch (repo t) @@ fun _ _ commit_t -> + H.three_way_merge commit_t ?max_depth ?n ~info c1.Commit.h c2.Commit.h (* FIXME: we might want to keep the new commit in case of conflict, and use it as a base for the next merge. *) diff --git a/src/irmin/tree.ml b/src/irmin/tree.ml index ecd5bcd1c0..0a61941d14 100644 --- a/src/irmin/tree.ml +++ b/src/irmin/tree.ml @@ -787,8 +787,8 @@ module Make (P: S.PRIVATE) = struct let import repo k = Node.of_key repo k - let export repo n = - let node n = P.Node.add (P.Repo.node_t repo) (Node.export_map n) in + let export repo contents_t node_t n = + let node n = P.Node.add node_t (Node.export_map n) in let todo = Stack.create () in let rec add_to_todo n = match n.Node.v with @@ -822,7 +822,7 @@ module Make (P: S.PRIVATE) = struct | Contents.Key _ -> () | Contents.Contents x -> Stack.push (fun () -> - P.Contents.add (P.Repo.contents_t repo) x >|= fun k -> + P.Contents.add contents_t x >|= fun k -> c.Contents.v <- Contents.Both (repo, k, x); ) todo ) !contents; diff --git a/src/irmin/tree.mli b/src/irmin/tree.mli index 6828c79369..0f7b8566ff 100644 --- a/src/irmin/tree.mli +++ b/src/irmin/tree.mli @@ -22,7 +22,8 @@ module Make (P: S.PRIVATE): sig and type contents = P.Contents.value val import: P.Repo.t -> P.Node.key -> node - val export: P.Repo.t -> node -> P.Node.key Lwt.t + val export: P.Repo.t -> [> `Write] P.Contents.t -> [> `Write] P.Node.t -> + node -> P.Node.key Lwt.t val dump: tree Fmt.t val equal: tree -> tree -> bool val node_t: node Type.t diff --git a/test/irmin-chunk/test.ml b/test/irmin-chunk/test.ml index fa6b4fa8d5..8d6c14f96d 100644 --- a/test/irmin-chunk/test.ml +++ b/test/irmin-chunk/test.ml @@ -29,11 +29,11 @@ let run f () = flush stdout let test_add_read ?(stable=false) (module AO: Test_chunk.S) () = - AO.create () >>= fun t -> + AO.v () >>= fun t -> let test size = let name = Printf.sprintf "size %d" size in let v = String.make size 'x' in - AO.add t v >>= fun k -> + AO.batch t (fun t -> AO.add t v) >>= fun k -> if stable then ( let str = Irmin.Type.encode_bin Test_chunk.Value.t v in Alcotest.(check key_t) (name ^ " is stable") k (Test_chunk.Key.digest str) diff --git a/test/irmin-chunk/test_chunk.ml b/test/irmin-chunk/test_chunk.ml index 4aba5f192c..d5b1e58a41 100644 --- a/test/irmin-chunk/test_chunk.ml +++ b/test/irmin-chunk/test_chunk.ml @@ -34,7 +34,8 @@ end module type S = sig include Irmin.CONTENT_ADDRESSABLE_STORE with type key = Key.t and type value = Value.t - val create: unit -> t Lwt.t + val v: unit -> [`Read] t Lwt.t + val batch: [`Read] t -> ([`Read|`Write] t -> 'a Lwt.t) -> 'a Lwt.t end module Append_only = Irmin_mem.Append_only @@ -42,13 +43,13 @@ module Content_addressable = Irmin.Content_addressable(Append_only) module Mem = struct include Content_addressable(Key)(Value) - let create () = v @@ Irmin_mem.config () + let v () = v @@ Irmin_mem.config () end module MemChunk = struct include Content_addressable(Key)(Value) let small_config = Irmin_chunk.config ~min_size:44 ~size:44 () - let create () = v small_config + let v () = v small_config end let init () =