The anatomy of version control.
A complete reimplementation of git in C - the object store, the index, the merge algorithm, and the wire protocol - plus four commands git doesn't have.
One external dependency: zlib. SHA-1, the binary index format, packfile parsing, delta reconstruction, the three-way merge, and the HTTP and SSH transports are all implemented from scratch.
Six phases, built bottom-up, in this order for a reason: nothing in a later phase could have been written before the one under it worked.
Phase 1 is the whole foundation - a file becomes a blob, a blob becomes a tree, a tree becomes a commit, and every one of those is a real, inspectable file format, not an abstraction over one:
Phases 2 through 5 are git, rebuilt from that foundation up to a real network protocol: the staging index, commit history, branching and merging, and finally the pack protocol over HTTP and SSH - the phase most student git reimplementations don't reach.
Phase 6 is what came after the reimplementation was actually finished: four commands that don't exist in git at all, built by recombining what the first five phases had already made solid.
It is actually git-compatible. Not "inspired by git" - compatible, and the test suite proves it rather than claiming it:
- Objects are byte-identical to git's. Same SHA-1 over the same
"<type> <size>\0<content>"framing, same zlib compression, sameobjects/xx/xxxxxxxx…layout. Tests cross-check against realgit cat-file,git write-tree, and git's known empty-blob hash. - The networking talks to real git.
clone,fetch,pull,push, andls-remoteare tested against actualgit http-backend- genuine git server code, not a mock. Void speaks the real pack protocol, parses real packfiles, and reconstructs real deltas.
You can clone a GitHub repository with Void and commit to it with git.
And it goes past reimplementing. void undo, void fixup, void audit,
and void checkpoint are four commands with no git equivalent, all resting
on one invariant established in Phase 1: void never destroys an object.
Reverse the last operation that moved a ref - commit, merge, reset, branch delete, tag delete, rebase, fixup. Git has the reflog; this is one command.
void reset --hard HEAD~3 # oh no
void undo # fixedEvery operation that moves a ref writes a journal entry recording what moved, where it was, and how much undoing it is allowed to touch. Nothing is ever deleted from the object store, so undo always has something to point back at.
Fold what's staged into an earlier commit, as if it had always been there.
--amend only reaches the last commit; this reaches any of them.
void add oauth_client.c
void fixup a1b2c3d # the commit where the bug actually isEvery commit after the target is rebuilt through a three-way merge. Messages, authors and dates are preserved exactly - a rewrite must never put your name on someone else's commit.
Scan the whole history - every commit reachable from every branch and tag - for committed secrets.
$ void audit
AWS access key id
config.ini:1
in commit a1b2c3d
AKIA...(20 characters)
Deleting a secret in a later commit does not remove it - the
blob is still in the object store and in every clone. Rotate
the credential.Detects private keys, AWS/GitHub/Google/Stripe/Slack/npm/Shopify/SendGrid
token formats, JWTs, URL-embedded credentials, and hardcoded assignments.
Never prints a secret in full - an audit tool that dumps credentials into
your CI logs has moved them somewhere worse. Exits nonzero, so
void audit && void push works as a gate.
Snapshot the working tree - untracked files included - without touching your branch, index, or staged changes.
*/5 * * * * cd /path/to/repo && void checkpoint # cron, not a daemonEverything else in Void protects work you've already committed. This protects
the hour of editing you haven't. A snapshot identical to the previous one
isn't taken at all, so idle time costs nothing; the newest 50 are kept.
void checkpoint restore snapshots your current state first, so it can't lose
anything either.
git clone https://github.com/ITAXBOX/Void.git
cd Void
make
./void init
./void add .
./void commit -m "first commit"
./void log --onelineRequires: a C compiler, make, and zlib. POSIX (Linux, macOS, WSL).
Repository - init, clone, status, help
Working tree - add, rm, diff (LCS-based unified diff, binary
detection), .voidignore
History - commit (-m, -a, --amend, --no-edit), log
(--oneline, -n), show, reset (--soft, --mixed, --hard),
undo, fixup, audit, checkpoint
Branching - branch (-d, -D, -m, --list), switch / checkout
(-c, -b), merge (fast-forward + three-way, conflict markers,
--abort), rebase (--continue, --abort), stash, tag
Remotes - remote, fetch, pull, push (--force), ls-remote,
over HTTP and SSH
Plumbing - hash-object, cat-file, ls-tree, write-tree,
commit-tree, update-index, ls-files, pack-objects, unpack-objects
Every command above is documented in full on the documentation site as its own implementation card - the problem it solved, the files it touched, and the tests that prove it:
make tests63 suites, 2,251 assertions, all passing, checked under valgrind:
| 43 CLI suites | one per command, black-box |
| 15 unit suites | SHA-1, packfiles, delta decoding, the index format, pkt-line framing, HTTP, transport, the journal |
| 5 e2e journeys | one per phase - multi-command sequences asserting HEAD, the index and the working tree agree after every transition |
The e2e journeys exist to catch what per-command tests structurally cannot:
drift that only becomes visible several operations later. The Phase 6 journey
drives fixup → rebase → fixup on the rebase's own output → a conflict →
checkpoint mid-conflict → --abort → four undos, then asserts that every
commit id it ever saw is still readable.
src/
core/ objects, sha1, compression, index, trees, commits, refs,
packfiles, delta, http, ssh, transport, diff, journal
commands/ one file per command
utils/ files, repo detection, error handling, colour
include/ one shared header
tests/ cli/ · unit/ · e2e/
A few decisions worth knowing:
- Nothing is ever deleted from the object store. "Deleting" a branch
removes a name. This single invariant is what makes
undo,fixup,rebaseandcheckpointsafe - and it's asserted end-to-end, not assumed. - Concurrent processes are handled. The index, refs, the journal and the config are each protected by lock files; the test suite launches real concurrent processes rather than simulating them.
- Errors return, they don't exit. Callers decide whether to recover.
Two reasons, and neither one was "the world needs another git."
Git is a tool I've used every day for years and always found genuinely useful and creative - not just infrastructure to get out of the way, but a piece of engineering worth understanding on its own terms: content-addressed storage, a merge algorithm that mostly gets out of your way, a wire protocol that turns a whole repository's history into a stream of deltas. Building it myself, from the object model up to the pack protocol, was the only way to actually see how those pieces fit rather than take them on faith.
The other reason is C. This project exists to master it at depth - binary file formats, network protocols, compression, cryptographic hashing, graph algorithms, memory management at scale - before moving into reverse engineering. Void is deliberately not an exercise: it is a real tool, git-compatible and tested against git itself, because the depth that transfers to RE work only shows up when the thing you built actually has to be correct.
MIT - see LICENSE.




