Timelace logo

Rust · content-addressed · git-shaped

See the whole
object model.

Timelace is a tiny content-addressed version control system in Rust, small enough to read in one sitting. Blobs, trees, and commits, each one addressed by the SHA-256 hash of its own bytes. Here is the real object model, running in your browser.

View on GitHub Read the docs
How to use this playground
Build a tiny repository in your browser: add files, commit snapshots, and watch the content-addressed object store fill up.

Working files

Add a file, then add another with identical content and compare blob ids below.

NameContentBlob id

Commit

A commit hashes a tree of the working files above, plus the current HEAD as its parent.

HEAD

(no commits yet)

Commit log

Walked from HEAD by following each commit's parent link. Check out an earlier commit to restore its snapshot into the working files above.

Object store

Every blob, tree, and commit ever created in this session, addressed only by the hash of its own bytes. Identical blob content always lands on the same id, so it is stored once.

real SHA-256 hashing, in your browser, nothing touches a network

Three objects, one idea

Every object's name is a hash of its own content. That single rule is what makes content addressing work, and it is the entire idea Timelace is built to make visible.

Blob

Raw file content

The bytes of one file, nothing else. Two files with identical content share one blob, automatically.

Tree

A directory snapshot

Names mapped to blob or tree ids, sorted before hashing so identical contents always produce the same id.

Commit

A point in history

A tree id, an optional parent, a message, and a timestamp. Follow parent links to walk the whole history.

A five-minute walkthrough

The full workflow, initialize a repository, stage a file, commit it twice, walk the history, and check an earlier snapshot back out. This is real output captured from the CLI.

$ timelace init
initialized empty timelace repository in ./.timelace

$ echo "hello" > notes.txt
$ timelace add notes.txt
$ timelace commit -m "first commit"
committed a3dd0375a9657697e621c10312a6f272b1bb8fdd97dacb8da2f6a4f3bd932e35

$ echo "hello again" > notes.txt
$ timelace add notes.txt
$ timelace commit -m "second commit"
committed a36723d63290713b16b3e4590038a3dfe37a7d76b03f81dcc81aa0769b019d05

$ timelace log
commit a36723d63290713b16b3e4590038a3dfe37a7d76b03f81dcc81aa0769b019d05
timestamp 1788785036

    second commit

commit a3dd0375a9657697e621c10312a6f272b1bb8fdd97dacb8da2f6a4f3bd932e35
timestamp 1788785034

    first commit

$ timelace checkout a3dd0375a9657697e621c10312a6f272b1bb8fdd97dacb8da2f6a4f3bd932e35
$ cat notes.txt
hello

Content addressing, in one command

Because an object's id is the hash of its own bytes, deduplication needs no bookkeeping. Stage two different files with identical content, and the tree records one blob for both.

$ printf 'hello\n' > a.txt
$ printf 'hello\n' > b.txt        # identical content, different name
$ timelace add a.txt
$ timelace add b.txt
$ timelace commit -m "two identical files"
committed c5461d0b23fd0081cc9a6a16be0a76b4ddcb695672165f38cd3f229424a032cd

$ find .timelace/objects -type f | wc -l
3                                     # one commit, one tree, one shared blob

How it differs

Timelace is not a git replacement. It is the same object model git is built on, implemented directly and left readable, with the layers that are not the point stripped away.

Git's object model

Content-addressed blobs, trees, and commits are exactly how git stores history underneath. The idea is small, but the real implementation buries it under packfiles, refs, the index cache, and decades of features.

Timelace

The same content addressing done directly: SHA-256 over a framed object, a two-level object directory, a HEAD pointer, and a staging index. No branches, remotes, merges, or packfiles, so the whole model fits in one file you can read in five minutes.

What you get

init, add, commit, log, status, and checkout, with automatic deduplication and exact working-tree restoration, all covered by an integration suite. Read src/objects.rs and you have read the entire model.

On-disk layout

Objects live in a two-level directory split by the first two characters of their id, the same trick git uses to keep any one directory small.

.timelace/ objects/<id[0..2]>/<id[2..]> one file per object HEAD latest commit id index paths staged for the next commit

Use it

A CLI over the object store, a Rust library underneath it, and an integration suite that pins the behavior down.

CLI

init, add, commit, log, status, and checkout. A file argument to add can be a single path or a whole directory.

Library

The object model in objects.rs and the store in store.rs are a plain Rust library. The only dependencies are sha2 for hashing and clap for argument parsing; the model itself is from scratch.

Tests

cargo test runs 15 tests: initialization, staging and committing, parent-chain linking, log ordering, content deduplication, exact checkout restoration, and the error paths for operating outside a repository, unknown ids, and an empty commit.

$ cargo test
     Running unittests src/lib.rs
running 6 tests
test result: ok. 6 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

     Running tests/integration.rs
running 9 tests
test result: ok. 9 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

Why use it

Not a git replacement. A way to actually see how content-addressed version control works, with no packfiles, no plumbing and porcelain split, no thousands of files standing between you and the idea. Read src/objects.rs and you have read the whole model.

Source, tests, and design notes are public on GitHub. The object model, the on-disk layout, and the commit and checkout algorithms are documented in DESIGN.md.

github.com/pavanchow/timelace