I'm building a small Git-like version-control system in Java as a learning and portfolio project. I initially called it reverse engineering, but reimplementation is more accurate: I studied how Git's object database behaves and am recreating the core concepts myself.
The project, called NanoGit, uses content-addressable storage. Blobs, trees, and commits are identified by SHA-256 hashes and compressed with Java's Deflater and Inflater classes. The index is currently stored using a custom plain-text format. I chose SHA-256 instead of Git's traditional SHA-1 mainly as an experiment, not because it provides a specific benefit for this project.
Version one is intended to work offline. I may add fetch, pull, and push functionality later. The commands currently implemented are init, add, commit, status, log, help, branch, and checkout. I'm working on diff and merge next.
For diff generation, I'm considering either the longest common subsequence approach or Myers' algorithm. Myers seems to be the more appropriate choice, but I don't understand it well enough yet, so I may start with LCS and improve it later.
I've used language models for planning and for investigating bugs, but not to write the implementation. I'd appreciate feedback on the project structure, design decisions, Java code, and what would make this a stronger learning or portfolio project.
4 Answers
A strong next step may be contributing to an existing project instead of only adding more features here. Reading an unfamiliar codebase, fixing a bug, writing tests, improving documentation, and following established conventions are skills employers use constantly. Your project can still be a good learning exercise, but reviewing mature code and making a small accepted change would complement it well.
For this project specifically, automated tests and clear documentation may be more valuable than rushing into networking. Test object hashing, compression round trips, index parsing, checkout safety, branch references, and recovery from interrupted operations. Those details will show that the implementation is reliable rather than just command-shaped.
For diffing, starting with LCS is a perfectly reasonable way to get a complete feature working. Myers is commonly used because it finds a shortest edit script efficiently, but the implementation and explanation are less approachable at first. Build a simple version, add tests for insertions, deletions, replacements, empty files, and large repeated sections, then replace the algorithm later if needed.
Merge logic will probably teach you more than the diff algorithm. Before tackling it, define how you represent parents, branches, conflicts, and the working tree. Make sure commits can have multiple parents and that conflict handling is explicit rather than silently choosing one side.
Calling this a reimplementation or clone is more accurate than calling it reverse engineering. Reverse engineering usually means recovering how an existing system works from its behavior or binaries, while you’re studying Git’s behavior and rebuilding similar functionality. Either way, it can be a worthwhile project—just describe it precisely on a résumé.
The content-addressable object store is a good foundation. One small terminology point: a plain-text index is still a data format, even if it’s an informal CSV-like one. That’s fine for an early version, but document its grammar and think about escaping, malformed records, paths containing spaces, and compatibility if you evolve it later.
Also, ByteArrayOutputStream does not need to be explicitly closed. It does no meaningful resource cleanup, unlike a file or network stream. The tokenizer is reasonable for handling a command such as `commit -m "a message with spaces"`, although you’ll eventually need to decide how to handle escaped quotes, unmatched quotes, repeated whitespace, and empty quoted arguments.
The tokenizer is mainly for commit messages. Most commands are simple, but `commit -m "a message with spaces"` needs the message to remain one argument. I’ll also look more carefully at quoting and malformed input.
The project sounds useful, but I’d avoid presenting it as if you independently discovered Git’s design. Git is extensively documented, so “implemented a Git-like version-control system in Java” communicates the work without overstating it. Using SHA-256 is fine as an experiment, but be clear that it won’t be compatible with normal Git repositories unless you deliberately match Git’s object formats, hashing rules, and algorithms.
It may also be worth studying JGit and comparing its architecture with yours. Don’t copy it, but seeing how a mature Java implementation separates storage, objects, references, indexing, and commands can reveal design issues that are difficult to notice in a small project.
That makes sense. I’ll describe it as a Git-like reimplementation and use JGit as a reference for architecture and design comparisons.

That’s helpful advice. I’ll spend more time reading existing codebases and focus on tests and documentation instead of only chasing new commands.