Module Volgo.Vcs
Source
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.
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.
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
.
Error handling
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.
Creating repositories
A platform
in vcs' terminology is an online software development service where users host repositories, such as "GitHub"
.
A type to uniquely identify a repository hosted on a platform.
The name of a repository as configured on a platform such as GitHub.
The root of a version control repository that is expected to exists on the local file system.
Initialize a Git repository at the given path. This errors out if a repository is already initialized there.
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.
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.
Revisions
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.
Creating mock revisions for use in expect tests.
Maintaining a mapping between mock revs and actual revs.
Commits
A path for a file versioned in a repository.
When this succeeds, this returns the revision of the commit that was just created.
Files
Files IO
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.
Create a new file, or truncate an existing one.
Returns the entries of the supplied directory, ordered increasingly according to String.compare
. The result does not include the unix entries ".", "..".
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.
Computing diffs
Number of lines involved in a change.
Manipulating the graph in memory
A log is a complete listing of the structure of the dag.
Capturing the information related to git refs.
Building an in-memory representation of the commit graph of a git repository for queries related to the structure of its nodes and edges.
Current branch & revision
User config
Author information as commonly used in Git.
A user handle, such as user pseudonym on GitHub, used in CR comments, etc.
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.
Low level Git cli
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.
Manipulating the output of processes run by vcs and backends - typically the "git"
command.
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
;;
Low level Mercurial cli
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.
Manipulating the output of processes run by vcs and backends - typically the "hg"
command.
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
;;
Non-raising APIs
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.
An Vcs
API based on Result
and Vcs.Err
.
An Vcs
API in the style of Rresult.
A functor to build non raising interfaces for Vcs
based on a custom result type.
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.