Upstream calls this a proof of concept, and promises no completeness, performance or API stability.
These pages say plainly what it doesn't do yet. For jj's own internals (commits, trees, conflicts, the
working copy), see the jj architecture reference. That reference
describes its own pinned jj version, which can differ from the jj-lib
0.43.0 this crate builds against.
The whole file system on one page
Left: a request's path from a process down to the repository on disk. Right: what the mount contains. Every box links to the page that explains it (or to the pinned source while that page is being written). jj-lib boxes link to the jj reference or to jj-lib 0.43.0's source.
The layers
Kernel and fuser
main.rs takes a mountpoint and a workspace root,
loads the repository at its current operation, and calls fuser::spawn_mount with the
options RO and FSName("jjfs"). It then waits for SIGINT, SIGTERM or SIGHUP.
The fuser crate talks to the kernel and calls
methods on a Filesystem implementation. See starting and mounting.
The FUSE adapter
JjFuse implements six of those methods:
init, lookup, getattr, read, readdir, readlink.
Every one except init turns the inode into a path with
InodeMap, spawns the work onto the tokio
runtime, and replies from inside the task. See the FUSE adapter
and inodes.
The virtual file layer
VirtualFilesystem is the path-based interface
JjFuse calls: attributes, read, read a directory, and read a link. Its one implementation,
PathMappedVfs, asks a PathMapper
for a VirtualFile and calls one method
on it. See the virtual file layer.
The namespace
AllCommitsPathMapper decides what exists: commits/<id>/… and
workspaces/<name>/…, served by three small directory types and
CommitTreeFile, which presents a commit's tree. See
the mount's namespace and
commits as directory trees.
jj-lib
Everything below that is jj-lib 0.43.0. The crate reads the repo's view
(wc_commit_ids), evaluates the all() revset, loads commits from the
Store, and reads trees, files and symlinks through MergedTree and the
Store. It never writes, and never starts a transaction.
Module map
| Module | Owns | Page |
|---|---|---|
fuse | JjFuse, reply_async!, FileAttributes::to_fuse, errno mapping | fuse |
inode_map | InodeMap | inodes |
jj_error | JjError | fuse |
vfs | VirtualFilesystem, PathMappedVfs | vfs-layer |
virtual_file | VirtualFile, FileType, FileAttributes, DirectoryEntry, DirectoryStream | vfs-layer |
path_mapper | PathMapper | vfs-layer |
path_mapper_all_commits | AllCommitsPathMapper | namespace |
static_directory | StaticDirectory | namespace |
commits_directory | CommitsDirectory | namespace |
workspaces_directory | WorkspacesDirectory | namespace |
commit_tree_file | CommitTreeFile | commit-trees |
test_helpers (#[cfg(test)]) | setup_test_repo, user_settings | testing |
These are the twelve pub mods in lib.rs.
The binary, main.rs, uses the library as
jjfsd::….
Dependencies
| Crate | Version | Used for |
|---|---|---|
jj-lib | 0.43.0 | Everything about the repository: workspace and repo loading, the view, revsets, the store, trees. |
fuser | 0.18.0 | The FUSE protocol, the Filesystem trait, mounting. |
tokio | 1.53.1 | The multi-threaded runtime FUSE requests run on, and signal handling. Features: rt-multi-thread, signal, macros, time. |
futures | 0.3.33 | AsyncRead for file content, BoxStream for directory listings, and block_on in CommitsDirectory. |
async-trait | 0.1.91 | The async methods on VirtualFilesystem, VirtualFile and PathMapper. |
thiserror | 2.0.19 | JjError. |
itertools | 0.15.0 | .unique() when merging a conflicted directory's listings. |
ustr | 1.1.0 | Interned file names in InodeMap. |
tracing, tracing-subscriber | 0.1.44, 0.3.23 | Instrumented spans on every operation, and an EnvFilter logger defaulting to INFO. |
anyhow, libc | 1.0.104, 0.2.189 | Declared, but not referenced by any source file at this pin. |
tempfile (dev) | 3 | Throwaway repositories and mountpoints in tests. |
pollster (dev) | 0.4.0 | Declared. Only mentioned in a TODO comment in the integration test. |
Source: Cargo.toml (edition 2024).
Status and limitations
The README says it plainly: this is a proof of concept, "beginning life as an intern project", with "no guarantee of completeness, performance, or API stability". At the pinned commit:
- The mount is read-only, and implements only lookup, attributes, directory listing, reads and symlinks.
- The repository is loaded once. The mount is a snapshot of the operation that was current at mount time.
- Conflicted paths can't be read, and submodules show as unreadable files.
- There are no timestamps, executable bits or real ownership: epoch times,
0644/0755, uid and gid 1000. - Performance shortcuts: sizes and reads at an offset read the file from the start, listings aren't paginated, and inodes are never freed.
Each is sourced on the namespace page's limitations table.
Pages
-
Starting and mounting
What jjfsd does from main() to a live mount: arguments, tracing, mount options, the tokio runtime, loading the repo, and unmounting.
-
The FUSE adapter
JjFuse: every FUSE operation it answers, how each maps onto the virtual file system, the async bridge, attributes and errno mapping.
-
Inodes
How InodeMap hands out inode numbers, maps them to paths and back, and what it never forgets.
-
The virtual file layer
The VirtualFilesystem, VirtualFile and PathMapper traits, PathMappedVfs, and how reads are served from async streams.
-
The mount's namespace
What lives at each path of a mounted repository, how a path is resolved to a file, and which repo snapshot every lookup sees.
-
Commits as directory trees
How a commit's tree becomes directories, files and symlinks, how conflicts and other value kinds are shown, and how content is streamed.
-
Tests and CI
The test helpers, the unit tests per module, the mounted integration test, and the CI workflow.