1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264(*
* Copyright (c) 2013-2022 Thomas Gazagnaire <thomas@gazagnaire.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*)open!ImportopenStore_propertiesmoduletypeS_generic_key=sig(** {1 Irmin stores}
Irmin stores are tree-like read-write stores with extended capabilities.
They allow an application (or a collection of applications) to work with
multiple local states, which can be forked and merged programmatically,
without having to rely on a global state. In a way very similar to version
control systems, Irmin local states are called {i branches}.
There are two kinds of store in Irmin: the ones based on {{!of_branch}
persistent} named branches and the ones based {{!of_commit} temporary}
detached heads. These exist relative to a local, larger (and shared)
store, and have some (shared) contents. This is exactly the same as usual
version control systems, that the informed user can see as an implicit
purely functional data-structure. *)moduleSchema:Schema.Styperepo(** The type for Irmin repositories. *)typet(** The type for Irmin stores. *)typestep=Schema.Path.step[@@derivingirmin](** The type for {!type-key} steps. *)typepath=Schema.Path.t[@@derivingirmin](** The type for store keys. A key is a sequence of {!step}s. *)typemetadata=Schema.Metadata.t[@@derivingirmin](** The type for store metadata. *)typecontents=Schema.Contents.t[@@derivingirmin](** The type for store contents. *)typenode[@@derivingirmin](** The type for store nodes. *)typetree[@@derivingirmin](** The type for store trees. *)typehash=Schema.Hash.t[@@derivingirmin](** The type for object hashes. *)typecommit(** Type for [`Commit] identifiers. Similar to Git's commit SHA1s. *)valcommit_t:repo->commitType.t(** [commit_t r] is the value type for {!commit}. *)typebranch=Schema.Branch.t[@@derivingirmin](** Type for persistent branch names. Branches usually share a common global
namespace and it's the user's responsibility to avoid name clashes. *)typeslice[@@derivingirmin](** Type for store slices. *)typeinfo=Schema.Info.t[@@derivingirmin](** The type for commit info. *)typelca_error=[`Max_depth_reached|`Too_many_lcas][@@derivingirmin](** The type for errors associated with functions computing least common
ancestors *)typeff_error=[`No_change|`Rejected|lca_error][@@derivingirmin](** The type for errors for {!Head.fast_forward}. *)moduleInfo:sigincludeInfo.Swithtypet=info(** @inline *)endtypecontents_key[@@derivingirmin]typenode_key[@@derivingirmin]typecommit_key[@@derivingirmin](** Repositories. *)moduleRepo:sig(** {1 Repositories}
A repository contains a set of branches. *)typet=repo(** The type of repository handles. *)valv:Conf.t->tLwt.t(** [v config] connects to a repository in a backend-specific manner. *)valconfig:t->Conf.t(** [config repo] is the configuration used to create [repo] *)includeCloseablewithtype_t:=t(** @inline *)valheads:t->commitlistLwt.t(** [heads] is {!Head.list}. *)valbranches:t->branchlistLwt.t(** [branches] is {!Branch.list}. *)valexport:?full:bool->?depth:int->?min:commitlist->?max:[`Head|`Maxofcommitlist]->t->sliceLwt.t(** [export t ~full ~depth ~min ~max] exports the store slice between [min]
and [max], using at most [depth] history depth (starting from the max).
If [max] is `Head (also the default value), use the current [heads]. If
[min] is not specified, use an unbound past (but can still be limited by
[depth]).
[depth] is used to limit the depth of the commit history. [None] here
means no limitation.
If [full] is set (default is true), the full graph, including the
commits, nodes and contents, is exported, otherwise it is the commit
history graph only. *)valimport:t->slice->(unit,[`Msgofstring])resultLwt.t(** [import t s] imports the contents of the slice [s] in [t]. Does not
modify branches. *)typeelt=[`Commitofcommit_key|`Nodeofnode_key|`Contentsofcontents_key|`Branchofbranch][@@derivingirmin](** The type for elements iterated over by {!iter}. *)valdefault_pred_commit:t->commit_key->eltlistLwt.tvaldefault_pred_node:t->node_key->eltlistLwt.tvaldefault_pred_contents:t->contents_key->eltlistLwt.tvaliter:?cache_size:int->min:eltlist->max:eltlist->?edge:(elt->elt->unitLwt.t)->?branch:(branch->unitLwt.t)->?commit:(commit_key->unitLwt.t)->?node:(node_key->unitLwt.t)->?contents:(contents_key->unitLwt.t)->?skip_branch:(branch->boolLwt.t)->?skip_commit:(commit_key->boolLwt.t)->?skip_node:(node_key->boolLwt.t)->?skip_contents:(contents_key->boolLwt.t)->?pred_branch:(t->branch->eltlistLwt.t)->?pred_commit:(t->commit_key->eltlistLwt.t)->?pred_node:(t->node_key->eltlistLwt.t)->?pred_contents:(t->contents_key->eltlistLwt.t)->?rev:bool->t->unitLwt.t(** [iter t] iterates in topological order over the closure graph of [t]. If
[rev] is set (by default it is) the traversal is done in reverse order.
[skip_branch], [skip_commit], [skip_node] and [skip_contents] allow the
traversal to be stopped when the corresponding objects are traversed. By
default no objects are skipped.
The [branch], [commit], [node] and [contents] functions are called
whenever the corresponding objects are traversed. By default these
functions do nothing. These functions are not called on skipped objects.
[pred_branch], [pred_commit], [pred_node] and [pred_contents] implicitly
define the graph underlying the traversal. By default they exactly match
the underlying Merkle graph of the repository [t]. These functions can
be used to traverse a slightly modified version of that graph, for
instance by modifying [pred_contents] to implicitly link structured
contents with other objects in the graph.
The traversed objects are all included between [min] (included) and
[max] (included), following the Merkle graph order. Moreover, the [min]
boundary is extended as follows:
- contents and node objects in [min] stop the traversal; their
predecessors are not traversed.
- commit objects in [min] stop the traversal for their commit
predecessors, but their sub-node are still traversed. This allows
users to define an inclusive range of commit to iterate over.
- branch objects in [min] implicitly add to [min] the commit they are
pointing to; this allow users to define the iteration between two
branches.
[cache_size] is the size of the LRU used to store traversed objects. If
an entry is evicted from the LRU, it can be traversed multiple times by
{!Repo.iter}. When [cache_size] is [None] (the default), no entries is
ever evicted from the cache; hence every object is only traversed once,
at the cost of having to store all the traversed objects in memory. *)valbreadth_first_traversal:?cache_size:int->max:eltlist->?branch:(branch->unitLwt.t)->?commit:(commit_key->unitLwt.t)->?node:(node_key->unitLwt.t)->?contents:(contents_key->unitLwt.t)->?pred_branch:(t->branch->eltlistLwt.t)->?pred_commit:(t->commit_key->eltlistLwt.t)->?pred_node:(t->node_key->eltlistLwt.t)->?pred_contents:(t->contents_key->eltlistLwt.t)->t->unitLwt.tendvalempty:repo->tLwt.t(** [empty repo] is a temporary, empty store. Becomes a normal temporary store
after the first update. *)valmain:repo->tLwt.t(** [main r] is a persistent store based on [r]'s main branch. This operation
is cheap, can be repeated multiple times. *)valof_branch:repo->branch->tLwt.t(** [of_branch r name] is a persistent store based on the branch [name].
Similar to {!main}, but use [name] instead of {!Irmin.Branch.S.main}. *)valof_commit:commit->tLwt.t(** [of_commit c] is a temporary store, based on the commit [c].
Temporary stores do not have stable names: instead they can be addressed
using the hash of the current commit. Temporary stores are similar to
Git's detached heads. In a temporary store, all the operations are
performed relative to the current head and update operations can modify
the current head: the current stores's head will automatically become the
new head obtained after performing the update. *)valrepo:t->repo(** [repo t] is the repository containing [t]. *)valtree:t->treeLwt.t(** [tree t] is [t]'s current tree. Contents is not allowed at the root of the
tree. *)moduleStatus:sigtypet=[`Empty|`Branchofbranch|`Commitofcommit](** The type for store status. *)valt:repo->tType.t(** [t] is the value type for {!type-t}. *)valpp:tFmt.t(** [pp] is the pretty-printer for store status. *)endvalstatus:t->Status.t(** [status t] is [t]'s status. It can either be a branch, a commit or empty. *)(** Managing the store's heads. *)moduleHead:sigvallist:repo->commitlistLwt.t(** [list t] is the list of all the heads in local store. Similar to
[git rev-list --all]. *)valfind:t->commitoptionLwt.t(** [find t] is the current head of the store [t]. This works for both
persistent and temporary branches. In the case of a persistent branch,
this involves getting the the head associated with the branch, so this
may block. In the case of a temporary store, it simply returns the
current head. Returns [None] if the store has no contents. Similar to
[git rev-parse HEAD]. *)valget:t->commitLwt.t(** Same as {!find} but raise [Invalid_argument] if the store does not have
any contents. *)valset:t->commit->unitLwt.t(** [set t h] updates [t]'s contents with the contents of the commit [h].
Can cause data loss as it discards the current contents. Similar to
[git reset --hard <hash>]. *)valfast_forward:t->?max_depth:int->?n:int->commit->(unit,ff_error)resultLwt.t(** [fast_forward t h] is similar to {!set} but the [t]'s head is updated to
[h] only if [h] is stricly in the future of [t]'s current head.
[max_depth] or [n] are used to limit the search space of the lowest
common ancestors (see {!lcas}).
The result is:
- [Ok ()] if the operation is succesfull;
- [Error `No_change] if [h] is already [t]'s head;
- [Error `Rejected] if [h] is not in the strict future of [t]'s head.
- [Error e] if the history exploration has been cut before getting
useful results. In that case. the operation can be retried using
different parameters of [n] and [max_depth] to get better results. *)valtest_and_set:t->test:commitoption->set:commitoption->boolLwt.t(** Same as {!set} but check that the value is [test] before updating to
[set]. Use {!set} or {!val-merge} instead if possible. *)valmerge:into:t->info:Info.f->?max_depth:int->?n:int->commit->(unit,Merge.conflict)resultLwt.t(** [merge ~into:t ?max_head ?n commit] merges the contents of the commit
associated to [commit] into [t]. [max_depth] is the maximal depth used
for getting the lowest common ancestor. [n] is the maximum number of
lowest common ancestors. If present, [max_depth] or [n] are used to
limit the search space of the lowest common ancestors (see {!lcas}). *)endmoduleHash:Hash.Swithtypet=hash(** Object hashes. *)(** [Commit] defines immutable objects to describe store updates. *)moduleCommit:sigtypet=commit(** The type for store commits. *)valt:repo->tType.t(** [t] is the value type for {!type-t}. *)valpp_hash:tFmt.t(** [pp] is the pretty-printer for commit. Display only the hash. *)valv:?clear:bool->repo->info:info->parents:commit_keylist->tree->commitLwt.t(** [v r i ~parents:p t] is the commit [c] such that:
- [info c = i]
- [parents c = p]
- [tree c = t]
When [clear] is set (the default), the tree cache is emptied upon the
function's completion, mirroring the effect of invoking {!Tree.clear}. *)valtree:commit->tree(** [tree c] is [c]'s root tree. *)valparents:commit->commit_keylist(** [parents c] are [c]'s parents. *)valinfo:commit->info(** [info c] is [c]'s info. *)valhash:commit->hash(** [hash c] is [c]'s hash. *)(** {1 Import/Export} *)valkey:commit->commit_key(** [key c] is [c]'s key. *)valof_key:repo->commit_key->commitoptionLwt.t(** [of_key r k] is the the commit object in [r] with key [k], or [None] if
no such commit object exists. *)valof_hash:repo->hash->commitoptionLwt.t(** [of_hash r h] is the commit object in [r] with hash [h], or [None] if no
such commit object is indexed in [r].
{b Note:} in stores for which {!commit_key} = {!type-hash}, this
function has identical behaviour to {!of_key}. *)end(** [Contents] provides base functions for the store's contents. *)moduleContents:sigincludeContents.Swithtypet=contents(** {1 Import/Export} *)valhash:contents->hash(** [hash c] it [c]'s hash. *)valof_key:repo->contents_key->contentsoptionLwt.t(** [of_key r k] is the contents object in [r] with key [k], or [None] if no
such contents object exists. *)valof_hash:repo->hash->contentsoptionLwt.t(** [of_hash r h] is the contents object in [r] with hash [h], or [None] if
no such contents object is indexed in [r].
{b Note:} in stores for which {!contents_key} = {!type-hash}, this
function has identical behaviour to {!of_key}. *)end(** Managing store's trees. *)moduleTree:sigincludeTree.Swithtypet:=treeandtypestep:=stepandtypepath:=pathandtypemetadata:=metadataandtypecontents:=contentsandtypecontents_key:=contents_keyandtypenode:=nodeandtypehash:=hash(** {1 Import/Export} *)typekinded_key=[`Contentsofcontents_key*metadata|`Nodeofnode_key][@@derivingirmin](** Keys in the Irmin store are tagged with the type of the value they
reference (either {!contents} or {!node}). In the [contents] case, the
key is paired with corresponding {!metadata}. *)valkey:tree->kinded_keyoption(** [key t] is the key of tree [t] in the underlying repository, if it
exists. Tree objects that exist entirely in memory (such as those built
with {!of_concrete}) have no backend key until they are exported to a
repository, and so will return [None]. *)valfind_key:Repo.t->tree->kinded_keyoptionLwt.t(** [find_key r t] is the key of a tree object with the same hash as [t] in
[r], if such a key exists and is indexed. *)valof_key:Repo.t->kinded_key->treeoptionLwt.t(** [of_key r h] is the tree object in [r] having [h] as key, or [None] if
no such tree object exists. *)valshallow:Repo.t->kinded_key->tree(** [shallow r h] is the shallow tree object with the key [h]. No check is
performed to verify if [h] actually exists in [r]. *)valhash:?cache:bool->tree->hash(** [hash t] is the hash of tree [t]. *)typekinded_hash=[`Contentsofhash*metadata|`Nodeofhash](** Like {!kinded_key}, but with hashes as value references rather than
keys. *)valkinded_hash:?cache:bool->tree->kinded_hash(** [kinded_hash t] is [c]'s kinded hash. *)valof_hash:Repo.t->kinded_hash->treeoptionLwt.t(** [of_hash r h] is the tree object in [r] with hash [h], or [None] if no
such tree object is indexed in [r].
{b Note:} in stores for which {!node_key} = {!contents_key} =
{!type-hash}, this function has identical behaviour to {!of_key}. *)(** {1 Proofs} *)type('proof,'result)producer:=repo->kinded_key->(tree->(tree*'result)Lwt.t)->('proof*'result)Lwt.t(** [produce r h f] runs [f] on top of a real store [r], producing a proof
and a result using the initial root hash [h].
The trees produced during [f]'s computation will carry the full history
of reads. This history will be reset when [f] is complete so subtrees
escaping the scope of [f] will not cause memory leaks.
Calling [produce_proof] recursively has an undefined behaviour. *)typeverifier_error=[`Proof_mismatchofstring|`Stream_too_longofstring|`Stream_too_shortofstring][@@derivingirmin](** The type for errors associated with functions that verify proofs. *)type('proof,'result)verifier:='proof->(tree->(tree*'result)Lwt.t)->(tree*'result,verifier_error)resultLwt.t(** [verify p f] runs [f] in checking mode. [f] is a function that takes a
tree as input and returns a new version of the tree and a result. [p] is
a proof, that is a minimal representation of the tree that contains what
[f] should be expecting.
Therefore, contrary to trees found in a storage, the contents of the
trees passed to [f] may not be available. For this reason, looking up a
value at some [path] can now produce three distinct outcomes:
- A value [v] is present in the proof [p] and returned :
[find tree path] is a promise returning [Some v];
- [path] is known to have no value in [tree] : [find tree path] is a
promise returning [None]; and
- [path] is known to have a value in [tree] but [p] does not provide it
because [f] should not need it: [verify] returns an error classifying
[path] as an invalid path (see below).
The same semantics apply to all operations on the tree [t] passed to [f]
and on all operations on the trees built from [f].
The generated tree is the tree after [f] has completed. That tree is
disconnected from the backend. It is possible to run operations on it as
long as they don't require loading shallowed subtrees, otherwise it
would raise [Dangling_hash].
The result is [Error _] if the proof is rejected:
- For tree proofs: when [p.before] is different from the hash of
[p.state];
- For tree and stream proofs: when [p.after] is different from the hash
of [f p.state];
- For tree and stream proofs: when [f p.state] tries to access paths
invalid paths in [p.state];
- For stream proofs: when the proof is not empty once [f] is done. *)typetree_proof:=Proof.treeProof.t(** The type for tree proofs.
Guarantee that the given computation performs exactly the same state
operations as the generating computation, *in some order*. *)valproduce_proof:(tree_proof,'a)producer(** [produce_proof] is the producer of tree proofs. *)valverify_proof:(tree_proof,'a)verifier(** [verify_proof] is the verifier of tree proofs. *)valhash_of_proof_state:Proof.tree->kinded_hashtypestream_proof:=Proof.streamProof.t(** The type for stream proofs.
Guarantee that the given computation performs exactly the same state
operations as the generating computation, in the exact same order.
Calling [fold] with [order = `Undefined] during the
production/verification of streamed proofs is undefined. *)valproduce_stream:(stream_proof,'a)producer(** [produce_stream] is the producer of stream proofs. *)valverify_stream:(stream_proof,'a)verifier(** [verify_stream] is the verifier of stream proofs. *)end(** {1 Reads} *)valkind:t->path->[`Contents|`Node]optionLwt.t(** [kind] is {!Tree.kind} applied to [t]'s root tree. *)vallist:t->path->(step*tree)listLwt.t(** [list t] is {!Tree.list} applied to [t]'s root tree. *)valmem:t->path->boolLwt.t(** [mem t] is {!Tree.mem} applied to [t]'s root tree. *)valmem_tree:t->path->boolLwt.t(** [mem_tree t] is {!Tree.mem_tree} applied to [t]'s root tree. *)valfind_all:t->path->(contents*metadata)optionLwt.t(** [find_all t] is {!Tree.find_all} applied to [t]'s root tree. *)valfind:t->path->contentsoptionLwt.t(** [find t] is {!Tree.find} applied to [t]'s root tree. *)valget_all:t->path->(contents*metadata)Lwt.t(** [get_all t] is {!Tree.get_all} applied on [t]'s root tree. *)valget:t->path->contentsLwt.t(** [get t] is {!Tree.get} applied to [t]'s root tree. *)valfind_tree:t->path->treeoptionLwt.t(** [find_tree t] is {!Tree.find_tree} applied to [t]'s root tree. *)valget_tree:t->path->treeLwt.t(** [get_tree t k] is {!Tree.get_tree} applied to [t]'s root tree. *)typekinded_key:=[`Contentsofcontents_key|`Nodeofnode_key]valkey:t->path->kinded_keyoptionLwt.t(** [id t k] *)valhash:t->path->hashoptionLwt.t(** [hash t k] *)(** {1 Updates} *)typewrite_error=[Merge.conflict|`Too_many_retriesofint|`Test_wasoftreeoption][@@derivingirmin](** The type for write errors.
- Merge conflict.
- Concurrent transactions are competing to get the current operation
committed and too many attemps have been tried (livelock).
- A "test and set" operation has failed and the current value is [v]
instead of the one we were waiting for. *)valset:?clear:bool->?retries:int->?allow_empty:bool->?parents:commitlist->info:Info.f->t->path->contents->(unit,write_error)resultLwt.t(** [set t k ~info v] sets [k] to the value [v] in [t]. Discard any previous
results but ensure that no operation is lost in the history.
This function always uses {!Metadata.default} as metadata. Use {!set_tree}
with `[Contents (c, m)] for different ones.
When [clear] is set (the default), the tree cache is emptied upon the
function's completion, mirroring the effect of invoking {!Tree.clear}.
The result is [Error `Too_many_retries] if the concurrent operations do
not allow the operation to commit to the underlying storage layer
(livelock). *)valset_exn:?clear:bool->?retries:int->?allow_empty:bool->?parents:commitlist->info:Info.f->t->path->contents->unitLwt.t(** [set_exn] is like {!set} but raise [Failure _] instead of using a result
type. *)valset_tree:?clear:bool->?retries:int->?allow_empty:bool->?parents:commitlist->info:Info.f->t->path->tree->(unit,write_error)resultLwt.t(** [set_tree] is like {!set} but for trees. *)valset_tree_exn:?clear:bool->?retries:int->?allow_empty:bool->?parents:commitlist->info:Info.f->t->path->tree->unitLwt.t(** [set_tree] is like {!set_exn} but for trees. *)valremove:?clear:bool->?retries:int->?allow_empty:bool->?parents:commitlist->info:Info.f->t->path->(unit,write_error)resultLwt.t(** [remove t ~info k] remove any bindings to [k] in [t].
The result is [Error `Too_many_retries] if the concurrent operations do
not allow the operation to commit to the underlying storage layer
(livelock). *)valremove_exn:?clear:bool->?retries:int->?allow_empty:bool->?parents:commitlist->info:Info.f->t->path->unitLwt.t(** [remove_exn] is like {!remove} but raise [Failure _] instead of a using
result type. *)valtest_and_set:?clear:bool->?retries:int->?allow_empty:bool->?parents:commitlist->info:Info.f->t->path->test:contentsoption->set:contentsoption->(unit,write_error)resultLwt.t(** [test_and_set ~test ~set] is like {!set} but it atomically checks that the
tree is [test] before modifying it to [set].
This function always uses {!Metadata.default} as metadata. Use
{!test_and_set_tree} with `[Contents (c, m)] for different ones.
The result is [Error (`Test t)] if the current tree is [t] instead of
[test].
The result is [Error `Too_many_retries] if the concurrent operations do
not allow the operation to commit to the underlying storage layer
(livelock). *)valtest_and_set_exn:?clear:bool->?retries:int->?allow_empty:bool->?parents:commitlist->info:Info.f->t->path->test:contentsoption->set:contentsoption->unitLwt.t(** [test_and_set_exn] is like {!test_and_set} but raise [Failure _] instead
of using a result type. *)valtest_and_set_tree:?clear:bool->?retries:int->?allow_empty:bool->?parents:commitlist->info:Info.f->t->path->test:treeoption->set:treeoption->(unit,write_error)resultLwt.t(** [test_and_set_tree] is like {!test_and_set} but for trees. *)valtest_and_set_tree_exn:?clear:bool->?retries:int->?allow_empty:bool->?parents:commitlist->info:Info.f->t->path->test:treeoption->set:treeoption->unitLwt.t(** [test_and_set_tree_exn] is like {!test_and_set_exn} but for trees. *)valtest_set_and_get:?clear:bool->?retries:int->?allow_empty:bool->?parents:commitlist->info:(unit->info)->t->path->test:contentsoption->set:contentsoption->(commitoption,write_error)resultLwt.t(** [test_set_and_get] is like {!test_and_set} except it also returns the
commit associated with updating the store with the new value if the
[test_and_set] is successful. No commit is returned if there was no update
to the store. *)valtest_set_and_get_exn:?clear:bool->?retries:int->?allow_empty:bool->?parents:commitlist->info:(unit->info)->t->path->test:contentsoption->set:contentsoption->commitoptionLwt.t(** [test_set_and_get_exn] is like {!test_set_and_get} but raises [Failure _]
instead. *)valtest_set_and_get_tree:?clear:bool->?retries:int->?allow_empty:bool->?parents:commitlist->info:(unit->info)->t->path->test:treeoption->set:treeoption->(commitoption,write_error)resultLwt.t(** [test_set_and_get_tree] is like {!test_set_and_get} but for a
{!type-tree} *)valtest_set_and_get_tree_exn:?clear:bool->?retries:int->?allow_empty:bool->?parents:commitlist->info:(unit->info)->t->path->test:treeoption->set:treeoption->commitoptionLwt.t(** [test_set_and_get_tree_exn] is like {!test_set_and_get_tree} but raises
[Failure _] instead. *)valmerge:?clear:bool->?retries:int->?allow_empty:bool->?parents:commitlist->info:Info.f->old:contentsoption->t->path->contentsoption->(unit,write_error)resultLwt.t(** [merge ~old] is like {!set} but merge the current tree and the new tree
using [old] as ancestor in case of conflicts.
This function always uses {!Metadata.default} as metadata. Use
{!merge_tree} with `[Contents (c, m)] for different ones.
The result is [Error (`Conflict c)] if the merge failed with the conflict
[c].
The result is [Error `Too_many_retries] if the concurrent operations do
not allow the operation to commit to the underlying storage layer
(livelock). *)valmerge_exn:?clear:bool->?retries:int->?allow_empty:bool->?parents:commitlist->info:Info.f->old:contentsoption->t->path->contentsoption->unitLwt.t(** [merge_exn] is like {!val-merge} but raise [Failure _] instead of using a
result type. *)valmerge_tree:?clear:bool->?retries:int->?allow_empty:bool->?parents:commitlist->info:Info.f->old:treeoption->t->path->treeoption->(unit,write_error)resultLwt.t(** [merge_tree] is like {!merge_tree} but for trees. *)valmerge_tree_exn:?clear:bool->?retries:int->?allow_empty:bool->?parents:commitlist->info:Info.f->old:treeoption->t->path->treeoption->unitLwt.t(** [merge_tree] is like {!merge_tree} but for trees. *)valwith_tree:?clear:bool->?retries:int->?allow_empty:bool->?parents:commitlist->?strategy:[`Set|`Test_and_set|`Merge]->info:Info.f->t->path->(treeoption->treeoptionLwt.t)->(unit,write_error)resultLwt.t(** [with_tree t k ~info f] replaces {i atomically} the subtree [v] under [k]
in the store [t] by the contents of the tree [f v], using the commit info
[info ()].
If [v = f v] and [allow_empty] is unset (default) then, the operation is a
no-op.
If [v != f v] and no other changes happen concurrently, [f v] becomes the
new subtree under [k]. If other changes happen concurrently to that
operations, the semantics depend on the value of [strategy]:
- if [strategy = `Set], use {!set} and discard any concurrent updates to
[k].
- if [strategy = `Test_and_set] (default), use {!test_and_set} and ensure
that no concurrent operations are updating [k].
- if [strategy = `Merge], use {!val-merge} and ensure that concurrent
updates and merged with the values present at the beginning of the
transaction.
{b Note:} Irmin transactions provides
{{:https://en.wikipedia.org/wiki/Snapshot_isolation} snapshot isolation}
guarantees: reads and writes are isolated in every transaction, but only
write conflicts are visible on commit. *)valwith_tree_exn:?clear:bool->?retries:int->?allow_empty:bool->?parents:commitlist->?strategy:[`Set|`Test_and_set|`Merge]->info:Info.f->t->path->(treeoption->treeoptionLwt.t)->unitLwt.t(** [with_tree_exn] is like {!with_tree} but raise [Failure _] instead of
using a return type. *)(** {1 Clones} *)valclone:src:t->dst:branch->tLwt.t(** [clone ~src ~dst] makes [dst] points to [Head.get src]. [dst] is created
if needed. Remove the current contents en [dst] if [src] is {!val-empty}. *)(** {1 Watches} *)typewatch(** The type for store watches. *)valwatch:t->?init:commit->(commitDiff.t->unitLwt.t)->watchLwt.t(** [watch t f] calls [f] every time the contents of [t]'s head is updated.
{b Note:} even if [f] might skip some head updates, it will never be
called concurrently: all consecutive calls to [f] are done in sequence, so
we ensure that the previous one ended before calling the next one. *)valwatch_key:t->path->?init:commit->((commit*tree)Diff.t->unitLwt.t)->watchLwt.t(** [watch_key t key f] calls [f] every time the [key]'s value is added,
removed or updated. If the current branch is deleted, no signal is sent to
the watcher. *)valunwatch:watch->unitLwt.t(** [unwatch w] disable [w]. Return once the [w] is fully disabled. *)(** {1 Merges and Common Ancestors} *)type'amerge=info:Info.f->?max_depth:int->?n:int->'a->(unit,Merge.conflict)resultLwt.t(** The type for merge functions. *)valmerge_into:into:t->tmerge(** [merge_into ~into:x ~info:i t] merges [t]'s current branch into [x]'s
current branch using the info [i]. After that operation, the two stores
are still independent. Similar to [git merge <branch>]. *)valmerge_with_branch:t->branchmerge(** Same as {!val-merge} but with a branch ID. *)valmerge_with_commit:t->commitmerge(** Same as {!val-merge} but with a commit_id. *)vallcas:?max_depth:int->?n:int->t->t->(commitlist,lca_error)resultLwt.t(** [lca ?max_depth ?n msg t1 t2] returns the collection of least common
ancestors between the heads of [t1] and [t2] branches.
- [max_depth] is the maximum depth of the exploration (default is
[max_int]). Return [Error `Max_depth_reached] if this depth is exceeded.
- [n] is the maximum expected number of lcas. Stop the exploration as soon
as [n] lcas are found. Return [Error `Too_many_lcas] if more [lcas] are
found. *)vallcas_with_branch:t->?max_depth:int->?n:int->branch->(commitlist,lca_error)resultLwt.t(** Same as {!lcas} but takes a branch ID as argument. *)vallcas_with_commit:t->?max_depth:int->?n:int->commit->(commitlist,lca_error)resultLwt.t(** Same as {!lcas} but takes a commmit as argument. *)(** {1 History} *)moduleHistory:Graph.Sig.PwithtypeV.t=commit(** An history is a DAG of heads. *)valhistory:?depth:int->?min:commitlist->?max:commitlist->t->History.tLwt.t(** [history ?depth ?min ?max t] is a view of the history of the store [t], of
depth at most [depth], starting from the [t]'s head (or from [max] if the
head is not set) and stopping at [min] if specified. *)vallast_modified:?depth:int->?n:int->t->path->commitlistLwt.t(** [last_modified ?number c k] is the list of the last [number] commits that
modified [path], in ascending order of date. [depth] is the maximum depth
to be explored in the commit graph, if any. Default value for [number] is
1. *)(** Manipulate branches. *)moduleBranch:sig(** {1 Branch Store}
Manipulate relations between {{!branch} branches} and {{!commit}
commits}. *)valmem:repo->branch->boolLwt.t(** [mem r b] is true iff [b] is present in [r]. *)valfind:repo->branch->commitoptionLwt.t(** [find r b] is [Some c] iff [c] is bound to [b] in [t]. It is [None] if
[b] is not present in [t]. *)valget:repo->branch->commitLwt.t(** [get t b] is similar to {!find} but raise [Invalid_argument] if [b] is
not present in [t]. *)valset:repo->branch->commit->unitLwt.t(** [set t b c] bounds [c] to [b] in [t]. *)valremove:repo->branch->unitLwt.t(** [remove t b] removes [b] from [t]. *)vallist:repo->branchlistLwt.t(** [list t] is the list of branches present in [t]. *)valwatch:repo->branch->?init:commit->(commitDiff.t->unitLwt.t)->watchLwt.t(** [watch t b f] calls [f] on every change in [b]. *)valwatch_all:repo->?init:(branch*commit)list->(branch->commitDiff.t->unitLwt.t)->watchLwt.t(** [watch_all t f] calls [f] on every branch-related change in [t],
including creation/deletion events. *)includeBranch.Swithtypet=branch(** Base functions for branches. *)end(** [Path] provides base functions for the stores's paths. *)modulePath:Path.Swithtypet=pathandtypestep=stepmoduleMetadata:Metadata.Swithtypet=metadata(** [Metadata] provides base functions for node metadata. *)(** Backend functions, which might be used by the backends. *)moduleBackend:Backend.SwithmoduleSchema=SchemawithtypeSlice.t=sliceandtypeRepo.t=repoandmoduleHash=HashandmoduleNode.Path=PathandtypeContents.key=contents_keyandtypeNode.key=node_keyandtypeCommit.key=commit_keytypeRemote.t+=|EofBackend.Remote.endpoint(** Extend the [remote] type with [endpoint]. *)(** {2 Converters to backend types} *)valof_backend_node:repo->Backend.Node.value->nodevalto_backend_node:node->Backend.Node.valueLwt.tvalto_backend_portable_node:node->Backend.Node_portable.tLwt.tvalto_backend_commit:commit->Backend.Commit.value(** [to_backend_commit c] is the backend commit object associated with the
commit [c]. *)valof_backend_commit:repo->Backend.Commit.Key.t->Backend.Commit.value->commit(** [of_backend_commit r k c] is the commit associated with the backend commit
object [c] that hash key [k] in [r]. *)valsave_contents:[>write]Backend.Contents.t->contents->contents_keyLwt.t(** Save a content into the database *)valsave_tree:?clear:bool->repo->[>write]Backend.Contents.t->[>read_write]Backend.Node.t->tree->kinded_keyLwt.t(** Save a tree into the database. Does not do any reads.
When [clear] is set (the default), the tree cache is emptied upon the
function's completion, mirroring the effect of invoking {!Tree.clear}. *)(** {2 Deprecated} *)valmaster:repo->tLwt.t[@@ocaml.deprecated"Use `main` instead."](** @deprecated Use {!main} instead *)endmoduletypeS=sigtypehash(** @inline *)includeS_generic_keywithtypeSchema.Hash.t=hashandtypehash:=hashandtypecontents_key=hashandtypenode_key=hashandtypecommit_key=hashendmoduleS_is_a_generic_keyed(X:S):S_generic_key=XmoduletypeMaker_generic_key=sigtypeendpointincludeKey.Store_spec.SmoduleMake(Schema:Schema.S):S_generic_keywithmoduleSchema=SchemaandtypeBackend.Remote.endpoint=endpointandtypecontents_key=(Schema.Hash.t,Schema.Contents.t)contents_keyandtypenode_key=Schema.Hash.tnode_keyandtypecommit_key=Schema.Hash.tcommit_keyendmoduletypeMaker=Maker_generic_keywithtype('h,_)contents_key='handtype'hnode_key='handtype'hcommit_key='hmoduletypeJson_tree=functor(Store:SwithtypeSchema.Contents.t=Contents.json)->sigincludeContents.Swithtypet=Contents.jsonvalto_concrete_tree:t->Store.Tree.concretevalof_concrete_tree:Store.Tree.concrete->tvalget_tree:Store.tree->Store.path->tLwt.t(** Extract a [json] value from tree at the given key. *)valset_tree:Store.tree->Store.path->t->Store.treeLwt.t(** Project a [json] value onto a tree at the given key. *)valget:Store.t->Store.path->tLwt.t(** Extract a [json] value from a store at the given key. *)valset:Store.t->Store.path->t->info:(unit->Store.info)->unitLwt.t(** Project a [json] value onto a store at the given key. *)endmoduletypeKV_generic_key=S_generic_keywithtypeSchema.Path.step=stringandtypeSchema.Path.t=stringlistandtypeSchema.Branch.t=stringmoduletypeKV=SwithtypeSchema.Path.step=stringandtypeSchema.Path.t=stringlistandtypeSchema.Branch.t=stringmoduletypeKV_maker_generic_key=sigtypeendpointtypemetadatatypehashtypeinfoincludeKey.Store_spec.SmoduleMake(C:Contents.S):KV_generic_keywithmoduleSchema.Contents=CandtypeSchema.Metadata.t=metadataandtypeBackend.Remote.endpoint=endpointandtypeSchema.Hash.t=hashandtypecontents_key=(hash,C.t)contents_keyandtypenode_key=hashnode_keyandtypecommit_key=hashcommit_keyandtypeSchema.Info.t=infoendmoduletypeKV_maker=KV_maker_generic_keywithtype('h,_)contents_key='handtype'hnode_key='handtype'hcommit_key='hmoduletypeSigs=sigmoduletypeS=SmoduletypeMaker=MakermoduletypeJson_tree=Json_treemoduletypeKV=KVmoduletypeKV_maker=KV_makermoduleGeneric_key:sigmoduletypeS=S_generic_keymoduletypeKV=KV_generic_keymoduletypeMaker=Maker_generic_keymoduletypeKV_maker=KV_maker_generic_keyendtypeRemote.t+=|Store:(moduleGeneric_key.Swithtypet='a)*'a->Remote.tmoduleMake(B:Backend.S):Generic_key.SwithmoduleSchema=B.Schemaandtypeslice=B.Slice.tandtyperepo=B.Repo.tandtypecontents_key=B.Contents.keyandtypenode_key=B.Node.keyandtypecommit_key=B.Commit.keyandmoduleBackend=BmoduleJson_tree:Json_tree(** [Json_tree] is used to project JSON values onto trees. Instead of the
entire object being stored under one key, it is split across several keys
starting at the specified root key. *)end