My own corner of monopam
monopampam ocaml-git
6 folders · 13 files

README.md

git #

Warning. This is a work-in-progress experimental fork of ocaml-git. Do not use it in production; use ocaml-git instead.

Git in pure OCaml: objects, packs, repositories and the smart protocol.

The smart protocol is git's native transfer protocol over HTTP or SSH: the two sides exchange the references and object hashes they hold, and the server then sends one pack file holding only the objects the client lacks.

Tools that manage many repositories at once, such as a monorepo that splits and merges subtrees, spend most of their time spawning git and parsing its output, and they fight over index.lock when two of them work in one checkout. This package reads and writes git's own formats directly instead: loose objects, pack files and their indexes, bitmaps, references and configuration. On top of that it implements the operations such tools need (clone, fetch, push, pull, checkout, merge, diff, history rewriting and subtree split) and a smart-protocol server that a stock git client can clone from. The object code is derived from ocaml-git and carton; the rest is newer and has had far less use.

The code is split into four opam packages:

  • nox-git: the formats, and the repository operations as functors over an abstract object store (Git.Store.S), with no I/O of their own;
  • nox-git-eio: those operations over a repository on disk, using Eio (Git_eio.Repository, Git_eio.Subtree, Git_eio.Rewrite, and so on);
  • nox-git-net: fetch, push and clone over smart HTTP and SSH, with a smart-HTTP server in nox-git-net.server and SSH fetch, push and serving in nox-git-net.ssh;
  • git-x: a command-line tool built on the three libraries.

Installation #

Install with opam:

$ opam install nox-git

If opam cannot find the package, it may not yet be released in the public opam-repository. Add the overlay repository, then install it:

$ opam repo add samoht https://tangled.org/gazagnaire.org/opam-overlay.git
$ opam update
$ opam install nox-git

Usage #

Build a blob, a tree holding it and a commit of that tree, and compute their object hashes without touching a repository:

open Git

(* Create a blob *)
let blob = Blob.of_string "Hello, World!\n"
let blob_hash = Blob.digest blob

(* Create a tree entry *)
let entry = Tree.entry ~perm:`Normal ~name:"hello.txt" blob_hash
let tree = Tree.v [ entry ]
let tree_hash = Tree.digest tree

(* Create a commit *)
let author = User.v ~name:"Alice" ~email:"alice@example.com"
  ~date:(Int64.of_float (Unix.gettimeofday ())) ()
let commit = Commit.v
  ~tree:tree_hash
  ~author
  ~committer:author
  (Some "Initial commit")

Command-line tool (git-x) #

git-x runs git operations for monorepos natively: it reads objects directly instead of spawning git, and it updates references atomically, so it can run while other people or tools edit the same checkout. Its main commands are:

  • git-x status [-v] DIR shows working-tree and upstream status for every repository under DIR. With -v, a modified OCaml file also lists the definitions whose source differs from HEAD, by address (:Module.value), using the nox-git.outline library. A changed submodule is shown by its changed members.
  • git-x commit create -m MSG PATH... commits the working-tree state of the named paths straight into the object database, without reading or writing the index. It never picks up files someone else staged and never waits on index.lock; afterwards it refreshes the index entries of the committed paths so that git status shows them clean.
  • git-x commit drop|reword|sort|squash|split SHA rewrites one commit in history and leaves the working tree and HEAD alone.
  • git-x tree split PREFIX extracts the history of a subdirectory as a standalone commit chain. The result is cached, so running it again costs almost nothing.
  • git-x tree graft --prefix DIR REV is the inverse. It replays a standalone chain under DIR as ordinary single-parent commits, one commit out for each chain commit in, so splitting the result back out gives the same chain, hash for hash. It records how far it has got in refs/graft/<DIR> instead of testing ancestry, so a second run replays nothing and the result does not depend on the shape of either history. Paths the chain does not touch are left alone, and HEAD, the working tree and the index move together.
  • git-x tree add|drop|check|fix|verify are the other subtree operations.
  • git-x clone, fetch, pull and push talk to remotes over smart HTTP and SSH, and git-x serve serves a repository to them.
$ git-x status -v
.  (main)  dirty
    M lib/parser.ml
        :Lexer.token
        :parse

Credits #

The object and pack code is a simplified rewrite derived from:

  • ocaml-git, the git format and protocol in pure OCaml, by Thomas Gazagnaire and Romain Calascibetta;
  • carton, the PACKv2 implementation, by Romain Calascibetta.

License #

ISC. See LICENSE.md.