MiniGit is a simplified, local, single-user version-control system built to explore and demonstrate the core mechanics behind Git-style version control. It focuses on content-addressed storage, immutable objects, directory snapshots, and linear commit history, without aiming for feature completeness or compatibility with Git.
minigit init: initialize a repository at.minigit/minigit hash-object <file> [--write]: compute a blob object ID; optionally store itminigit cat-file <oid>: print an object’s type and raw contentsminigit commit -m <message>: snapshot the working directory and create a commit (Linear commit history (single parent per commit))minigit log: traverse and print commit history fromHEADminigit checkout <commit_oid>: restore a previous snapshot and move the current ref (MVP semantics)
These are deliberate scope decisions for the MVP:
- No staging area / index (
add,status) - No branching, merging, rebasing, or conflict resolution
- No remotes or networking (push/pull/fetch)
- No packfiles, delta compression, or performance optimizations
- No interoperability with real Git object formats
- No concurrency guarantees or multi-user support
MiniGit uses a Git-inspired object model stored under .minigit/objects/:
- Blob: raw file bytes.
- Tree: a directory snapshot mapping names to objects (files, subtrees, symlinks).
- Commit: points to a root tree, an optional parent commit (single parent only), and includes metadata (timestamp + message).
Objects are content-addressed: the object ID (OID) is a SHA-1 hash of the object’s bytes. This makes stored objects effectively immutable—writing the same content yields the same OID, and existing objects are not overwritten.
.minigit/HEADpoints to a ref underrefs/(e.g.,refs/heads/main).- The ref file stores the current commit OID.
- History is linear: each commit has at most one parent.
- Checkout moves the current branch ref to the target commit; detached HEAD is intentionally not supported in the MVP.
Checkout is implemented as “restore an exact snapshot”:
- Restores the working directory to exactly match the target commit’s root tree.
- Overwrites existing files and directories as needed.
- Deletes files/directories that are present in the working directory but not in the snapshot.
- Always preserves
.minigit/.
Symlinks:
- Snapshotting does not follow symlinks; it stores the symlink itself.
- Restore creates a real symlink when permitted; otherwise checkout fails with a clear error (no silent fallback).
- Never writes outside the repository root (guards against path traversal / escape attempts).
- Ignores
.minigit/during snapshotting and restoration (except ref updates). - Deterministic trees: directory snapshots are encoded in a stable order so the same working tree state yields the same tree OID.
- Graceful, user-facing errors for missing objects, invalid OIDs, and detectable corruption (no crashes).
- No staging area: reduces surface area and keeps the data model focused on snapshots.
- Linear history only: commit traversal is straightforward and predictable.
- Destructive checkout is allowed: the MVP overwrites local changes to keep semantics simple (MiniGit prints a warning).
- Correctness over performance: objects are stored individually; there are no packs, deltas, or caching.
- Python 3.10+ (recommended: latest stable)
From the repository root:
python -m minigit --help
python -m minigit initIf installed as a script entry point (see pyproject.toml):
minigit --helpuv run --with pytest pytest -q
# or, if pytest is already installed:
python -m pytest -q- Modeling system behavior via immutable data structures and explicit state transitions (refs vs objects).
- Designing a small system around explicit invariants (object immutability, path safety, deterministic snapshots).
- Filesystem traversal and cross-platform edge cases (byte-for-byte correctness, symlinks, path handling).
- Building a spec-driven MVP with thin slices and tests to keep scope under control.