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.

Project Goals

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):

public class Main {
   public static void main(String[] args) {
      // FILL IN
   }
}

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

DateCompleted TaskTotal Lines of Code
4/15Starter Code39 lines
4/19Create Project Skeleton Structure321 lines
4/19Init, Repository, and Persistence404 lines
4/19Main.add + Commit class implementation757 lines
4/20Full Serialization Features892 lines
4/21Project 3 Checkpoint (Init, Add, Commit, Log)1142 lines
4/26(RM)Branch + Test Scripts1292 lines
4/27(Global) Log, Find, Checkout1628 lines
4/28Everything up to Merge1805 lines
4/29SplitCommit()1937 lines
4/29Initial Merge Algorithm2141 lines
4/29Merge w/o criss-cross2246 lines
4/29Fully Functional Gitlet Program2384 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…