My own corner of monopam
monopampam ocaml-git
6 folders · 14 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 (loose files and packed-refs, or the reftable stack a repository made with git init --ref-format=reftable keeps) and configuration. On top of that it implements the operations such tools need (clone, fetch, push, pull, checkout, merge, diff, history rewriting, subtree split, and notes with their union merge) 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 five 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; listing, fetching, pushing and serving over the git protocol, git://, in Git_net.Daemon; listing, fetching and pushing over smart HTTP with Git's credential helpers and its http.* settings in Git_net.Smart_http, Git_net.Credential and Git_net.Http_config, the store, cache and osxkeychain helpers answered without a git process;
  • git-resolve: a stopped operation's conflict resolved by one request to a language model, through llm-eio, and staged path by path for a person to review (git-x resolve and mq resolve call it);
  • git-x: a command-line tool built on these 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. A rename has two paths: name both, or the staged deletion of the old one stays behind and the commit carries both files. The message is one -m per paragraph; there is no -F.
  • 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|verify are the other subtree operations. tree drop -p GLOB removes the matching paths from history by rewriting objects and references, leaving the working tree and HEAD alone; when the current branch is among the rewritten references it also unstages the dropped paths, so the next commit cannot bring them back. --dry-run reports what it would unstage.
  • git-x resolve resolves the conflict git status shows once a merge, a rebase, a cherry-pick, a revert or git am stops. It asks a language model once, through the git-resolve library, for the lines that replace each conflict region, writes each file whose answer passes its checks to the working tree and stages it, and prints the operation's next command, such as git rebase --continue. It never commits; a path whose answer fails a check is named with the reason and left unmerged. The API key is FIREWORKS_API_KEY.
  • git-x clone, fetch, pull and push talk to remotes over smart HTTP and SSH, and git-x serve serves a repository to them. pull only fast-forwards and never overwrites an uncommitted change: it applies the incoming diff to every path whose working copy still matches the old commit, keeps and names each path you changed, and stops after the fetch, merging nothing, when the histories have diverged.
$ 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.