Grokmirror
Grokmirror keeps collections of git repositories in sync between an origin server and any number of replicas. It was written for kernel.org, where most of the several hundred repositories are forks of the same kernel tree.
Overview
The origin server publishes a manifest: a small JSON file that lists every repository it carries, along with a timestamp and a special “fingerprint” for each one, calculated from the state of all of its refs. A git hook refreshes the manifest whenever a repository changes, so it is always current.
Replicas poll that manifest over HTTP. If it has not changed, the origin
answers with a 304 Not Modified and the replica does no work at all.
If it has changed, the replica compares it against its own copy and only
touches the repositories whose fingerprints moved – everything else is
left alone.
That is the whole idea, and it is what makes grokmirror cheap on both sides. A replica of a collection that had two repositories pushed to since its last pass does exactly two fetches, no matter how big the collection is.
Concepts
The manifest
The manifest is a JSON dictionary keyed by the repository path relative to the toplevel, usually served gzip-compressed:
{
"/path/to/bare/repository.git": {
"description": "Repository description",
"head": "ref: refs/heads/branchname",
"reference": "/path/to/reference/repository.git",
"forkgroup": "forkgroup-guid",
"modified": timestamp,
"fingerprint": sha1sum(git show-ref),
"symlinks": [
"/location/to/symlink"
]
}
}
Replicas fetch it with If-Modified-Since, so an unchanged manifest
costs a single conditional request.
Object storage repositories
Mirroring a few hundred forks of linux.git naively means storing the
same few gigabytes of objects a few hundred times. Grokmirror avoids that
with object storage repositories, or “objstore” for short.
grok-fsck recognizes related repositories by looking at their root
commits. When it finds two or more of them, it creates a shared objstore
repository and fetches every ref from each sibling into it, under
refs/virtual/<hash-of-sibling>/. The original repositories are then
pointed at the objstore through objects/info/alternates and repacked
down to almost nothing.
The objstore repository is repacked with delta islands enabled, so clones of any individual sibling stay fast.
Warning
Because siblings share an object pool, any object from one sibling can
in principle be retrieved through another one if the object hash is
known – the same behavior you see on GitHub forks. If some of your
repositories must not leak objects, list them in core.private.
See Configuration.
Features
Cheap change detection – one conditional HTTP request when nothing moved
Shared object storage for forks, with delta-island repacking
Continuous (daemon) operation with optional push notifications
Routine repacking, fsck, and corruption reporting via
grok-fsckAutomatic recloning of repositories that turn up corrupt
Symlink,
projects.list, andgit-daemon-export-okhandlingHooks at every interesting point: per-repository, after clones, and when the work queue drains
Companion tools for public-inbox archives and for Android’s
repo
Non-features
Only bare repositories are supported
No mirroring of anything that is not a git repository
No authentication scheme of its own – use netrc, ssh, or your httpd
The tools
|
Build and update the manifest (origin side) |
|
Clone and update repositories (replica side) |
|
Repack, fsck, and manage object storage |
|
Generate |
|
Update repositories that grokmirror does not manage |
|
Pipe new public-inbox messages into a command |
|
Index mirrored public-inbox repositories |
Each has its own man page; this documentation covers how they fit together.
FAQ
Why is it called “grokmirror”?
Because it is developed at kernel.org and “grok” is a mirror of “korg”. Also because it groks git mirroring. It both long predates and has nothing to do with a certain LLM bot.
Why not just use rsync?
Rsync is a poor fit for git trees, which consist of many small files that rarely change. Rsync has to checksum every file on every run, which mostly produces disk thrashing.
Worse, if repositories share objects with each other, rsync will only produce working repositories when the disk paths are identical on both ends. Anything else gives you broken repositories.
And it is a bit silly to begin with: git already knows exactly how to describe what changed between two revisions.
Can a replica also be an origin?
Yes. grok-pull writes its own manifest as it goes, so a replica can
publish that manifest and serve its own downstream replicas.