Building a Git-compatible version control system in Rust from scratch.
The goal of this project is simple:
- Learn Rust by building something real.
- Understand how Git works internally instead of treating it as a black box.
- Reproduce Git's behavior as closely as possible.
This is not intended to become another Git implementation. It's a learning project.
- Rust (latest stable)
- Cargo
- macOS
git clone https://github.com/Faisal-Manzer/rit.git
cd ritDebug build:
cargo buildRelease build:
cargo build --releaseUsing Cargo:
cargo run -- init
cargo run -- hash-object -w README.md
cargo run -- add .
cargo run -- commit -m "Initial commit"Or run the compiled binary directly:
./target/debug/rit initRelease binary:
./target/release/rit initInstall rit into Cargo's binary directory:
cargo install --path .Now you can run it directly:
rit init
rit add .
rit commit -m "Initial commit"| Item | Details |
|---|---|
| Language | Rust |
| Platform | macOS |
| Goal | Re-implement Git from scratch |
| AI Usage | Minimal (only when completely stuck) |
| Success Criteria | rit <command> should behave identically to git <command> for supported workflows |
| Experience Level | Started with zero Rust knowledge and only a basic understanding of Git internals |
-
init -
hash-object -
cat-file -
add -
commit -
log -
status -
checkout -
branch
-
clone -
push -
pull
Instead of reading Git's source code, I'm implementing features one at a time while learning how Git actually stores data internally.
The project intentionally grows organically. Some implementations may be incorrect initially and get rewritten as my understanding of Git improves.
The objective isn't to finish quickly-it's to understand every piece that makes Git work.
The rough order of implementation is:
- Command parser
- Repository initialization
- Blob objects
- Tree objects
- Index (
git add) - Commits
- References (
HEAD, branches) - Checkout
- Status
- Remote operations
Running rit init creates the following structure:
.git
├── config
├── description
├── HEAD
├── hooks
│ └── README.sample
├── info
│ └── exclude
├── objects
│ ├── info
│ └── pack
└── refs
├── heads
└── tags
- Repository initialization
- SHA-1 object hashing
- Blob objects
- Tree objects
- Git object storage
- Zlib compression/decompression
cat-file.gitignoresupport- Git index (DIRC v2)
- Recursive directory staging
- Commit object generation
- Reading Git configuration (
user.name,user.email)
Current workflow: I use
ritfor every command that has been implemented (init,hash-object,cat-file,add, andcommit). All other Git operations are still performed using the officialgitCLI.I also intentionally avoid using Git commands like
reset,checkout,branch, or any other command that modifies repository state, since RIT doesn't fully understand those changes yet. This ensures the repository remains in a state that RIT can reliably work with while new features are being implemented.
These resources have been incredibly helpful:
-
Write Yourself a Git (WYAG)
https://wyag.thb.lt/ -
ugit: DIY Git in Python
https://www.leshenko.net/p/ugit/ -
The Rust CLI Book
https://rust-cli.github.io/book/tutorial/cli-args.html
One of the rules I set for this project was not to learn Rust before starting.
Instead, I wanted to experience what it feels like to build software in an unfamiliar language by reading documentation, experimenting, failing, and figuring things out as I went.
When I started this project, I knew almost nothing about Rust. I didn't understand ownership, borrowing, lifetimes, traits, binary data, or most of the language itself. Much of the early development was simply trial and error until things eventually clicked.
AI was not used to build the project for me.
Whenever I got stuck, I first spent a good amount of time reading documentation, experimenting with different approaches, and trying to solve the problem myself. Only after exhausting those options did I ask AI for help.
AI helped unblock me in a handful of Rust-specific problems, including:
- Binary serialization
- Lifetime and borrowing issues
- Zlib compression/decompression
.gitignoreimplementation- Correct Git tree sorting
- Reading and writing the Git index format
Everything else was implemented after understanding Git's behavior and experimenting until it worked.
Ironically, by refusing to study Rust first, I ended up learning a surprising amount of Rust naturally while building RIT. I now understand the language far better than when I started, and once this project reaches a good milestone, I plan to go back and formally learn Rust fundamentals.
Note: The implementation is entirely my own. This README was generated with AI after
commitwas working because I'd rather spend my time building Git than writing documentation. AI was used for documentation-not for implementing the project.
RIT intentionally uses as few external dependencies as possible. Wherever practical, I prefer implementing functionality myself to better understand how Git works internally.
Currently, the project depends on only three external crates:
| Crate | Purpose |
|---|---|
sha1 |
SHA-1 hashing used by Git objects |
zlib-rs |
Zlib compression and decompression for Git object storage |
hex |
Hex encoding and decoding |
Everything else—including object storage, repository layout, index parsing, tree generation, commit creation, .gitignore handling, and Git object serialization—is implemented from scratch.
This project is a learning exercise.
The implementation is expected to evolve significantly as I gain a deeper understanding of both Rust and Git internals. Bugs, incorrect behavior, and rewrites are all part of the journey.
MIT