Skip to content
a versioning filesystem inspired by git – toy project
C C++ Shell
Find file
Failed to load latest commit information.
block-sha1 block-sha1: Import SHA1 library from git.git
t test-sha1: fix segfault
xdiff Add xdiff library
.gitignore Change project name to phoenixfs
LICENSE LICENSE: Use a 2-clause BSD license
Makefile Makefile; add test target pointing to t README: retire project; it is a toy
btree.c btree: resurrect print_btree()
btree.h btree: change BTREE_ORDER to 3
buffer.c buffer: Write small library for buffered IO
buffer.h Makefile: Scrub both Makefiles clean
common.c Implement subcommand `diff` and refactor heavily
common.h Change project name to phoenixfs
compress.c Makefile: Scrub both Makefiles clean
compress.h compress: Write a test-compress and deflate old revisions
crc32.c crc32: Import from Linux source tree
crc32.h crc32: Import from Linux source tree
delta.c delta: Import deltification algorithm from git.git
delta.h delta: Import deltification algorithm from git.git
diff.c Squelch some compiler warnings
diff.h Update remaining references to "gitfs"
fstree.c btree: resurrect print_btree()
fstree.h fstree: re-create frs on startup
fuse.c fuse: thread-lock read, write, and release
fuse.h Update remaining references to "gitfs"
loose.c loose: zero-length pointer arrays are illegal
loose.h loose: zero-length pointer arrays are illegal
main.c Squelch some compiler warnings
main.h Change project name to phoenixfs
pack.c loose: zero-length pointer arrays are illegal
pack.h Squelch some compiler warnings
persist.c persist: destroy B+ tree after dumping
persist.h persist: Introduce a persistence layer above fstree
sha1.c sha1: rewrite print_sha1
sha1.h Squelch some compiler warnings


A filesystem implemented in userspace (using FUSE), inspired by the way Git is designed. This is a toy - expect it to be buggy.


  1. Zlib (>= 1.2)
  2. FUSE (>= 2.6)
  3. pkg-config (>= 0.25)
  4. Linux kernel (>= 2.6.15)


For the first run, you need two directories: 1. A git directory where the data will be stored. 2. An empty directory to use as the mountpoint.

$ cd /tmp
$ mkdir gitdir mountp
$ phoneixfs mount gitdir mountp

Use the mountpoint as you see fit. Data will be written to the gitdir on umount: you can use it for subsequent mounts.

Now, everything in mountpoint is versioned. To access older revisions of FILE, use FILE@REV syntax, where REV is the number of revisions in the past you want to access. For example:

 $ echo "hello" >file1
 $ echo "goodbye" >file1
 $ echo "another hello" >file1
 $ cat file1
 another hello
 $ cat file1@1
 $ cat file1@2

Finally, to umount:

 $ fusermount -u mountp

Technical documentation

Uses a B+ tree to keep track of the filesystem tree, and a modified version of packfile v3/ packfile index v2 for storing revision information.

  • gitdir/.git/loose/ contains zlib-deflated versions of content blobs, named by the SHA-1 digest of the content.

  • gitdir/fstree is a raw dump of the B+ tree in a custom format.

  • gitdir/master.pack and gitdir/master.idx are the packfile and packfile index respectively. During an unmount, the files in gitdir/.git/loose/ are packed up, and an index is generated.

  • /tmp/phoenixfs.log is the debug log

master.idx, master.pack, and fstree are enough to recreate the entire versioned filesystem. The files in gitdir/ and gitdir/.git/loose/ can be removed after unmount.

Notes on the filesystem tree:

(dr: directory record | fr: file record | vfr: versioned file record)

  • dr just contains a name and node pointer referencing vfrs. drs are inserted directly into the root node.

  • vfr contains the path of the file, a list of frs representing the various versions of the file (fixed at REV_TRUNCATE), and a HEAD pointer to keep track of the latest version of the file.

  • The B+ tree is keyed by the CRC32 hash of the path of the vfr/ dr, a design decision inspired by Btrfs.

  • An fr, vfr, and dr (corresponding to its path) are created when a new file is created on the filesystem. Empty directories are not tracked: no dr is created for empty directories.


The following invocations don't work:

 $ cp file1@1 file1 # FILE@REV can't be treated like a file


Simply fork the project on GitHub and send pull requests.

Something went wrong with that request. Please try again.