GitSnapFS
GitSnapFS exposes snapshots of a Git repository as a read-only filesystem designed for safe inspection, automated audits, and tooling integration.
Highlights
commits/<revspec>presents the tree for an individual commit; annotated tags and other revspecs are peeled to their commit (commits/v1.0works). Likewisetrees/<revspec>peels to a tree, sotrees/HEADnames HEAD's root tree.branches/andtags/mirror the ref hierarchy:refs/heads/feature/fooappears asbranches/feature/foo, with intermediate directories, and each ref is a symlink intocommits/(ortrees/for tag objects pointing at trees).HEADis a symlink intocommits/.- Git tree-entry modes are honoured:
100755blobs are executable (0o555),100644blobs are0o444,120000blobs are symlinks. Submodule gitlinks appear as empty directories. - The filesystem is strictly read-only and answers requests lazily; updates in the underlying repo are surfaced without a pre-scan.
- Directory listings leave
.and..to the kernel, letting path caches stay in userspace. - We leverage the kernel’s zero-message open/opendir paths (
NO_OPEN_SUPPORT,NO_OPENDIR_SUPPORT) for near-native performance once data is cached.
Caching: the kernel owns it
gix can only inflate a blob in its entirety, but the kernel asks for file data
in readahead-sized chunks. Instead of keeping a userspace cache, the first
read of a file inflates the blob once, replies with the requested chunk, and
pushes the whole blob into the kernel page cache via FUSE_NOTIFY_STORE. Two
details are load-bearing: the push runs on a dedicated notifier thread
(pushing from the request-handling thread deadlocks against the folio locks
of in-flight reads; see tests/mount_read.rs), and it is issued in segments
from the back of the file forwards, so it meets the kernel's front-to-back
readahead wave head-on instead of trailing it (measured on an 8 MiB file:
8 daemon reads instead of 46). Combined with FOPEN_KEEP_CACHE (the kernel
default under zero-message open), subsequent reads — same pass, later opens,
mmap — are served entirely from the page cache, and eviction under memory
pressure is the kernel's call. GitSnapFS retains no blob data of its own; a
dropped push merely means the kernel keeps asking chunk by chunk, each
answered by a full re-inflation.
Inode scheme and collisions
Inodes are stateless: the top nibble tags the inode class (directory, regular
blob, executable blob, symlink blob, gitlink, ref-tree node), and the low 60
bits are the object id's leading 15 hex digits (or a name hash for ref nodes).
Folding the tree-entry mode into the inode means the same blob reachable as
both 100644 and 100755 gets two distinct inodes, so attributes are always
coherent. See src/inode.rs for the full layout.
Truncating object ids to 60 bits admits birthday collisions (probability
≈ n²/2⁶¹ for n objects, i.e. ~4×10⁻⁵ at 10 million objects). Collisions are
detected, not silent: inodes resolve through the object database's
unambiguous-prefix lookup, so a collision surfaces as EIO plus an error log
rather than another object's content. In addition, each object entry carries
object-id bits 64..128 in the FUSE generation field: the kernel treats
(nodeid, generation) as the inode identity, so on a collision it evicts the
stale inode instead of serving one object's cached pages or attributes as the
other's, and NFS file handles for the evicted object return ESTALE.
Enumerating commits/ or trees/ is deliberately unsupported and returns
ENOTSUP.
Requirements
- Linux with FUSE kernel support that advertises
EXPORT_SUPPORT,ZERO_MESSAGE_OPEN, andZERO_MESSAGE_OPENDIR. fusermount/fusermount3(typically provided byfusepackages).- Rust toolchain nightly or stable recent enough to build the dependency graph (
cargo,rustc).
Quick Start
cargo run -- --repo path/to/.git --mountpoint /tmp/gitfs
The mount exposes the root layout (commits, branches, tags, HEAD). Unmount with:
fusermount -u /tmp/gitfs # or fusermount3 -uOr by just terminating the process.
Development
-
Design notes live in
codex_spec.md. -
Formatting and linting are enforced with the following commands:
cargo fmt cargo clippy --all-targets --all-features -- -D clippy::pedantic -D clippy::style -D clippy::cargo
-
The
clippy.tomldocuments unavoidable duplicate crate versions coming from upstream dependencies. -
Please keep the filesystem read-only and avoid libfuse/libgit2 shims; all Git access goes through
gixand FUSE plumbing throughfuse-backend-rs.