Gitlet
Introduction
Gitlet was my first “big” solo project and was a Java-based implementation of the Git
Version-Control System (VCS). It was the final project for Data Structures class with a focus on software engineering – designing, testing, and debugging a large solo project (final size ~2300 lines of code).
We had to meticulously plan the design and API for the commit storage, data persistance, data structures, custom error handling, and different algorithms (hashing, serialization, set operations, graph traversal) to complete the functionality. Additionally, there was zero starter code with zero tests provided (aside from hidden autograder integration tests).
I had two weeks to code a custom version of Git
from scratch.
Fully functional command-line tool written in Java that supports the following Git
-equivalent commands (and a few extras not included):
- git init
- git add
- git commit
- git rm
- git log
- git status
- git checkout
- git branch
- git branch -D
- git reset
- git merge
These commands are run sequentially and must recreate normal Git
functionality.
Project Requirements:
Taken from the official spec:
The only structure requirement we’re giving you is that you have a class named gitlet.Main and that it has a main method. Here’s your skeleton code for this project (in package
Gitlet
):
Additionally, there was a small javaUtils.java
file provided for assistance with byte formatting.
The full (70-page) spec can be found here.
Just like regular Git
, the program would be executed serially through the command line and thus had to include functionality for persistance (serialization), hashing, adding files, representing commits, saving project states, saving (and switching between) multiple branches, and finally implementing a custom algorithm for resolving merges between branches with different commit histories.
Solving the Project
I approached the task by creating a detailed design document (in markdown) and updating throughout the project. For fun, I also kept a log of my time spent on each task of the project (linked below).
I took the task incrementally following a test-driven approach, writing unit tests, attempting a first-draft implementation, and then leveraging the IntelliJ debugger to efficiently complete each task.
Additionally, because the project scope was so large, I made sure to keep clean documentation and leaned on my IDE (IntelliJ) for code completion and refactoring.
Source
Gitlet is still used as a project for CS 61B at UC Berkeley so I cannot post my solution publicly (it is saved in a private Github repository. Funnily, Gitlet
can be saved using itself as the VCS). I can, however, share the log file documenting my progression throughout the project. Enjoy!
Progression
Date | Completed Task | Total Lines of Code |
---|---|---|
4/15 | Starter Code | 39 lines |
4/19 | Create Project Skeleton Structure | 321 lines |
4/19 | Init, Repository, and Persistence | 404 lines |
4/19 | Main.add + Commit class implementation | 757 lines |
4/20 | Full Serialization Features | 892 lines |
4/21 | Project 3 Checkpoint (Init, Add, Commit, Log) | 1142 lines |
4/26 | (RM)Branch + Test Scripts | 1292 lines |
4/27 | (Global) Log, Find, Checkout | 1628 lines |
4/28 | Everything up to Merge | 1805 lines |
4/29 | SplitCommit() | 1937 lines |
4/29 | Initial Merge Algorithm | 2141 lines |
4/29 | Merge w/o criss-cross | 2246 lines |
4/29 | Fully Functional Gitlet Program | 2384 lines |
# Gitlet Design Document
author: Noah Sedlik
## (Gitlet) Log ##
-- 0 hours --
* Wed 4/13 -- 1.5 hours (spec + intro videos)
* Thurs 4/14 -- 2 hours (design document lab)
* Fri 4/15 -- 3 hours (design document & illustration)
* Mon 4/18 -- 1 hour (11pm-12pm) (spec re-design and conceptualization)
* Tues 4/19 -- 1 hour (1am-2am) (implement class structure from Design Doc + basic file structure initializations)
* Tues 4/19 -- 1.5 hours (in the morning) (redesign classes and implement boilerplate code)
-- 10 hours --
* Tues 4/19 -- 1 hour (implement Repository persistence)
* Tues 4/19 -- 2 hours (create file staging procedure)
* Tues 4/19 -- 1.5 hours (implement .gitlet/staged and Add.java)
* Weds 4/20 -- 2 hours (12:40am-2:40am) (implement Commit.java fix Add.java bugs)
* Weds 4/20 -- 0.5 hours (11am) (implement Log.java)
* Weds 4/20 -- 2 hour (5pm) (add Commit serialization feature and implement GlobalLog.java and Find.java)
* Thurs 4/21 -- 1 hour (12am) (implement Checkout #1)
-- 20 hours --
* Thurs 4/21 -- 1 hour (1am) (implement Checkout #2 and finish project checkpoint ✨)
* Sun 4/24 -- 1 hour (10:30pm) (implement BranchCommand.java and RMBranch.java)
* Tues 4/26 -- 0.5 hours (add test scripts and testing toggle in Main.java)
* Tues 4/26 -- 1.5 hours (implement RM command)
* Wed 4/27 -- 1.5 hours (implement Status command)
* Wed 4/27 -- 1 hour ((unsuccessfully) debug RM + status)
* Wed 4/27 -- 1 hour (debug RM + status)
* Thurs 4/28 -- 1 hour (10pm) (implement checkoutBranch and RMBranch)
* Thurs 4/28 -- 0.5 hours (11pm) (fix checkoutBranch tracking bug)
* Thurs 4/28 -- 1 hour (12am-1am) (implement reset + fix invalid command args bug)
-- 30 hours --
* Fri 4/29 -- 2 hours (1:30am-3:30am) (first merge-ish)
* Fri 4/29 -- 1 hour (3:30am-4:30am) (first merge acceptance test!)
* Fri 4/29 -- 1 hour (4:30am-5:30) (unsuccessfully attempt to debug merge)
* Fri 4/29 -- 3 hours (start 2:30pm-5:30pm) (implement merge (w/o criss-cross))
* Fri 4/29 -- 1 hour (5:30pm-6:30pm) (implement merge -- finish Gitlet functionality :) )
* Fri 4/29 -- 2 hours (implement status EC and finish Gitlet!)
-- 40 hours --
And the design document continues on…