Skip to content

gitrepo: serve immutable Git checkouts over NFS - #29

Draft
tomhjp wants to merge 1 commit into
mainfrom
tomhjp/git-repo-checkouts
Draft

tomhjp wants to merge 1 commit into
mainfrom
tomhjp/git-repo-checkouts

Conversation

@tomhjp

@tomhjp tomhjp commented Sep 11, 2026

Copy link
Copy Markdown
Member

This commit adds the ability to serve an immutable, shallow checkout of a git repository at a specific commit over NFS. It includes some minimal git metadata so that the git CLI can run read-only commands much like any other shallow checkout.

Callers acquire a checkout before mounting /repos// and release it after the guest stops. Concurrent guests share an export until its last lease is released. The checkout is read-only, including its synthetic Git metadata. The index assumes tracked files are unchanged, so this view is not suitable as the backing filesystem for a writable overlay.

The backing storage is a bare git repo on disk per configured repository. This is preferred over packing all git repos into the same git storage that the existing gitstore uses for 2 main reasons; firstly the methods required of each store are fairly specialised to their use case, and secondly, I wanted to avoid moving into "interesting" territory for git storage. A store per repo keeps repository data separate for isolation and keeps us on the happy path for the kinds of use cases that git storage is generally well optimised for.

Git repository NFS handles are stable SHA-256 hashes of the repository name, commit, and path. Their reverse mappings are kept in memory until the checkout's last lease is released. Unlike the go mod implementation, these mappings are not reconstructed on a cache miss: after release or restart, an old handle remains stale until the checkout is acquired and the path's mapping is registered again. This keeps checkout state bounded by active leases without coupling file identity to a lease's lifetime.

The nfsexports package implements a multiplexer to allow us to serve multiple exports on the same NFS server, keeping compatibility with clients that rely on rpcbind. It carves out a namespace for the existing go mod NFS handles (64 bytes long), and a schema for adding other namespaces alongside the new git repo handle namespace.

@tomhjp
tomhjp requested a review from bradfitz September 11, 2026 14:39
This commit adds the ability to serve an immutable, shallow checkout of
a git repository at a specific commit over NFS. It includes some minimal
git metadata so that the git CLI can run read-only commands much like
any other shallow checkout.

Callers acquire a checkout before mounting /repos/<repo>/<commit> and
release it after the guest stops. Concurrent guests share an export until
its last lease is released. The checkout is read-only, including its
synthetic Git metadata. The index assumes tracked files are unchanged, so
this view is not suitable as the backing filesystem for a writable overlay.

The backing storage is a bare git repo on disk per configured
repository. This is preferred over packing all git repos into the same
git storage that the existing gitstore uses for 2 main reasons; firstly
the methods required of each store are fairly specialised to their use
case, and secondly, I wanted to avoid moving into "interesting"
territory for git storage. A store per repo keeps repository data
separate for isolation and keeps us on the happy path for the kinds of
use cases that git storage is generally well optimised for.

Git repository NFS handles are stable SHA-256 hashes of the repository
name, commit, and path. Their reverse mappings are kept in memory until
the checkout's last lease is released. Unlike the go mod implementation,
these mappings are not reconstructed on a cache miss: after release or
restart, an old handle remains stale until the checkout is acquired and
the path's mapping is registered again. This keeps checkout state bounded
by active leases without coupling file identity to a lease's lifetime.

The nfsexports package implements a multiplexer to allow us to serve
multiple exports on the same NFS server, keeping compatibility with
clients that rely on rpcbind. It carves out a namespace for the existing
go mod NFS handles (64 bytes long), and a schema for adding other
namespaces alongside the new git repo handle namespace.

Signed-off-by: Tom Proctor <tomhjp@users.noreply.github.com>
@tomhjp
tomhjp force-pushed the tomhjp/git-repo-checkouts branch from b727e44 to c4d83b0 Compare September 11, 2026 14:42
@bradfitz

Copy link
Copy Markdown
Member

an NFSv3 filehandle is 64 bytes (we conventional treat it as two 32 byte sections) but a git hash is only 20 bytes. I feel like we have plenty of space to play some encoding games on mapping e.g. "7th entry of tree XXXXXX" in the handle, so then we can just do a git lookup of tree object XXXXX and find the 7th entry to get back to the hash of the file, etc?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants