Volgo.Vcs
A Versatile Library for Git Operations.
Vcs is a library providing a direct-style API for interacting with Git repositories in a type-safe way. It is designed as an interface composed of traits, each providing different functionalities associated with Git interaction.
Vcs dynamically dispatches its implementation at runtime thanks to a design powered by the use of OCaml Objects under the hood, with some design guidelines aimed at making it so that users only need to make limited direct use of objects in their code.
module Trait : sig ... end
The traits that Vcs
depends on to implement its functionality.
At its core, Vcs operates on a value encapsulating the functionalities implemented by a set of traits, represented by a set of classes indicating which functions from the API you can use with such a vcs
.
In your interfaces, you should specify the exact list of traits you need, while keeping the type of the object parameter open, to make your code flexible and compatible with backend offering more traits than your strict requirements.
val create : 'a -> 'a t
create traits
returns a vcs
that implements a given set of traits. Typical users do not use create
vcs objects directly, but rather will rely on helper library. See for example Volgo_git_eio.create
.
The default API of Vcs
is one that exposes functions that may raise a single exception Err.E
, which carries an abstract payload err
containing printable information. err
is not meant for pattern matching - we're only targeting a non-specialized error recovery.
Err.E
is meant to be the only exception ever raised by functions from the Vcs
interface. Err.t
doesn't carry the raw backtrace, so you'll need to manipulate the backtrace yourself if you care about it (like you would with any other exceptions).
A general design principle that we follow here is that if an error result is of interest for pattern matching, we want to incorporate it into the successful branch of the function's result, rather than in its error part - either by making the result a variant type, or otherwise adding more functions to the API with finer granularity for particular use cases. Consider opening an issue on GitHub
if what you'd like to match on isn't available.
As library authors we realize that manipulating Result.t
is a popular choice too: we also export the Vcs
's functionality via non-raising APIs if you prefer.
module Platform : sig ... end
Online software development platforms where users host repositories, e.g. "GitHub"
.
module Repo_name : sig ... end
The name of a repository as configured on a platform such as GitHub.
module Repo_root : sig ... end
The root of a version control repository that is expected to exists on the local file system.
module Url : sig ... end
A url to access a repository on a supported platform.
val init :
< Trait.init.. > t ->
path:Fpath_sexp0.Absolute_path.t ->
Repo_root.t
Initialize a Git repository at the given path. This errors out if a repository is already initialized there.
val find_enclosing_repo_root :
< Trait.file_system.. > t ->
from:Fpath_sexp0.Absolute_path.t ->
store:(Fpath_sexp0.Fsegment.t * 'store) list ->
('store * Repo_root.t) option
find_enclosing_repo_root vcs ~from:dir ~store
walks up the path from the given directory dir
and stops when at the root of a repository. If no repo root has been found when reaching the root path "/"
, the function returns None
.
The way we determine whether we are at the root of a repo is by looking for the presence of one of the store entries in the directory (e.g. ".git"
).
When present, we do not check that the store is itself a directory, so that this function is able to correctly infer and return the root of Git repos where ".git"
is not a directory (e.g. Git worktrees).
You may supply several stores if you want to stop at the first store that is encountered, if you do not know in what kind of repo you are. For example, [".git", `Git; ".hg", `Hg]
. The store that was matched is returned as part of the result.
If you know you are in a Git repository you may want to use the wrapper find_enclosing_git_repo_root
instead.
val find_enclosing_git_repo_root :
< Trait.file_system.. > t ->
from:Fpath_sexp0.Absolute_path.t ->
Repo_root.t option
find_enclosing_git_repo_root vcs ~from:dir
is a convenient wrapper around find_enclosing_repo_root
for Git repositories. This is looking for the deepest directory containing a ".git"
entry, starting from dir
and walking up.
module Rev : sig ... end
A revision uniquely identifies a node in the dag formed by the commits of a repository. The name was inherited from Mercurial. For git, this correspond to the commit-hash. In both systems, these are 40-chars hashes.
module Mock_rev_gen : sig ... end
Creating mock revisions for use in expect tests.
module Mock_revs : sig ... end
Maintaining a mapping between mock revs and actual revs.
module Commit_message : sig ... end
A commit message.
module Path_in_repo : sig ... end
A path for a file versioned in a repository.
val add :
< Trait.add.. > t ->
repo_root:Repo_root.t ->
path:Path_in_repo.t ->
unit
val commit :
< Trait.rev_parse ; Trait.commit.. > t ->
repo_root:Repo_root.t ->
commit_message:Commit_message.t ->
Rev.t
When this succeeds, this returns the revision of the commit that was just created.
module File_contents : sig ... end
Representing the raw contents of files on disk.
val ls_files :
< Trait.ls_files.. > t ->
repo_root:Repo_root.t ->
below:Path_in_repo.t ->
Path_in_repo.t list
val show_file_at_rev :
< Trait.show.. > t ->
repo_root:Repo_root.t ->
rev:Rev.t ->
path:Path_in_repo.t ->
[ `Present of File_contents.t | `Absent ]
Vcs contains some basic backend based functions to manipulate files from the file system. The goal is to allow some users of Vcs
to use this simple API without committing to a particular implementation. For example, if the backend used at runtime is based on Eio
, these functions will use Eio.Path
underneath.
val load_file :
< Trait.file_system.. > t ->
path:Fpath_sexp0.Absolute_path.t ->
File_contents.t
val save_file :
?perms:int ->
< Trait.file_system.. > t ->
path:Fpath_sexp0.Absolute_path.t ->
file_contents:File_contents.t ->
unit
Create a new file, or truncate an existing one.
val read_dir :
< Trait.file_system.. > t ->
dir:Fpath_sexp0.Absolute_path.t ->
Fpath_sexp0.Fsegment.t list
Returns the entries of the supplied directory, ordered increasingly according to String.compare
. The result does not include the unix entries ".", "..".
module Branch_name : sig ... end
module Remote_name : sig ... end
module Remote_branch_name : sig ... end
module Tag_name : sig ... end
val rename_current_branch :
< Trait.branch.. > t ->
repo_root:Repo_root.t ->
to_:Branch_name.t ->
unit
This translates to git branch --move $NAME
, which is used to enforce the name of a default branch during tests. If the current branch already has this name, this has no further effect.
module Name_status : sig ... end
module Num_status : sig ... end
module Num_lines_in_diff : sig ... end
Number of lines involved in a change.
val name_status :
< Trait.name_status.. > t ->
repo_root:Repo_root.t ->
changed:Name_status.Changed.t ->
Name_status.t
val num_status :
< Trait.num_status.. > t ->
repo_root:Repo_root.t ->
changed:Num_status.Changed.t ->
Num_status.t
module Log : sig ... end
A log is a complete listing of the structure of the dag.
module Ref_kind : sig ... end
module Refs : sig ... end
Capturing the information related to git refs.
module Graph : sig ... end
Building an in-memory representation of the commit graph of a git repository for queries related to the structure of its nodes and edges.
val log : < Trait.log.. > t -> repo_root:Repo_root.t -> Log.t
val refs : < Trait.refs.. > t -> repo_root:Repo_root.t -> Refs.t
val graph : < Trait.log ; Trait.refs.. > t -> repo_root:Repo_root.t -> Graph.t
val current_branch :
< Trait.rev_parse.. > t ->
repo_root:Repo_root.t ->
Branch_name.t
val current_revision :
< Trait.rev_parse.. > t ->
repo_root:Repo_root.t ->
Rev.t
module Author : sig ... end
Author information as commonly used in Git.
module User_email : sig ... end
module User_handle : sig ... end
A user handle, such as user pseudonym on GitHub, used in CR comments, etc.
module User_name : sig ... end
User name information as specified by the Git config user.name value.
During tests in the GitHub environment we end up having issues if we do not set the user name and email. Also, we rather not do it globally. If this is never called, the current user config is used as usual by Git processes invocations.
val set_user_name :
< Trait.config.. > t ->
repo_root:Repo_root.t ->
user_name:User_name.t ->
unit
val set_user_email :
< Trait.config.. > t ->
repo_root:Repo_root.t ->
user_email:User_email.t ->
unit
This part of Vcs provides direct access to the "git"
command line interface. This should be considered non portable and brittle. Generally speaking, one hope is that you shouldn't have to use git
directly. Instead, consider requesting proper integration of your use case into the typed and parametrized API of Vcs
. However, sometimes this is just what you need e.g. in tests, or for quick one-off, and if your backend happens to be a CLI
based backend, we might as well expose this. Use at your own risk/convenience.
module Git : sig ... end
Manipulating the output of processes run by vcs and backends - typically the "git"
command.
val git :
?env:string array ->
?run_in_subdir:Path_in_repo.t ->
< Trait.git.. > t ->
repo_root:Repo_root.t ->
args:string list ->
f:(Git.Output.t -> 'a) ->
'a
Note a non trivial behavior nuance depending on whether you are using this function using the raising or non-raising API. In the raising API, f
is allowed to raise: git
will catch any exception raised by f
, and rewrap it under a proper E err
exception with added context. In the non-raising APIs, if f
raises instead of returning an Error
, that exception would escape the function git
and be raised by git
as an uncaught exception. This would be considered a programming error.
Some helpers are provided by the module Git
to help you build the f
parameter. Non-raising modules are also included in the Git
module dedicated to their respective result type (see for example Volgo_base.Vcs.Git.Or_error
).
The expectation is that you should be using the Git
module of the API you are using to access the git
function, and not mix and match.
For example using the raising API::
let git_status () : string =
Vcs.git vcs ~repo_root ~args:[ "status" ] ~f:Vcs.Git.exit0_and_stdout
;;
Or the non-raising API (result):
let git_status () : string Vcs.Result.t =
Vcs.Result.git
vcs
~repo_root
~args:[ "status" ]
~f:Vcs.Git.Result.exit0_and_stdout
;;
This part of Vcs provides direct access to the "hg"
command line interface when operating in a Mercurial repository.
This is similar to the low level access provided by git
and the same restrictions and advices apply.
module Hg : sig ... end
Manipulating the output of processes run by vcs and backends - typically the "hg"
command.
val hg :
?env:string array ->
?run_in_subdir:Path_in_repo.t ->
< Trait.hg.. > t ->
repo_root:Repo_root.t ->
args:string list ->
f:(Hg.Output.t -> 'a) ->
'a
Simiar to git
, helpers are provided by the module Hg
to build the f
parameter.
The expectation is that you should be using the Hg
module of the API you are using to access the hg
function, and not mix and match.
For example using the raising API:
let hg_status () : string =
Vcs.hg vcs ~repo_root ~args:[ "status" ] ~f:Vcs.Hg.exit0_and_stdout
;;
Or the non-raising API (result):
let hg_status () : string Vcs.Result.t =
Vcs.Result.hg vcs ~repo_root ~args:[ "status" ] ~f:Vcs.Hg.Result.exit0_and_stdout
;;
For convenience and to allow experimenting with different error handling strategies, Vcs
exports non-raising APIs. The functions there return Result.t
s instead of raising.
module Result : sig ... end
An Vcs
API based on Result
and Vcs.Err
.
module Non_raising : sig ... end
A functor to build non raising interfaces for Vcs
based on a custom result type.
module Private : sig ... end
This part of the interface is not stable. Things may break without notice and outside of the guidelines set by semver when upgrading to a new version of Vcs
. This is used e.g. by tests or libraries with strong ties to Vcs
. Do not use.