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 innox-git-net.serverand SSH fetch, push and serving innox-git-net.ssh; listing, fetching, pushing and serving over the git protocol,git://, inGit_net.Daemon; listing, fetching and pushing over smart HTTP with Git's credential helpers and itshttp.*settings inGit_net.Smart_http,Git_net.CredentialandGit_net.Http_config, thestore,cacheandosxkeychainhelpers answered without a git process;git-resolve: a stopped operation's conflict resolved by one request to a language model, throughllm-eio, and staged path by path for a person to review (git-x resolveandmq resolvecall 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] DIRshows working-tree and upstream status for every repository underDIR. With-v, a modified OCaml file also lists the definitions whose source differs fromHEAD, by address (:Module.value), using thenox-git.outlinelibrary. 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 onindex.lock; afterwards it refreshes the index entries of the committed paths so thatgit statusshows 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-mper paragraph; there is no-F.git-x commit drop|reword|sort|squash|split SHArewrites one commit in history and leaves the working tree andHEADalone.git-x tree split PREFIXextracts 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 REVis the inverse. It replays a standalone chain underDIRas 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 inrefs/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, andHEAD, the working tree and the index move together.git-x tree add|drop|verifyare the other subtree operations.tree drop -p GLOBremoves the matching paths from history by rewriting objects and references, leaving the working tree andHEADalone; when the current branch is among the rewritten references it also unstages the dropped paths, so the next commit cannot bring them back.--dry-runreports what it would unstage.git-x resolveresolves the conflictgit statusshows once a merge, a rebase, a cherry-pick, a revert orgit amstops. It asks a language model once, through thegit-resolvelibrary, 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 asgit rebase --continue. It never commits; a path whose answer fails a check is named with the reason and left unmerged. The API key isFIREWORKS_API_KEY.git-x clone,fetch,pullandpushtalk to remotes over smart HTTP and SSH, andgit-x serveserves a repository to them.pullonly 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.