A Git-inspired version control system written in C.
The project implements the core components of a VCS from scratch including object storage, content hashing, staging, commit creation, reference management, checkout, and repository state tracking.
The implementation is intentionally low level. Every object, commit, reference, and repository file is managed directly through the filesystem without relying on existing version control libraries.
- SHA-256 object storage
- content-addressable objects
- staging area (index)
- commit graph using parent references
- repository initialization
- HEAD and branch reference management
- detached HEAD support
- commit checkout
- object inspection
- repository status
.vcs/
├── objects/
├── refs/
│ └── heads/
├── HEAD
├── index
└── config
Stores every object by its SHA-256 hash.
Objects are immutable. Once written they are never modified.
The staging area.
Stores every file that will be included in the next commit.
Each entry contains
<path> <hash>
Stores branch references.
Each branch contains the hash of its latest commit.
Example
refs/
└── heads/
└── master
contents
91a8...
Tracks the current repository position.
Attached state
ref: refs/heads/master
Detached state
91a8...
Each commit stores
- parent commit
- timestamp
- commit message
- complete snapshot of tracked files
The snapshot consists of
path hash
path hash
path hash
rather than file contents.
The actual file data already exists inside the object store.
doom init
↓
create .vcs
↓
create objects/
↓
create refs/
↓
create HEAD
↓
create index
↓
create config
doom add file.c
↓
read file
↓
compute SHA-256
↓
write object
↓
update index
doom commit
↓
read HEAD
↓
resolve current parent
↓
read staged files
↓
build snapshot
↓
create commit object
↓
store commit
↓
update current branch
↓
clear index
doom checkout <hash>
↓
read commit
↓
extract snapshot
↓
restore every object
↓
detach HEAD
doom init
doom hash-object file.txt
doom hash-object -w file.txt
doom add file.txt
doom commit -m "message"
doom log
doom cat-object <hash>
doom status
doom checkout <commit_hash>The difficult part of the project is not hashing files.
The difficult part is maintaining repository state.
A commit depends on
- the current HEAD state
- the current branch
- the parent commit
- the staged index
- the object database
Changing one component usually affects several others.
For example, creating a commit requires resolving whether HEAD is attached or detached before deciding whether the branch reference or HEAD itself should advance.
Checkout has a similar dependency chain. Restoring the working tree is only one part of the operation. Repository references also need to transition between attached and detached states while keeping commit history consistent.
Although each module is relatively small, the complexity comes from coordinating the interactions between objects, commits, references, and the working tree while preserving repository consistency.