• Structural Information

  • Initialize: init

  • Add: add [file name]

  • Commit: commit [message]

  • Remove: rm [file name]

  • Log: log

  • Find: find [commit message]

  • Status: status

  • Checkout:

    checkout [file name]
    checkout [commit id] [file name]
    checkout [branch name]

  • Branch: branch [branch name]

  • Remove: rm-branch [branch name]

  • Reset: reset [commit id]

  • Merge: merge [branch name]

  • Rebase: rebase [branch name]

  • Interactive Rebase: i-rebase [branch name]

  • Input & Output

  • Data Structure and Approach:

  • Hidden Folder Structure: .Gitlet

    All information not in the running directory is stored in a .gitlet folder that is hidden by default (starts with “.”)

  • “Running Directory”:

    All files not in the .gitlet folder are in your “running directory”

  • Error Messages and Printing

    Some commands have special error messages. Only print out these exact error messages. Do not print out anything that the spec doesn’t say you should print out.

  • Description:

    Creates a new gitlet version control system in the current directory. This system will automatically start with one commit: a commit that contains no files and has the commit message initial commit.

  • Runtime:

    constant

  • Special Cases:

    In case there already exists a system, print error message:

    “A gitlet version control system already exists in the current directory.”

  • Description:

    Indicates you want the file to be included in the upcoming commit as having been changed. Adding a file is also called staging the file. If the file had been marked for removal, instead just unmark it.

  • Runtime:

    Linear in size of added file

  • Failure cases:

    If the file does not exist, print the error message File does not exist. If the file has not been modified since the last commit, aborts and prints the error message File has not been modified since the last commit.

  • Differences from real git:

    In Gitlet, if a file is added, then modified, then committed, the contents of the file at the time of the commit command are recorded (not the contents at the time of add). By contrast, in Git, if a file is added, then modified, then committed, the contents of the file at the time of the add command are recorded (not the contents at the time of commit). In short, real Git creates snapshots for every add, whereas in Gitlet, this does not occur.

  • Description:

    Saves a snapshot of certain files that can be viewed or restored at a later time. The files in a commit’s snapshot come from two sources: files that were newly added to this commit (staged prior to the commit), and files that were inherited from the previous commit. We’ll refer to these two groups of files as “the commit’s added files” and “the commit’s old files” respectively. In general, a new commit inherits all of the files in the previous commit as its old files (both the previous commit’s added and old files). However, don’t inherit files that were added to the new commit, becuase the added file takes precedent over the old one. Remember that adding a file indicates you want to save a new version of the file, so if you added the file it means that you don’t need the old version anymore.

  • Runtime:

  • Memory:

  • Failure cases:

    • If no files have been staged (or marked for removal: more on that next), aborts. Print the error message “No changes added to the commit”.
    • Also, the commit must have a non-blank message.
    • If it doesn’t, print the error message “Please enter a commit message”.
  • Differences from real git:

    In real git, commits are not associated with any old id number, but a special kind of hash code. Using a hash is actually more powerful than using an arbitrary id number; can you think of why? Also, snapshots in real git are taken when files are added, not committed.

  • Description:

    Mark the file for removal; this means it will not be inherited as an old file in the next commit. If the file had been staged, instead unstage it.

  • Runtime:

    Constant

  • Failure cases:

    If the file is neither added nor included in the previous commit, print the error message “No reason to remove the file”.

  • Differences from real git:

    Be aware the the real git rm command works differently; it will actually delete the file! This command is more similar to git rm —cached [file name].

  • Description:

    Starting at the current head pointer, display information about each commit backwards along the commit tree until the initial commit. This set of commit nodes is called the commit’s history. For every node in this history, the information it should display is the commit id, the time the commit was made, and the commit message.

  • Runtime:

    Should be linear with respect to the number of nodes in head’s history.

  • Failure cases: None

  • Structural Clarification:

  • Description:

    Prints out the id of the commit that has the given commit message. If there are multiple such commits, it prints the ids out on separate lines.

  • Runtime:

    Should be linear relative to the number of commits that have the given message.

  • Failure cases:

    If no such commit exists, prints the error message Found no commit with that message.

  • Differences from real git:

    Doesn’t exist in real git. Similar effects can be achieved by grepping the output of log.

  • Description:

    Displays what branches currently exist, and marks the current branch with a *. Also displays what files have been staged or marked for removal. An example of the exact format it should follow is as follows.

    === Branches ===
    *master
    other-branch

    === Staged Files ===
    wug.txt
    some_folder/wugs.txt

    === Files Marked for Removal ===
    goodbye.txt

    Notice there is an empty line between each section. The order of branches/files within each section does not matter.

  • Runtime: Linear relative to…

    • Number of files stages
    • Number of files marked for removal
    • Number of currently open branches
  • Failure cases: None

  • Description: [file name]

    Restores the given file in the working directory to its state at the commit at the head of the current branch.

  • Description: [commit id] [file name]

    Restores the given file in the working directory to its state at the given commit.

  • Description: [branch name]

    Restores all files in the working directory to their versions in the commit at the head of the given branch. Considers the given branch to now be the current branch.

  • Comment:

    When File Name is the same as the name of an existing branch, then the branch name takes precedence.

  • Dangerous

    Make sure to ask the user before overwriting any files

  • Description:

    Creates a new branch with the given name. A branch is nothing more than the name of a head pointer in the commit graph. Before you ever call branch, your code should be running with a default branch called “master”. Note: Does NOT immediately switch to the newly created branch.

  • Runtime: Constant

  • Failure cases:

    If a branch with the given name already exists, print the error message A branch with that name already exists.

  • Description:

    Deletes the branch with the given name. This only means to delete the head pointer associated with the branch; it does not mean to delete all commits that were created under the branch, or anything like that.

  • Runtime: Constant

  • Failure cases:

    If a branch with the given name does not exist, aborts. Print the error message A branch with that name does not exist. If you try to remove the branch you’re currently on, aborts, printing the error message Cannot remove the current branch.

  • Descriptions:

    Restores all files to their versions in the commit with the given id. Also moves the current branch’s head to that commit node.

  • Runtime:

  • Failure cases:

    If no commit with the given id exists, print No commit with that id exists.

  • Dangerous

    Make sure to ask the user for permission before executing.

  • Differences from real git:

    Just note that this command is closest to using the —hard option, i.e. git reset —hard [commit hash].

  • Description:

    Merges files from the head of the given branch into the head of the current branch.

  • Runtime:

  • Failure cases:

    If a branch with the given name does not exist, print the error message A branch with that name does not exist. If attempting to merge a branch with itself, print the error message Cannot merge a branch with itself.

  • Dangerous:

    Also, take note that although this method modifies files, it should NOT modify the commit tree. In order to save changes into the commit tree, they must be added and committed like normal. There is no special case for this commit; it’s just a normal commit.

  • Differences from real git:

    There are a few. For one, if the real git doesn’t run into any merge conflicts, it will automatically create a new commit for you at the end, but gitlet does not. In git, this new commit at the end of merge is special, because it maintains two back pointers remembering which two branches it came from. But gitlet only maintains one normal back pointer on the current branch.

  • Description:

    Conceptually, what rebase does is find the split point of the current branch and the given branch, then snaps off the current branch at this point, then reattaches the current branch to the head of the given branch.

  • Runtime:

    Should be linear relative to the history of the current brach and the given branch. Should also be linear in terms of the number of files added to both branches. Should also be linear relative to the total size of files added to the given branch. Also, be aware that rebase should not need to make any additional backup copies of files.

  • Failure cases:

    • If a branch with the given name does not exist, print the error message A branch with that name does not exist.
    • If the given branch name is the same as the current branch name, print the error message Cannot rebase a branch onto itself.
    • If the input branch’s head is in the history of the current branch’s head, print the error message Already up-to-date.
  • Dangerous:

    Make sure to ask the user before you execute this command

  • Differences from the real git:

    The real git’s rebase is a complicated and many-flagged command. Gitlet’s rebase really only gets at the core idea. In particular, note that the way it handles conflicts is much different! For instance, the real rebase will pause when it encounters a conflict, make the user fix it, and then continue on afterward.

  • Description:

    This does essentially what rebase does, but with one twist. For each node it replays, it allows the user to change the commit’s message or skip replaying the commit. This means the command needs to pause and prompt the user for text input before continuing with each commit.

  • Runtime: Same as rebase [branch name]

  • Failure cases: Same as rebase [branch name]

  • Dangerous:

    Make sure to ask the user before you execute this command

  • Differences from the real git:

    This command is inspired by the -i flag for git’s rebase, i.e. git rebase -i [branch name]. Git’s interactive rebase is much more fully featured than gitlet’s!

  • Class: java.io.File

    The File class is used to get access to the underlying file system, and allows me to specify the location of files, and create directory structures.

    It does not provide methods to edit files

  • Class: java.io.Serializable

    The Serializable interface, to which files belong to, can be used to transform objects into files, and the other way around. This allows me to preserve state across different instances of my program.

  • Thoughts and Personal Comments:

  • Necessary Data Structures

  • Class: Commit

    I will need a commit class which captures the information of a single commit, and that implements my linked list Data Structure

  • Old Files are stored in .gitlet folder

  • If the stored backup files are manually deleted by the user, and the user tries to restore them, then ignore the missing file and do not overwrite the current files in the working directory.

  • No Gitlet command should ever delete files in this folder!

  • Any command that edits files in the working directory is classified as dangerous

    In that case: Gitlet must provide a prompt with the message:

    “Warning: The command you entered may alter the files in your working directory. Uncommitted changes may be lost. Are you sure you want to continue? (yes/no)”

  • Cards in the “Running Directory” are only every deleted when the user has been prompted with the above warning.

  • Personal Comment:

    I will need a file in which I keep track of all of the stages files. This file is emptied with each commit, and the files linked in it are added to the Data Structure in .gitlet.

  • Personal Comment:

    I also need a file that keeps track of files that have been marked for removal. It seems to be sane to keep this in a single file, allowing different flags (need to make sure I make IO on this simple)

  • Personal Comment:

    I need a flag that checks files whether they have been modified. This seems to be best done by looking at the “edited” date that the filesystem keeps track of, and comparing it to the time of the last commit. This needs to be checked when we run this command.

  • Field: Unique integer id number

  • Field: Date when commit was made

  • Field: Message left by the user.

    Note: Only one entry in the args[] array, surround by quotes if you want longer

  • Algorithm: This command moves the head pointer to point to the new node.

  • Algorithm: The commit is added as a new node in the commit tree.

  • Picture:

  • Constant: Number of Commits

  • Linear: Size of files in snapshot

  • Linear: Number of files in last commit

  • Memory Requirement:

    Size increase of .gitlet folder is smaller than total size of new commit’s files + Metadata

  • Comment:

    Don’t store redundant copies of files that you already have and that haven’t changed

  • Comment:

    You are allowed to save whole additional copies of files; don’t worry about only saving diffs, or anything like that.

  • Example:

    ====
    Commit 2.
    2015-03-14 11:59:26
    A commit message.

    ====
    Commit 1.
    2015-03-14 11:49:29
    Another commit message.

    ====
    Commit 0.
    2015-03-14 11:39:26
    initial commit

  • Information Displayed:

    • Commit ID
    • Time of the Commit
    • Commit Message
  • Formatting Comments:

    • “====” separating each commit
    • empty line between each commit
    • most recent commit is at the top
    • Use Java Standard Library for dates!
  • Example:

  • Comment:

    The branch pointer is pointing at the commit to the far right lower corner. In this case the circled commits would be printed.

  • Comment:

    The history of a commit with a particular ID can never change. That history is immutable.

  • Personal Comment:

    This means I will have to create a HashMap from commit messages to commits.

  • Field: HashMap< String, Commit >

    HashMap mapping commit messages to commit for runtime reasons

  • Output: List of existing branches (endpoints in the commit tree)

  • Output: List of staged files (files added with add)

  • Output: List of to be removed files

  • Field: Collection< Commits >

    Collection of pointers to the current branch endpoints

  • File: rem_marked_files.csv

    list of all files currently marked for removal

  • File: staged_files.csv

    list of all currently staged (added but not commited) files.

  • Runtime: Linear to size of file in [file name]

  • Failure Cases:

    If the file does not exist in the previous commit, aborts, printing the error message “File does not exist in the most recent commit, or no such branch exists”.

  • Runtime: Linear to size of file in [file name]

  • Failure Cases:

    If no commit with the given id exists, print “No commit with that id exists”. Else, if the file does not exist in the given commit, print “File does not exist in that commit”.

  • Runtime:

    • Linear to size of of files in commit’s snapshot
    • Constant to number of commits
    • Constant to number of branches
  • Failure Cases:

    • If no branch with that name exists, print “File does not exist in the most recent commit, or no such branch exists”.
    • If that branch is the current branch, print “No need to checkout the current branch”.
  • Example:

    Initial State:

  • Call: java Gitlet branch cool-beans:

  • Call: java Gitlet checkout cool-beans

    Switch to branch “cool-beans”

  • Call: java Gitlet add... + java Gitlet commit...

    Changed files and commited stuff

  • Call: java Gitlet checkout master

    Switch back to original branch

  • Call: java Gitlet add... + java Gitlet commit...

    Commit on the original branch

  • Comment:

    • All that branch does is create a new head pointer.
    • There is only one active head-pointer at any time (indicated by *)
    • checkout switches the current active head-pointer
    • commit creates a new commit in front of the currently active head pointer, even if one is already here.
  • Linear:

    Total size of files in the commit’s snapshot

  • Constant:

    Number of commits

  • Example:

    Consider the split point of the current branch and the given branch:

  • Comment:

    Any files that have been modified in the given branch (added to any of the commits along the branch, and not subsequently removed) but not in the current branch since the split point should be changed to their versions in the given branch.

  • Comment:

    Any files that have been modified in the current branch (added and not subsequently removed) but not in the given branch since the split point should stay as they are.

  • Comment:

    For files that have been modified in both branches since the split point (added but not subsequently removed), the files should stay as they are in the current branch. However, in addition, the version of the file from the given branch should be copied into the file system with the name [old file name].conflicted.

  • Linear:

    Length of History of the current commit

    Linear:

    Total size of files in the two commit branches

  • Additional Comment:

    Furthermore, the real git handles merge conflicts differently than gitlet. The real git will splice the two conflicted files together into a single file, then ask the user to pick and choose the correct sections manually. Gitlet does not do this, instead just adding in the .conflicted copy. Furthermore, git will put you in a special state where the commands you can run are limited until you finish resolving the merge conflict. Gitlet does no such thing.

  • Example:

    Say we are on branch branch and we make the call java Gitlet rebase master:

  • Comment: Why would you ever want to do this?

    You can think of it as an alternative to merge, where instead of having two branches that come together, you just pretend as if one of the branches came after the other one. If you use it smartly, this can create a cleaner history than merge.

  • Comment: Doesn’t this ruin what you said about the commit tree being immutable?

    Yes, it does! That’s because I just lied to you in the picture above. In fact, rebase does not break off the current branch. Instead, it leaves the current branch there, but makes a copy of the current branch on top of the given branch (this is called replaying the branch). Then it moves the branch pointer to point to this copy, so that you can pretend you moved it.

  • Real Picture:

  • Note:

    Note: the replayed commits should have new ids, not copies of the original ids. This allows you to still access the original commits using their old ids, if you really wanted to. In addition, the replayed commits should have new time stamps, allowing you to distinguish them from the originals in global-log.

  • Comment:

    There’s one more point to make about rebase: If after the split point the given branch contains modifications to files that were not modified in the current branch, then these modifications should propagate through the replayed branch. If both the given branch and the current branch have modifications to the same files, then what you would expect to happen is that you would get conflicted files, much like merge. However, for simplicity, we’re not going to have you deal with conflicts: in this case, just use the current branch’s copies of the files.

  • Comment:

    Finally, after any successful rebase command, update the files in the working directory to reflect the versions of the files at the new head of the current branch.

  • Comment:

    By the way, if there are multiple branches after the split point, you should NOT replay the other branches. For example, say we are on branch branch1 and we make the call java Gitlet rebase master:

  • Sequence of Operation:

    For each commit, the command should print out the following:

    Currently replaying:

  • Then it should print out information about the commit like log does (just the one commit, not all of its history).

  • Finally it should prompt the user with the message

    Would you like to (c)ontinue, (s)kip this commit, or change this commit's (m)essage?

    It should act based on whether the user types c, s, or m. If the user types something different, asks the user for input again.

  • If the user choose to continue, then it replays the commit and continues on to ask about the next one.

  • If the user chooses to skip, then it does not replay the commit, but continues on to ask about the next one.

  • Note:

    skipping a commit does not mean you forget its changes. Rather, the changes made in a skipped commit are incorporated into or overwritten by the next unskipped commit. You cannot skip the initial or final commit of a branch. If a user does this, ask them for input again.

  • If the user chooses to change the commit’s message, then prompt them with

    Please enter a new message for this commit.

    and wait for them to enter a message. Include this as the message of the replayed commit.

  • Comment:

    By the way, if you didn’t notice, the point of interactive rebase is that it gives you the power to rewrite history. Interactive rebase essentially allows you to pretend to modify the commit tree, fixing typos in commits or getting rid of useless ones.

  • Personal Comment:

    I should probably just implement rebase ... and i-rebase ... with the same command but different parameters. They basically do the same thing, and the interactive part if i-rebase ... can be done with a few conditionals on a flag or something like that.

  • Instantiating a java.io.File

    Before you can do anything with the file system or File class, you must obtain a File instance. Here is how that is done:

    File file = new File("c:\\data\\input-file.txt");

    Simple, right? The File class also has a few other constructors you can use to instantiate File instances in different ways.

  • Check if File Exists

    Once you have instantiated a File object you can check if the corresponding file actually exists already. The File class constructor will not fail if the file does not already exists. You might want to create it now, right?

    To check if the file exists, call the exists() method. Here is a simple example:

    <pre><code>File file = new File(“c:\data\input-file.txt”);
    boolean fileExists = file.exists();
    </pre></code>

  • Create a Directory if it Does Not Exist

    You can use the File class to create directories if they don’t already exists. The File class contains the method mkdir() and mkdirs() for that purpose.

    The mkdir() method creates a single directory if it does not already exist. Here is an example:

    <pre><code> File file = new File(“c:\users\jakobjenkov\newdir”);

    boolean dirCreated = file.mkdir();
    </pre></code>

    Provided that the directory c:\users\jakobjenkov already exists, the above code will create a subdirectory of jakobjenkov named newdir. The mkdir() returns true if the directory was created, and false if not.

    The mkdirs() will create all directories that are missing in the path the File object represents. Here is an example:

    <pre><code>File file = new File(“c:\users\jakobjenkov\newdir”);
    boolean dirCreated = file.mkdirs();</pre></code>

    Provided that the C drive exists, this example will create all the directories in the path c:\users\jakobjenkov\newdir. The mkdirs() method will return true if all the directories were created, and false if not.

  • File Length:

    To read the length of a file in bytes, call the length() method. Here is a simple example:

    <pre><code>File file = new File(“c:\data\input-file.txt”);

    long length = file.length();
    </pre></code>

    Rename or Move File:

    To rename (or move) a file, call the method renameTo() on the File class. Here is a simple example:

    <pre><code>File file = new File(“c:\data\input-file.txt”);

    boolean success = file.renameTo(new File(“c:\data\new-file.txt”));
    </pre></code>

    As briefly mentioned earlier, the renameTo() method can also be used to move a file to a different directory. The new file name passed to the renameTo() method does not have to be in the same directory as the file was already residing in.

    The renameTo() method returns boolean (true or false), indicating whether the renaming was successful. Renaming of moving a file may fail for various reasons, like the file being open, wrong file permissions etc.

  • Delete File:

    To delete a file call the delete() method. Here is a simple example:

    <pre><code>File file = new File(“c:\data\input-file.txt”);

    boolean success = file.delete();</pre></code>
    The delete() method returns boolean (true or false), indicating whether the deletion was successful. Deleting a file may fail for various reasons, like the file being open, wrong file permissions etc.

  • Check if Path is File or Directory:

    A File object can point to both a file or a directory.

    You can check if a File object points to a file or directory, by calling its isDirectory() method. This method returns true if the File points to a directory, and false if the File points to a file. Here is a simple example:

    <pre><code>File file = new File(“c:\data”);

    boolean isDirectory = file.isDirectory();</pre></code>
    Read List of Files in Directory

    You can obtain a list of all the files in a directory by calling either the list() method or the listFiles() method. The list() method returns an array of String’s with the file and / or directory names of directory the File object points to. The listFiles() returns an array of File objects representing the files and / or directories in the directory the File points to.

    Here is a simple example:

    <pre><code>File file = new File(“c:\data”);

    String[] fileNames = file.list();

    File[] files = file.listFiles();</pre></code>

  • Explanation:

    Java provides a mechanism, called object serialization where an object can be represented as a sequence of bytes that includes the object’s data as well as information about the object’s type and the types of data stored in the object.

    After a serialized object has been written into a file, it can be read from the file and deserialized that is, the type information and bytes that represent the object and its data can be used to recreate the object in memory.

    Classes ObjectInputStream and ObjectOutputStream are high-level streams that contain the methods for serializing and deserializing an object.
    Most impressive is that the entire process is JVM independent, meaning an object can be serialized on one platform and deserialized on an entirely different platform.

  • Instructions:

    The ObjectOutputStream class contains many write methods for writing various data types, but one method in particular stands out:

    public final void writeObject(Object x) throws IOException

    The above method serializes an Object and sends it to the output stream. Similarly, the ObjectInputStream class contains the following method for deserializing an object:

    <pre><code>
    public final Object readObject() throws IOException,
    ClassNotFoundException</pre></code>

    This method retrieves the next Object out of the stream and deserializes it. The return value is Object, so you will need to cast it to its appropriate data type.

    To demonstrate how serialization works in Java, I am going to use the Employee class that we discussed early on in the book. Suppose that we have the following Employee class, which implements the Serializable interface:

    <pre><code>public class Employee implements java.io.Serializable
    {
    public String name;
    public String address;
    public transient int SSN;
    public int number;
    public void mailCheck()
    {
    System.out.println(“Mailing a check to “ + name + “ “ + address);
    }
    }</pre></code>

    Notice that for a class to be serialized successfully, two conditions must be met:

    The class must implement the java.io.Serializable interface.

    All of the fields in the class must be serializable. If a field is not serializable, it must be marked transient.

    If you are curious to know if a Java Standard Class is serializable or not, check the documentation for the class. The test is simple: If the class implements java.io.Serializable, then it is serializable; otherwise, it’s not.

  • Serializing an Object:

    The ObjectOutputStream class is used to serialize an Object. The following SerializeDemo program instantiates an Employee object and serializes it to a file.

    When the program is done executing, a file named employee.ser is created. The program does not generate any output, but study the code and try to determine what the program is doing.

    Note: When serializing an object to a file, the standard convention in Java is to give the file a .ser extension.

    <pre><code>import java.io.*;

    public class SerializeDemo
    {
    public static void main(String [] args)
    {
    Employee e = new Employee();
    e.name = “Reyan Ali”;
    e.address = “Phokka Kuan, Ambehta Peer”;
    e.SSN = 11122333;
    e.number = 101;
    try
    {
    FileOutputStream fileOut =
    new FileOutputStream(“/tmp/employee.ser”);
    ObjectOutputStream out = new ObjectOutputStream(fileOut);
    out.writeObject(e);
    out.close();
    fileOut.close();
    System.out.printf(“Serialized data is saved in /tmp/employee.ser”);
    }catch(IOException i)
    {
    i.printStackTrace();
    }
    }
    }</pre></code>

  • Deserializing an Object:

    The following DeserializeDemo program deserializes the Employee object created in the SerializeDemo program. Study the program and try to determine its output:

    <pre><code>import java.io.*;
    public class DeserializeDemo
    {
    public static void main(String [] args)
    {
    Employee e = null;
    try
    {
    FileInputStream fileIn = new FileInputStream(“/tmp/employee.ser”);
    ObjectInputStream in = new ObjectInputStream(fileIn);
    e = (Employee) in.readObject();
    in.close();
    fileIn.close();
    }catch(IOException i)
    {
    i.printStackTrace();
    return;
    }catch(ClassNotFoundException c)
    {
    System.out.println(“Employee class not found”);
    c.printStackTrace();
    return;
    }
    System.out.println(“Deserialized Employee…”);
    System.out.println(“Name: “ + e.name);
    System.out.println(“Address: “ + e.address);
    System.out.println(“SSN: “ + e.SSN);
    System.out.println(“Number: “ + e.number);
    }
    }</pre></code>
    This would produce the following result:

    <pre><code>Deserialized Employee…
    Name: Reyan Ali
    Address:Phokka Kuan, Ambehta Peer
    SSN: 0
    Number:101</pre></code>

  • Additional Comments & Observations

    The try/catch block tries to catch a ClassNotFoundException, which is declared by the readObject() method. For a JVM to be able to deserialize an object, it must be able to find the bytecode for the class. If the JVM can’t find a class during the deserialization of an object, it throws a ClassNotFoundException.

    Notice that the return value of readObject() is cast to an Employee reference.

    The value of the SSN field was 11122333 when the object was serialized, but because the field is transient, this value was not sent to the output stream. The SSN field of the deserialized Employee object is 0.

  • Comment:

    you can generally ignore serialization time, except with the caveat that your serialization time cannot depend in any way on the total size of files that have been added, committed, etc.

  • Personal Comment:

    As my serialization time cannot depend in any way on the total size of files in my commit tree, this means that I should obviously not serialize any of the files in my commit tree, but make sure to only have references to any files, and not have them ported into an internal data structure of mine.

  • Field: HashMap < String, Commit > branchMap

    I need a way to store all of the open branches and associate them with their respective branch name. A HashMap seems to be most sane for that.

  • Field: HashMap < String, List< Commit>> commitMap

    To fulfill the linear speed requirement of find ... I will need a HashMap from commit messages to commits.

  • Field: String currentCommit

    Saves what commit is currently active, and which commit is synced with the current directory.

  • Structure: LinkedList

    Implicit in my commit objects will be a LinkedList data structure.

  • Field: HashMap< File, boolean> addedMap

    Map to keep track off the files that are currently added. This gets emptied on checkout or commit.

  • Field: HashMap< File, boolean> removeMap

    Map to keep track off the files that are currently marked for removal. This gets emptied on checkout or commit.

  • Field: Integer ID

    Unique integer id number

  • Field: String commitDate

    Comment on getting current time as String:

    <pre><code>import java.text.DateFormat
    import java.util.TimeZone

    TimeZone tz = TimeZone.getDefault();
    DateFormat df = new SimpleDateFormat(“yyyy-MM-dd’T’HH:mm’Z’”);
    df.setTimeZone(tz);
    String nowAsISO = df.format(new Date());</pre></code>

  • Field: String message

    Message left by the user.

  • Field: Commit parent

    Parent of the current commit

  • Field: HashMap< Long, File> commitFiles

    Saves the files in this commit, together with a hash of their contents, to check for collisions and modifications

{"cards":[{"_id":"53d8b4a5031274dc89000005","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197053,"position":1,"parentId":null,"content":"# Structural Information"},{"_id":"53d8b9fc031274dc89000007","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197183,"position":1,"parentId":"53d8b4a5031274dc89000005","content":"## Hidden Folder Structure: .Gitlet\n\nAll information not in the running directory is stored in a .gitlet folder that is hidden by default (starts with \".\")"},{"_id":"53d8bec5031274dc89000009","treeId":"53a0fe6e4e52b1a71e00000f","seq":2196618,"position":1,"parentId":"53d8b9fc031274dc89000007","content":"Old Files are stored in .gitlet folder"},{"_id":"53d8d167031274dc89000013","treeId":"53a0fe6e4e52b1a71e00000f","seq":2196719,"position":3,"parentId":"53d8b9fc031274dc89000007","content":"If the stored backup files are manually deleted by the user, and the user tries to restore them, then **ignore the missing file and do not overwrite the current files in the working directory**."},{"_id":"53d8cd4b031274dc89000010","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197066,"position":4,"parentId":"53d8b9fc031274dc89000007","content":"**No Gitlet command should ever delete files in this folder!**"},{"_id":"53d8bf6e031274dc8900000a","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197184,"position":1.25,"parentId":"53d8b4a5031274dc89000005","content":"### \"Running Directory\":\n\nAll files not in the .gitlet folder are in your \"running directory\""},{"_id":"53d8c090031274dc8900000b","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197067,"position":2,"parentId":"53d8bf6e031274dc8900000a","content":"Any command that edits files in the working directory is classified as **dangerous**\n\nIn that case: Gitlet must provide a prompt with the message: \n\n**\"Warning: The command you entered may alter the files in your working directory. Uncommitted changes may be lost. Are you sure you want to continue? (yes/no)\"**"},{"_id":"53d8ce40031274dc89000011","treeId":"53a0fe6e4e52b1a71e00000f","seq":2196714,"position":3,"parentId":"53d8bf6e031274dc8900000a","content":"Cards in the \"Running Directory\" are only every deleted when the user has been prompted with the above warning. "},{"_id":"53d8cfa9031274dc89000012","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197193,"position":3,"parentId":"53d8b4a5031274dc89000005","content":"### Error Messages and Printing\n\nSome commands have special error messages. Only print out these exact error messages. **Do not print out anything that the spec doesn't say you should print out.**"},{"_id":"53d8d87b031274dc89000017","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197052,"position":2.125,"parentId":null,"content":"## **Initialize:** `init` \n"},{"_id":"53d8dddc031274dc89000019","treeId":"53a0fe6e4e52b1a71e00000f","seq":2224495,"position":1,"parentId":"53d8d87b031274dc89000017","content":"### **Description:** \nCreates a new gitlet version control system in the current directory. This system will automatically start with one commit: a commit that contains no files and has the commit message `initial commit`."},{"_id":"53d8de20031274dc8900001a","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197199,"position":2,"parentId":"53d8d87b031274dc89000017","content":"### Runtime: \nconstant"},{"_id":"53d8de36031274dc8900001b","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197200,"position":3,"parentId":"53d8d87b031274dc89000017","content":"### Special Cases: \n\nIn case there already exists a system, print error message:\n\n**\"A gitlet version control system already exists in the current directory.\"**"},{"_id":"53d8e1ff031274dc8900001e","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197054,"position":2.25,"parentId":null,"content":"## **Add:** `add [file name]`"},{"_id":"53d8e302031274dc89000020","treeId":"53a0fe6e4e52b1a71e00000f","seq":2224874,"position":1,"parentId":"53d8e1ff031274dc8900001e","content":"## **Description:** \nIndicates you want the file to be included in the upcoming commit as having been changed. Adding a file is also called staging the file. If the file had been marked for removal, instead just unmark it."},{"_id":"53d8f000031274dc89000025","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197173,"position":1,"parentId":"53d8e302031274dc89000020","content":"#### Personal Comment:\nI will need a file in which I keep track of all of the stages files. This file is emptied with each commit, and the files linked in it are added to the Data Structure in .gitlet."},{"_id":"53d9b91c288961cba3000071","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197205,"position":1.5,"parentId":"53d8e302031274dc89000020","content":"#### Personal Comment:\nI also need a file that keeps track of files that have been marked for removal. It seems to be sane to keep this in a single file, allowing different flags (need to make sure I make IO on this simple)"},{"_id":"53d9b92e288961cba3000072","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197177,"position":1.75,"parentId":"53d8e302031274dc89000020","content":"#### Personal Comment:\nI need a flag that checks files whether they have been modified. This seems to be best done by looking at the \"edited\" date that the filesystem keeps track of, and comparing it to the time of the last commit. This needs to be checked when we run this command."},{"_id":"53d8e87a031274dc89000024","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197201,"position":1.25,"parentId":"53d8e1ff031274dc8900001e","content":"### Runtime: \nLinear in size of added file"},{"_id":"53d8e7ff031274dc89000023","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197202,"position":1.5,"parentId":"53d8e1ff031274dc8900001e","content":"### Failure cases: \nIf the file does not exist, print the error message File does not exist. If the file has not been modified since the last commit, aborts and prints the error message File has not been modified since the last commit."},{"_id":"53d8e529031274dc89000021","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197203,"position":2,"parentId":"53d8e1ff031274dc8900001e","content":"### Differences from real git:\n\n*In Gitlet, if a file is added, then modified, then committed, the contents of the file at the time of the commit command are recorded (not the contents at the time of add). By contrast, in Git, if a file is added, then modified, then committed, the contents of the file at the time of the add command are recorded (not the contents at the time of commit). In short, real Git creates snapshots for every add, whereas in Gitlet, this does not occur.*"},{"_id":"53d8e643031274dc89000022","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197206,"position":2.5,"parentId":null,"content":"## **Commit:** `commit [message]`"},{"_id":"53d8f9fc031274dc89000028","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197207,"position":1,"parentId":"53d8e643031274dc89000022","content":"## **Description:** \nSaves a snapshot of certain files that can be viewed or restored at a later time. The files in a commit's snapshot come from two sources: files that were newly added to this commit (staged prior to the commit), and files that were inherited from the previous commit. We'll refer to these two groups of files as \"the commit's added files\" and \"the commit's old files\" respectively. In general, a new commit inherits all of the files in the previous commit as its old files (both the previous commit's added and old files). However, don't inherit files that were added to the new commit, becuase the added file takes precedent over the old one. Remember that adding a file indicates you want to save a new version of the file, so if you added the file it means that you don't need the old version anymore."},{"_id":"53d90ffb031274dc8900003a","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197154,"position":3.50001,"parentId":"53d8f9fc031274dc89000028","content":"#### **Field:** Unique integer id number"},{"_id":"53d90dff031274dc89000038","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197155,"position":4.00001,"parentId":"53d8f9fc031274dc89000028","content":"#### **Field:** Date when commit was made"},{"_id":"53d9167f031274dc8900003c","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197157,"position":4.12501,"parentId":"53d8f9fc031274dc89000028","content":"#### **Field:** Message left by the user. \n\n*Note: Only one entry in the args[] array, surround by quotes if you want longer*"},{"_id":"53d90dd6031274dc89000036","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197158,"position":4.25001,"parentId":"53d8f9fc031274dc89000028","content":"#### **Algorithm:** This command moves the head pointer to point to the new node."},{"_id":"53d90da0031274dc89000035","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197159,"position":4.50001,"parentId":"53d8f9fc031274dc89000028","content":"#### **Algorithm:** The commit is added as a new node in the commit tree."},{"_id":"53d91c52031274dc8900003e","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197160,"position":4.75001,"parentId":"53d8f9fc031274dc89000028","content":"#### **Picture:** \n![](https://www.filepicker.io/api/file/xTDC4cueS6qQVj197Yku)"},{"_id":"53d8fbe1031274dc8900002a","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197220,"position":3,"parentId":"53d8e643031274dc89000022","content":"### **Runtime:**"},{"_id":"53d90117031274dc8900002d","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197213,"position":1,"parentId":"53d8fbe1031274dc8900002a","content":"#### **Constant:** Number of Commits"},{"_id":"53d9046c031274dc8900002e","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197214,"position":2,"parentId":"53d8fbe1031274dc8900002a","content":"#### **Linear:** Size of files in snapshot"},{"_id":"53d90478031274dc8900002f","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197215,"position":3,"parentId":"53d8fbe1031274dc8900002a","content":"#### **Linear:** Number of files in last commit"},{"_id":"53d90498031274dc89000030","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197221,"position":3.5,"parentId":"53d8e643031274dc89000022","content":"### **Memory:**"},{"_id":"53d9063e031274dc89000031","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197216,"position":1,"parentId":"53d90498031274dc89000030","content":"#### Memory Requirement:\nSize increase of .gitlet folder is smaller than total size of new commit's files + Metadata"},{"_id":"53d90b15031274dc89000033","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197217,"position":1.5,"parentId":"53d90498031274dc89000030","content":"#### Comment:\nDon't store redundant copies of files that you already have and that haven't changed"},{"_id":"53d906f4031274dc89000032","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197148,"position":2,"parentId":"53d90498031274dc89000030","content":"#### Comment:\nYou are allowed to save whole additional copies of files; don't worry about only saving diffs, or anything like that."},{"_id":"53d8fdef031274dc8900002b","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197219,"position":4,"parentId":"53d8e643031274dc89000022","content":"### Failure cases:\n+ If no files have been staged (or marked for removal: more on that next), aborts. Print the error message **\"No changes added to the commit\"**. \n+ Also, the commit must have a non-blank message. \n+ If it doesn't, print the error message **\"Please enter a commit message\"**."},{"_id":"53d90088031274dc8900002c","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197212,"position":5,"parentId":"53d8e643031274dc89000022","content":"### Differences from real git: \n*In real git, commits are not associated with any old id number, but a special kind of hash code. Using a hash is actually more powerful than using an arbitrary id number; can you think of why? Also, snapshots in real git are taken when files are added, not committed.*"},{"_id":"53d90bf7031274dc89000034","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197059,"position":4,"parentId":null,"content":"## **Remove:** `rm [file name]`"},{"_id":"53d92057031274dc89000040","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197222,"position":1,"parentId":"53d90bf7031274dc89000034","content":"## **Description:** \nMark the file for removal; this means it will not be inherited as an old file in the next commit. If the file had been staged, instead unstage it."},{"_id":"53d920fd031274dc89000041","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197223,"position":2,"parentId":"53d90bf7031274dc89000034","content":"### Runtime: \nConstant"},{"_id":"53d92162031274dc89000042","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197225,"position":3,"parentId":"53d90bf7031274dc89000034","content":"### Failure cases: \nIf the file is neither added nor included in the previous commit, print the error message **\"No reason to remove the file\"**."},{"_id":"53d9224c031274dc89000043","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197227,"position":4,"parentId":"53d90bf7031274dc89000034","content":"### Differences from real git:\n*Be aware the the real git rm command works differently; it will actually delete the file! This command is more similar to git rm --cached [file name].*"},{"_id":"53d9231f031274dc89000044","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197057,"position":5,"parentId":null,"content":"## **Log:** `log`"},{"_id":"53d9236b031274dc89000045","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197228,"position":1,"parentId":"53d9231f031274dc89000044","content":"## Description:\n \nStarting at the current head pointer, display information about each commit backwards along the commit tree until the initial commit. This set of commit nodes is called the commit’s history. For every node in this history, the information it should display is the commit id, the time the commit was made, and the commit message. "},{"_id":"53d9244f031274dc89000046","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197262,"position":1,"parentId":"53d9236b031274dc89000045","content":"#### **Example:**\n\n>====\n>Commit 2.\n>2015-03-14 11:59:26\n>A commit message.\n>\n>====\n>Commit 1.\n>2015-03-14 11:49:29\n>Another commit message.\n>\n>====\n>Commit 0.\n>2015-03-14 11:39:26\n>initial commit"},{"_id":"53d92e96031274dc8900004b","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197246,"position":1.5,"parentId":"53d9236b031274dc89000045","content":"#### Information Displayed: \n+ Commit ID\n+ Time of the Commit\n+ Commit Message"},{"_id":"53d9ce3d288961cba3000073","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197253,"position":1.75,"parentId":"53d9236b031274dc89000045","content":"#### Formatting Comments:\n+ **\"====\"** separating each commit\n+ **empty line** between each commit\n+ **most recent** commit is at the top\n+ **Use Java Standard Library for dates!**"},{"_id":"53d93053031274dc8900004d","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197254,"position":1.5,"parentId":"53d9231f031274dc89000044","content":"### Runtime: \nShould be linear with respect to the number of nodes in head’s history."},{"_id":"53d93006031274dc8900004c","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197255,"position":2,"parentId":"53d9231f031274dc89000044","content":"### Failure cases: None"},{"_id":"53d93182031274dc8900004f","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197256,"position":3,"parentId":"53d9231f031274dc89000044","content":"### **Structural Clarification:**"},{"_id":"53d9aa63288961cba3000070","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197264,"position":0.5,"parentId":"53d93182031274dc8900004f","content":"#### **Example:**\n\n![](https://www.filepicker.io/api/file/VydOCiJGTGCEkjaWg11F)"},{"_id":"53d9343f031274dc89000051","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197266,"position":1,"parentId":"53d93182031274dc8900004f","content":"#### Comment: \n\nThe branch pointer is pointing at the commit to the far right lower corner. In this case the circled commits would be printed."},{"_id":"53d936a0031274dc89000052","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197276,"position":2,"parentId":"53d93182031274dc8900004f","content":"#### Comment:\n\nThe history of a commit with a particular ID can never change. That history is immutable."},{"_id":"53d9382c031274dc89000053","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197060,"position":6,"parentId":null,"content":"## **Find:** `find [commit message]`"},{"_id":"53d938b9031274dc89000054","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197278,"position":1,"parentId":"53d9382c031274dc89000053","content":"## **Description:** \nPrints out the id of the commit that has the given commit message. If there are multiple such commits, it prints the ids out on separate lines."},{"_id":"53d9394b031274dc89000055","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197279,"position":2,"parentId":"53d9382c031274dc89000053","content":"### Runtime:\nShould be linear relative to the number of commits that have the given message."},{"_id":"53d9a799288961cba300006f","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197281,"position":1,"parentId":"53d9394b031274dc89000055","content":"#### **Personal Comment:**\nThis means I will have to create a HashMap from commit messages to commits."},{"_id":"53d9d739288961cba3000077","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197298,"position":2,"parentId":"53d9394b031274dc89000055","content":"#### **Field:** HashMap< String, Commit >\n\nHashMap mapping commit messages to commit for runtime reasons"},{"_id":"53d939dd031274dc89000057","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197284,"position":4,"parentId":"53d9382c031274dc89000053","content":"### Failure cases: \nIf no such commit exists, prints the error message Found no commit with that message."},{"_id":"53d93a9e031274dc89000058","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197286,"position":5,"parentId":"53d9382c031274dc89000053","content":"### Differences from real git: \n\n*Doesn't exist in real git. Similar effects can be achieved by grepping the output of log.*"},{"_id":"53d93b04031274dc89000059","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197061,"position":7,"parentId":null,"content":"## **Status:** `status`"},{"_id":"53d93b82031274dc8900005a","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197287,"position":1,"parentId":"53d93b04031274dc89000059","content":"## **Description: **\nDisplays what branches currently exist, and marks the current branch with a *. Also displays what files have been staged or marked for removal. An example of the exact format it should follow is as follows.\n\n>=== Branches ===\n>*master\n>other-branch\n>\n>=== Staged Files ===\n>wug.txt\n>some_folder/wugs.txt\n>\n>=== Files Marked for Removal ===\n>goodbye.txt\n\nNotice there is an empty line between each section. The order of branches/files within each section does not matter."},{"_id":"53d941d5031274dc8900005e","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197288,"position":1,"parentId":"53d93b82031274dc8900005a","content":"#### **Output:** List of existing branches (endpoints in the commit tree)"},{"_id":"53d943ba031274dc8900005f","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197083,"position":2,"parentId":"53d93b82031274dc8900005a","content":"#### **Output:** List of staged files (files added with add)"},{"_id":"53d9450e031274dc89000060","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197084,"position":3,"parentId":"53d93b82031274dc8900005a","content":"#### **Output:** List of to be removed files"},{"_id":"53d948b9031274dc89000062","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197296,"position":5,"parentId":"53d93b82031274dc8900005a","content":"#### **Field:** Collection< Commits > \nCollection of pointers to the current branch endpoints"},{"_id":"53d94b28031274dc89000063","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197086,"position":6,"parentId":"53d93b82031274dc8900005a","content":"#### **File:** rem_marked_files.csv\n\nlist of all files currently marked for removal"},{"_id":"53d9465f031274dc89000061","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197087,"position":7,"parentId":"53d93b82031274dc8900005a","content":"#### **File:** staged_files.csv\n\nlist of all currently staged (added but not commited) files."},{"_id":"53d93e50031274dc8900005b","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197299,"position":2,"parentId":"53d93b04031274dc89000059","content":"### Runtime: Linear relative to...\n+ Number of files stages\n+ Number of files marked for removal\n+ Number of currently open branches"},{"_id":"53d93e61031274dc8900005c","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197301,"position":3,"parentId":"53d93b04031274dc89000059","content":"### Failure cases: None"},{"_id":"53d94e4f031274dc89000064","treeId":"53a0fe6e4e52b1a71e00000f","seq":2206920,"position":8,"parentId":null,"content":"## **Checkout:** \n\n`checkout [file name]`\n`checkout [commit id] [file name]`\n`checkout [branch name]`"},{"_id":"53d956b6031274dc89000065","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197303,"position":1,"parentId":"53d94e4f031274dc89000064","content":"## **Description:** `[file name]`\n\nRestores the given file in the working directory to its state at the commit at the head of the current branch."},{"_id":"53d95dc2031274dc89000069","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197315,"position":2,"parentId":"53d956b6031274dc89000065","content":"#### **Runtime:** Linear to size of file in [file name]"},{"_id":"53d967e0031274dc8900006c","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197313,"position":3,"parentId":"53d956b6031274dc89000065","content":"#### Failure Cases: \nIf the file does not exist in the previous commit, aborts, printing the error message **\"File does not exist in the most recent commit, or no such branch exists\"**."},{"_id":"53d95a48031274dc89000067","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197307,"position":2,"parentId":"53d94e4f031274dc89000064","content":"## **Description:** `[commit id] [file name]`\n\nRestores the given file in the working directory to its state at the given commit."},{"_id":"53d95e80031274dc8900006a","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197095,"position":1,"parentId":"53d95a48031274dc89000067","content":"#### **Runtime:** Linear to size of file in [file name]"},{"_id":"53d96922031274dc8900006d","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197316,"position":2,"parentId":"53d95a48031274dc89000067","content":"#### Failure Cases: \nIf no commit with the given id exists, print** \"No commit with that id exists\"**. Else, if the file does not exist in the given commit, print \"**File does not exist in that commit\"**."},{"_id":"53d95bc2031274dc89000068","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197309,"position":3,"parentId":"53d94e4f031274dc89000064","content":"## **Description:** `[branch name]`\n\nRestores all files in the working directory to their versions in the commit at the head of the given branch. Considers the given branch to now be the current branch."},{"_id":"53d962d6031274dc8900006b","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197328,"position":1,"parentId":"53d95bc2031274dc89000068","content":"#### Runtime: \n+ Linear to size of of files in commit's snapshot\n+ Constant to number of commits\n+ Constant to number of branches "},{"_id":"53d96aef031274dc8900006e","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197331,"position":2,"parentId":"53d95bc2031274dc89000068","content":"#### Failure Cases: \n+ If no branch with that name exists, print **\"File does not exist in the most recent commit, or no such branch exists\"**. \n+ If that branch is the current branch, print **\"No need to checkout the current branch\"**."},{"_id":"53d96ee7031274dc8900006f","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197329,"position":4,"parentId":"53d94e4f031274dc89000064","content":"### Comment: \nWhen File Name is the same as the name of an existing branch, then the branch name takes precedence. "},{"_id":"53d971a1031274dc89000071","treeId":"53a0fe6e4e52b1a71e00000f","seq":2197310,"position":5,"parentId":"53d94e4f031274dc89000064","content":"### **Dangerous**\n\nMake sure to ask the user before overwriting any files"},{"_id":"53e4d9ef1609823bf9000072","treeId":"53a0fe6e4e52b1a71e00000f","seq":2206918,"position":8.5,"parentId":null,"content":"## **Branch:** `branch [branch name]`"},{"_id":"53e4db611609823bf9000073","treeId":"53a0fe6e4e52b1a71e00000f","seq":2206842,"position":1,"parentId":"53e4d9ef1609823bf9000072","content":"## **Description:** \nCreates a new branch with the given name. A branch is nothing more than the name of a head pointer in the commit graph. Before you ever call branch, your code should be running with a default branch called \"master\". Note: Does NOT immediately switch to the newly created branch."},{"_id":"53e4ddd51609823bf9000076","treeId":"53a0fe6e4e52b1a71e00000f","seq":2206877,"position":1,"parentId":"53e4db611609823bf9000073","content":"### Example: \n\nInitial State: \n\n![](http://berkeley-cs61b.github.io/public_html/materials/proj/proj2/image/simple_history.png)\n"},{"_id":"53e4e05f1609823bf9000077","treeId":"53a0fe6e4e52b1a71e00000f","seq":2206881,"position":2,"parentId":"53e4db611609823bf9000073","content":"#### Call: `java Gitlet branch cool-beans`:\n\n![](http://berkeley-cs61b.github.io/public_html/materials/proj/proj2/image/just_called_branch.png)"},{"_id":"53e513081609823bf9000078","treeId":"53a0fe6e4e52b1a71e00000f","seq":2206888,"position":3,"parentId":"53e4db611609823bf9000073","content":"Call: `java Gitlet checkout cool-beans`\n\n*Switch to branch \"cool-beans\"*\n\n![](http://berkeley-cs61b.github.io/public_html/materials/proj/proj2/image/just_switched_branch.png)"},{"_id":"53e517581609823bf9000079","treeId":"53a0fe6e4e52b1a71e00000f","seq":2206889,"position":4,"parentId":"53e4db611609823bf9000073","content":"#### Call: `java Gitlet add...` + `java Gitlet commit...` \n\n*Changed files and commited stuff*\n\n![](http://berkeley-cs61b.github.io/public_html/materials/proj/proj2/image/commit_on_branch.png)"},{"_id":"53e51bcf1609823bf900007a","treeId":"53a0fe6e4e52b1a71e00000f","seq":2206891,"position":5,"parentId":"53e4db611609823bf9000073","content":"#### Call: `java Gitlet checkout master`\n\n*Switch back to original branch*\n\n![](http://berkeley-cs61b.github.io/public_html/materials/proj/proj2/image/checkout_master.png)"},{"_id":"53e51ddf1609823bf900007b","treeId":"53a0fe6e4e52b1a71e00000f","seq":2206892,"position":6,"parentId":"53e4db611609823bf9000073","content":"#### Call: `java Gitlet add...` + `java Gitlet commit...`\n\n*Commit on the original branch*\n\n![](http://berkeley-cs61b.github.io/public_html/materials/proj/proj2/image/branched.png)"},{"_id":"53e520071609823bf900007c","treeId":"53a0fe6e4e52b1a71e00000f","seq":2206906,"position":7,"parentId":"53e4db611609823bf9000073","content":"#### Comment: \n\n+ All that `branch` does is create a new head pointer. \n+ There is only one active head-pointer at any time (indicated by *)\n+ `checkout` switches the current active head-pointer\n+ `commit` creates a new commit in front of the currently active head pointer, even if one is already here. "},{"_id":"53e4dc751609823bf9000074","treeId":"53a0fe6e4e52b1a71e00000f","seq":2206909,"position":2,"parentId":"53e4d9ef1609823bf9000072","content":"### **Runtime:** Constant"},{"_id":"53e4dce21609823bf9000075","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207016,"position":3,"parentId":"53e4d9ef1609823bf9000072","content":"### Failure cases: \nIf a branch with the given name already exists, print the error message `A branch with that name already exists`."},{"_id":"53e5275a1609823bf900007d","treeId":"53a0fe6e4e52b1a71e00000f","seq":2206917,"position":8.75,"parentId":null,"content":"##**Remove:** `rm-branch [branch name]`"},{"_id":"53e529181609823bf900007e","treeId":"53a0fe6e4e52b1a71e00000f","seq":2206923,"position":1,"parentId":"53e5275a1609823bf900007d","content":"## **Description:** \nDeletes the branch with the given name. This only means to delete the head pointer associated with the branch; it does not mean to delete all commits that were created under the branch, or anything like that."},{"_id":"53e52aa51609823bf900007f","treeId":"53a0fe6e4e52b1a71e00000f","seq":2206926,"position":2,"parentId":"53e5275a1609823bf900007d","content":"### **Runtime:** Constant"},{"_id":"53e52b171609823bf9000080","treeId":"53a0fe6e4e52b1a71e00000f","seq":2206982,"position":3,"parentId":"53e5275a1609823bf900007d","content":"### Failure cases: \nIf a branch with the given name does not exist, aborts. Print the error message `A branch with that name does not exist`. If you try to remove the branch you're currently on, aborts, printing the error message `Cannot remove the current branch`.\n"},{"_id":"53e52cd71609823bf9000081","treeId":"53a0fe6e4e52b1a71e00000f","seq":2206986,"position":8.875,"parentId":null,"content":"## **Reset:** `reset [commit id]`"},{"_id":"53e52d8a1609823bf9000082","treeId":"53a0fe6e4e52b1a71e00000f","seq":2206994,"position":1,"parentId":"53e52cd71609823bf9000081","content":"## **Descriptions:**\n\nRestores all files to their versions in the commit with the given id. Also moves the current branch's head to that commit node."},{"_id":"53e52f721609823bf9000083","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207011,"position":2,"parentId":"53e52cd71609823bf9000081","content":"### **Runtime:**"},{"_id":"53e5338e1609823bf9000087","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207014,"position":1,"parentId":"53e52f721609823bf9000083","content":"#### Linear: \nTotal size of files in the commit's snapshot"},{"_id":"53e534a51609823bf9000088","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207013,"position":2,"parentId":"53e52f721609823bf9000083","content":"#### Constant: \nNumber of commits"},{"_id":"53e530421609823bf9000084","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207015,"position":3,"parentId":"53e52cd71609823bf9000081","content":"### **Failure cases:**\n\nIf no commit with the given id exists, print `No commit with that id exists`."},{"_id":"53e531501609823bf9000085","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207010,"position":4,"parentId":"53e52cd71609823bf9000081","content":"### **Dangerous**\n\nMake sure to ask the user for permission before executing."},{"_id":"53e531601609823bf9000086","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207007,"position":5,"parentId":"53e52cd71609823bf9000081","content":"### Differences from real git: \n*Just note that this command is closest to using the --hard option, i.e. git reset --hard [commit hash].*"},{"_id":"53e536951609823bf9000089","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207017,"position":8.9375,"parentId":null,"content":"## **Merge:** `merge [branch name]`"},{"_id":"53e537601609823bf900008a","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207018,"position":1,"parentId":"53e536951609823bf9000089","content":"## **Description: **\nMerges files from the head of the given branch into the head of the current branch."},{"_id":"53e5416c1609823bf9000091","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207034,"position":1,"parentId":"53e537601609823bf900008a","content":"#### Example: \n\nConsider the split point of the current branch and the given branch: \n\n![](http://berkeley-cs61b.github.io/public_html/materials/proj/proj2/image/split_point.png)"},{"_id":"53e547291609823bf9000092","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207036,"position":2,"parentId":"53e537601609823bf900008a","content":"#### Comment: \nAny files that have been modified in the given branch (added to any of the commits along the branch, and not subsequently removed) but not in the current branch since the split point should be changed to their versions in the given branch."},{"_id":"53e548a41609823bf9000093","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207037,"position":3,"parentId":"53e537601609823bf900008a","content":"#### Comment: \nAny files that have been modified in the current branch (added and not subsequently removed) but not in the given branch since the split point should stay as they are."},{"_id":"53e548b71609823bf9000094","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207039,"position":4,"parentId":"53e537601609823bf900008a","content":"#### Comment: \nFor files that have been modified in both branches since the split point (added but not subsequently removed), the files should stay as they are in the current branch. However, in addition, the version of the file from the given branch should be copied into the file system with the name `[old file name].conflicted`."},{"_id":"53e539161609823bf900008b","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207026,"position":2,"parentId":"53e536951609823bf9000089","content":"### **Runtime: **"},{"_id":"53e53e551609823bf900008f","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207027,"position":1,"parentId":"53e539161609823bf900008b","content":"#### Linear: \nLength of History of the current commit\n\n#### Linear: \nTotal size of files in the two commit branches"},{"_id":"53e53c7d1609823bf900008c","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207025,"position":3,"parentId":"53e536951609823bf9000089","content":"### Failure cases: \nIf a branch with the given name does not exist, print the error message `A branch with that name does not exist`. If attempting to merge a branch with itself, print the error message `Cannot merge a branch with itself`."},{"_id":"53e53ca81609823bf900008d","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207021,"position":4,"parentId":"53e536951609823bf9000089","content":"### Dangerous: \n*Also, take note that although this method modifies files, it should NOT modify the commit tree. In order to save changes into the commit tree, they must be added and committed like normal. There is no special case for this commit; it's just a normal commit.*"},{"_id":"53e53d271609823bf900008e","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207022,"position":5,"parentId":"53e536951609823bf9000089","content":"### Differences from real git: \n*There are a few. For one, if the real git doesn't run into any merge conflicts, it will automatically create a new commit for you at the end, but gitlet does not. In git, this new commit at the end of merge is special, because it maintains two back pointers remembering which two branches it came from. But gitlet only maintains one normal back pointer on the current branch.*"},{"_id":"53e54be91609823bf9000097","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207048,"position":1,"parentId":"53e53d271609823bf900008e","content":"#### Additional Comment: \n\n*Furthermore, the real git handles merge conflicts differently than gitlet. The real git will splice the two conflicted files together into a single file, then ask the user to pick and choose the correct sections manually. Gitlet does not do this, instead just adding in the .conflicted copy. Furthermore, git will put you in a special state where the commands you can run are limited until you finish resolving the merge conflict. Gitlet does no such thing.*"},{"_id":"53e54ad01609823bf9000096","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207050,"position":8.96875,"parentId":null,"content":"## **Rebase:** `rebase [branch name]`"},{"_id":"53e54d921609823bf9000098","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207052,"position":1,"parentId":"53e54ad01609823bf9000096","content":"## **Description: **\nConceptually, what rebase does is find the split point of the current branch and the given branch, then snaps off the current branch at this point, then reattaches the current branch to the head of the given branch."},{"_id":"53e54f2c1609823bf9000099","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207054,"position":1,"parentId":"53e54d921609823bf9000098","content":"### **Example:**\n*Say we are on branch `branch` and we make the call `java Gitlet rebase master`:*\n\n![](http://berkeley-cs61b.github.io/public_html/materials/proj/proj2/image/conceptual_rebase.png)\n "},{"_id":"53e5534e1609823bf900009b","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207055,"position":2,"parentId":"53e54d921609823bf9000098","content":"#### **Comment:** Why would you ever want to do this?\nYou can think of it as an alternative to merge, where instead of having two branches that come together, you just pretend as if one of the branches came after the other one. If you use it smartly, this can create a cleaner history than merge."},{"_id":"53e554e51609823bf900009c","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207056,"position":3,"parentId":"53e54d921609823bf9000098","content":"#### **Comment:** Doesn't this ruin what you said about the commit tree being immutable? \nYes, it does! That's because I just lied to you in the picture above. In fact, rebase does not break off the current branch. Instead, it leaves the current branch there, but makes a copy of the current branch on top of the given branch (this is called replaying the branch). Then it moves the branch pointer to point to this copy, so that you can pretend you moved it."},{"_id":"53e555f01609823bf900009d","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207057,"position":4,"parentId":"53e54d921609823bf9000098","content":"### Real Picture: \n\n![](http://berkeley-cs61b.github.io/public_html/materials/proj/proj2/image/real_rebase.png)"},{"_id":"53e557c71609823bf900009e","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207058,"position":5,"parentId":"53e54d921609823bf9000098","content":"#### Note: \n\n*Note: the replayed commits should have new ids, not copies of the original ids. This allows you to still access the original commits using their old ids, if you really wanted to. In addition, the replayed commits should have new time stamps, allowing you to distinguish them from the originals in global-log.*"},{"_id":"53e55c991609823bf900009f","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207071,"position":6,"parentId":"53e54d921609823bf9000098","content":"#### Comment: \nThere's one more point to make about rebase: If after the split point the given branch contains modifications to files that were not modified in the current branch, then these modifications should propagate through the replayed branch. If both the given branch and the current branch have modifications to the same files, then what you would expect to happen is that you would get conflicted files, much like merge. However, for simplicity, we're not going to have you deal with conflicts: in this case, just use the current branch's copies of the files."},{"_id":"53e55da41609823bf90000a1","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207073,"position":8,"parentId":"53e54d921609823bf9000098","content":"#### Comment: \nFinally, after any successful rebase command, update the files in the working directory to reflect the versions of the files at the new head of the current branch."},{"_id":"53e55dc81609823bf90000a2","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207076,"position":9,"parentId":"53e54d921609823bf9000098","content":"#### Comment: \nBy the way, if there are multiple branches after the split point, you should NOT replay the other branches. For example, say we are on branch branch1 and we make the call `java Gitlet rebase master`:\n\n![](http://berkeley-cs61b.github.io/public_html/materials/proj/proj2/image/branching_rebase.png)"},{"_id":"53e563511609823bf90000a3","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207079,"position":2,"parentId":"53e54ad01609823bf9000096","content":"### **Runtime:** \nShould be linear relative to the history of the current brach and the given branch. Should also be linear in terms of the number of files added to both branches. Should also be linear relative to the total size of files added to the given branch. Also, be aware that rebase should not need to make any additional backup copies of files."},{"_id":"53e565f21609823bf90000a4","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207080,"position":3,"parentId":"53e54ad01609823bf9000096","content":"### Failure cases: \n+ If a branch with the given name does not exist, print the error message `A branch with that name does not exist`. \n+ If the given branch name is the same as the current branch name, print the error message `Cannot rebase a branch onto itself`. \n+ If the input branch's head is in the history of the current branch's head, print the error message `Already up-to-date`."},{"_id":"53e566e81609823bf90000a5","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207084,"position":4,"parentId":"53e54ad01609823bf9000096","content":"### **Dangerous:** \n\nMake sure to ask the user before you execute this command"},{"_id":"53e5673b1609823bf90000a6","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207082,"position":5,"parentId":"53e54ad01609823bf9000096","content":"### Differences from the real git: \n*The real git's rebase is a complicated and many-flagged command. Gitlet's rebase really only gets at the core idea. In particular, note that the way it handles conflicts is much different! For instance, the real rebase will pause when it encounters a conflict, make the user fix it, and then continue on afterward.*"},{"_id":"53e5685f1609823bf90000a7","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207087,"position":8.984375,"parentId":null,"content":"## **Interactive Rebase**: `i-rebase [branch name]`"},{"_id":"53e569c21609823bf90000a8","treeId":"53a0fe6e4e52b1a71e00000f","seq":2263741,"position":1,"parentId":"53e5685f1609823bf90000a7","content":"## **Description**:\n \nThis does essentially what rebase does, but with one twist. For each node it replays, it allows the user to change the commit’s message or skip replaying the commit. This means the command needs to pause and prompt the user for text input before continuing with each commit."},{"_id":"53e57a911609823bf90000ab","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207111,"position":1,"parentId":"53e569c21609823bf90000a8","content":"### Sequence of Operation: \n\nFor each commit, the command should print out the following:\n\n`Currently replaying:`"},{"_id":"53e57c1b1609823bf90000ac","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207112,"position":2,"parentId":"53e569c21609823bf90000a8","content":"Then it should print out information about the commit like log does (just the one commit, not all of its history)."},{"_id":"53e57c4a1609823bf90000ad","treeId":"53a0fe6e4e52b1a71e00000f","seq":2263740,"position":3,"parentId":"53e569c21609823bf90000a8","content":"Finally it should prompt the user with the message\n\n`Would you like to (c)ontinue, (s)kip this commit, or change this commit's (m)essage?`\n\nIt should act based on whether the user types c, s, or m. If the user types something different, asks the user for input again."},{"_id":"53e57ceb1609823bf90000ae","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207114,"position":4,"parentId":"53e569c21609823bf90000a8","content":"If the user choose to continue, then it replays the commit and continues on to ask about the next one."},{"_id":"53e57d191609823bf90000af","treeId":"53a0fe6e4e52b1a71e00000f","seq":2224788,"position":5,"parentId":"53e569c21609823bf90000a8","content":"If the user chooses to skip, then it does not replay the commit, but continues on to ask about the next one."},{"_id":"540b8e859034670b06000112","treeId":"53a0fe6e4e52b1a71e00000f","seq":2224791,"position":5.5,"parentId":"53e569c21609823bf90000a8","content":"#### Note: \n*skipping a commit does not mean you forget its changes. Rather, the changes made in a skipped commit are incorporated into or overwritten by the next unskipped commit. You cannot skip the initial or final commit of a branch. If a user does this, ask them for input again.*"},{"_id":"53e57d371609823bf90000b0","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207116,"position":6,"parentId":"53e569c21609823bf90000a8","content":"If the user chooses to change the commit's message, then prompt them with\n\n`Please enter a new message for this commit.`\n\nand wait for them to enter a message. Include this as the message of the replayed commit."},{"_id":"53e57da41609823bf90000b1","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207119,"position":7,"parentId":"53e569c21609823bf90000a8","content":"#### Comment: \nBy the way, if you didn't notice, the point of interactive rebase is that it gives you the power to rewrite history. Interactive rebase essentially allows you to pretend to modify the commit tree, fixing typos in commits or getting rid of useless ones."},{"_id":"53e584d41609823bf90000b8","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207147,"position":8,"parentId":"53e569c21609823bf90000a8","content":"#### Personal Comment: \n\nI should probably just implement `rebase ...` and `i-rebase ...` with the same command but different parameters. They basically do the same thing, and the interactive part if `i-rebase ...` can be done with a few conditionals on a flag or something like that. "},{"_id":"53e579821609823bf90000a9","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207120,"position":2,"parentId":"53e5685f1609823bf90000a7","content":"### **Runtime:** Same as `rebase [branch name]`"},{"_id":"53e580011609823bf90000b2","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207122,"position":3,"parentId":"53e5685f1609823bf90000a7","content":"### **Failure cases:** Same as `rebase [branch name]`"},{"_id":"53e581ad1609823bf90000b3","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207124,"position":4,"parentId":"53e5685f1609823bf90000a7","content":"### Dangerous:\n\nMake sure to ask the user before you execute this command"},{"_id":"53e582931609823bf90000b4","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207126,"position":5,"parentId":"53e5685f1609823bf90000a7","content":"### Differences from the real git: \n*This command is inspired by the -i flag for git's rebase, i.e. git rebase -i [branch name]. Git's interactive rebase is much more fully featured than gitlet's!*"},{"_id":"53e58b131609823bf90000b9","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207148,"position":8.9921875,"parentId":null,"content":"## Input & Output"},{"_id":"53e58bca1609823bf90000ba","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207166,"position":1,"parentId":"53e58b131609823bf90000b9","content":"### Class: `java.io.File`\n\nThe File class is used to get access to the underlying file system, and allows me to specify the location of files, and create directory structures. \n\n**It does not provide methods to edit files**"},{"_id":"53e59f521609823bf90000bb","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207171,"position":1,"parentId":"53e58bca1609823bf90000ba","content":"#### Instantiating a java.io.File\n\nBefore you can do anything with the file system or File class, you must obtain a File instance. Here is how that is done:\n\n`File file = new File(\"c:\\\\data\\\\input-file.txt\");`\n\nSimple, right? The File class also has a few other constructors you can use to instantiate File instances in different ways."},{"_id":"53e5a26a1609823bf90000bc","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207204,"position":2,"parentId":"53e58bca1609823bf90000ba","content":"#### Check if File Exists\n\nOnce you have instantiated a File object you can check if the corresponding file actually exists already. The File class constructor will not fail if the file does not already exists. You might want to create it now, right?\n\nTo check if the file exists, call the exists() method. Here is a simple example:\n\n<pre><code>File file = new File(\"c:\\\\data\\\\input-file.txt\");\nboolean fileExists = file.exists(); \n</pre></code>"},{"_id":"53e5a28f1609823bf90000bd","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207208,"position":3,"parentId":"53e58bca1609823bf90000ba","content":"#### Create a Directory if it Does Not Exist\n\nYou can use the File class to create directories if they don't already exists. The File class contains the method `mkdir()` and `mkdirs()` for that purpose.\n\nThe `mkdir()` method creates a single directory if it does not already exist. Here is an example:\n\n<pre><code> File file = new File(\"c:\\\\users\\\\jakobjenkov\\\\newdir\"); \n\nboolean dirCreated = file.mkdir();\n</pre></code>\n\nProvided that the directory `c:\\users\\jakobjenkov` already exists, the above code will create a subdirectory of jakobjenkov named newdir. The `mkdir()` returns true if the directory was created, and false if not.\n\nThe mkdirs() will create all directories that are missing in the path the File object represents. Here is an example:\n\n<pre><code>File file = new File(\"c:\\\\users\\\\jakobjenkov\\\\newdir\");\nboolean dirCreated = file.mkdirs();</pre></code>\n\nProvided that the C drive exists, this example will create all the directories in the path `c:\\users\\jakobjenkov\\newdir`. The `mkdirs()` method will return true if all the directories were created, and false if not."},{"_id":"53e5b8a01609823bf90000bf","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207211,"position":4,"parentId":"53e58bca1609823bf90000ba","content":"#### File Length:\n\nTo read the length of a file in bytes, call the `length()` method. Here is a simple example:\n\n<pre><code>File file = new File(\"c:\\\\data\\\\input-file.txt\");\n\nlong length = file.length();\n</pre></code>\n#### Rename or Move File:\n\nTo rename (or move) a file, call the method `renameTo()` on the File class. Here is a simple example:\n\n<pre><code>File file = new File(\"c:\\\\data\\\\input-file.txt\");\n\nboolean success = file.renameTo(new File(\"c:\\\\data\\\\new-file.txt\"));\n</pre></code>\n\nAs briefly mentioned earlier, the `renameTo()` method can also be used to move a file to a different directory. The new file name passed to the `renameTo()` method does not have to be in the same directory as the file was already residing in.\n\nThe `renameTo()` method returns boolean (true or false), indicating whether the renaming was successful. Renaming of moving a file may fail for various reasons, like the file being open, wrong file permissions etc."},{"_id":"53e5bd841609823bf90000c0","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207214,"position":5,"parentId":"53e58bca1609823bf90000ba","content":"#### Delete File:\n\nTo delete a file call the `delete()` method. Here is a simple example:\n\n<pre><code>File file = new File(\"c:\\\\data\\\\input-file.txt\");\n\nboolean success = file.delete();</pre></code>\nThe delete() method returns boolean (true or false), indicating whether the deletion was successful. Deleting a file may fail for various reasons, like the file being open, wrong file permissions etc."},{"_id":"53e5bf6f1609823bf90000c1","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207213,"position":6,"parentId":"53e58bca1609823bf90000ba","content":"#### Check if Path is File or Directory:\n\nA File object can point to both a file or a directory.\n\nYou can check if a File object points to a file or directory, by calling its isDirectory() method. This method returns true if the File points to a directory, and false if the File points to a file. Here is a simple example:\n\n<pre><code>File file = new File(\"c:\\\\data\");\n\nboolean isDirectory = file.isDirectory();</pre></code>\nRead List of Files in Directory\n\nYou can obtain a list of all the files in a directory by calling either the list() method or the listFiles() method. The list() method returns an array of String's with the file and / or directory names of directory the File object points to. The listFiles() returns an array of File objects representing the files and / or directories in the directory the File points to.\n\nHere is a simple example:\n\n<pre><code>File file = new File(\"c:\\\\data\");\n\nString[] fileNames = file.list();\n\nFile[] files = file.listFiles();</pre></code>"},{"_id":"53e5c0891609823bf90000c2","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207275,"position":2,"parentId":"53e58b131609823bf90000b9","content":"###Class: `java.io.Serializable`\nThe Serializable interface, to which files belong to, can be used to transform objects into files, and the other way around. This allows me to preserve state across different instances of my program."},{"_id":"53e5c73c1609823bf90000c3","treeId":"53a0fe6e4e52b1a71e00000f","seq":2262765,"position":1,"parentId":"53e5c0891609823bf90000c2","content":"#### Explanation: \n\nJava provides a mechanism, called object serialization where an object can be represented as a sequence of bytes that includes the object's data as well as information about the object's type and the types of data stored in the object.\n\nAfter a serialized object has been written into a file, it can be read from the file and deserialized that is, the type information and bytes that represent the object and its data can be used to recreate the object in memory.\n\nClasses `ObjectInputStream` and `ObjectOutputStream` are high-level streams that contain the methods for serializing and deserializing an object.\nMost impressive is that the entire process is JVM independent, meaning an object can be serialized on one platform and deserialized on an entirely different platform."},{"_id":"53e5d4cb1609823bf90000c7","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207259,"position":1.5,"parentId":"53e5c0891609823bf90000c2","content":"#### Instructions: \n\nThe ObjectOutputStream class contains many write methods for writing various data types, but one method in particular stands out:\n\n`public final void writeObject(Object x) throws IOException`\n\nThe above method serializes an Object and sends it to the output stream. Similarly, the `ObjectInputStream` class contains the following method for deserializing an object:\n\n<pre><code>\npublic final Object readObject() throws IOException, \n ClassNotFoundException</pre></code>\n\nThis method retrieves the next Object out of the stream and deserializes it. The return value is Object, so you will need to cast it to its appropriate data type.\n\nTo demonstrate how serialization works in Java, I am going to use the Employee class that we discussed early on in the book. Suppose that we have the following Employee class, which implements the Serializable interface:\n\n<pre><code>public class Employee implements java.io.Serializable\n{\n public String name;\n public String address;\n public transient int SSN;\n public int number;\n public void mailCheck()\n {\n System.out.println(\"Mailing a check to \" + name + \" \" + address);\n }\n}</pre></code>\n\nNotice that for a class to be serialized successfully, two conditions must be met:\n\nThe class must implement the `java.io.Serializable` interface.\n\nAll of the fields in the class must be serializable. If a field is not serializable, it must be marked transient.\n\nIf you are curious to know if a Java Standard Class is serializable or not, check the documentation for the class. The test is simple: If the class implements java.io.Serializable, then it is serializable; otherwise, it's not."},{"_id":"53e5ce801609823bf90000c4","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207241,"position":2,"parentId":"53e5c0891609823bf90000c2","content":"#### Serializing an Object:\nThe `ObjectOutputStream` class is used to serialize an Object. The following `SerializeDemo` program instantiates an Employee object and serializes it to a file.\n\nWhen the program is done executing, a file named `employee.ser` is created. The program does not generate any output, but study the code and try to determine what the program is doing.\n\n*Note: When serializing an object to a file, the standard convention in Java is to give the file a .ser extension.*\n\n<pre><code>import java.io.*;\n\npublic class SerializeDemo\n{\n public static void main(String [] args)\n {\n Employee e = new Employee();\n e.name = \"Reyan Ali\";\n e.address = \"Phokka Kuan, Ambehta Peer\";\n e.SSN = 11122333;\n e.number = 101;\n try\n {\n FileOutputStream fileOut =\n new FileOutputStream(\"/tmp/employee.ser\");\n ObjectOutputStream out = new ObjectOutputStream(fileOut);\n out.writeObject(e);\n out.close();\n fileOut.close();\n System.out.printf(\"Serialized data is saved in /tmp/employee.ser\");\n }catch(IOException i)\n {\n i.printStackTrace();\n }\n }\n}</pre></code>"},{"_id":"53e5d0431609823bf90000c5","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207245,"position":3,"parentId":"53e5c0891609823bf90000c2","content":"#### Deserializing an Object:\nThe following DeserializeDemo program deserializes the Employee object created in the SerializeDemo program. Study the program and try to determine its output:\n\n<pre><code>import java.io.*;\npublic class DeserializeDemo\n{\n public static void main(String [] args)\n {\n Employee e = null;\n try\n {\n FileInputStream fileIn = new FileInputStream(\"/tmp/employee.ser\");\n ObjectInputStream in = new ObjectInputStream(fileIn);\n e = (Employee) in.readObject();\n in.close();\n fileIn.close();\n }catch(IOException i)\n {\n i.printStackTrace();\n return;\n }catch(ClassNotFoundException c)\n {\n System.out.println(\"Employee class not found\");\n c.printStackTrace();\n return;\n }\n System.out.println(\"Deserialized Employee...\");\n System.out.println(\"Name: \" + e.name);\n System.out.println(\"Address: \" + e.address);\n System.out.println(\"SSN: \" + e.SSN);\n System.out.println(\"Number: \" + e.number);\n }\n}</pre></code>\nThis would produce the following result:\n\n<pre><code>Deserialized Employee...\nName: Reyan Ali\nAddress:Phokka Kuan, Ambehta Peer\nSSN: 0\nNumber:101</pre></code>"},{"_id":"53e5d2bc1609823bf90000c6","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207247,"position":4,"parentId":"53e5c0891609823bf90000c2","content":"#### Additional Comments & Observations\n\nThe try/catch block tries to catch a `ClassNotFoundException`, which is declared by the `readObject()` method. For a JVM to be able to deserialize an object, it must be able to find the bytecode for the class. If the JVM can't find a class during the deserialization of an object, it throws a `ClassNotFoundException`.\n\nNotice that the return value of `readObject()` is cast to an Employee reference.\n\nThe value of the SSN field was 11122333 when the object was serialized, but because the field is transient, this value was not sent to the output stream. The SSN field of the deserialized Employee object is 0."},{"_id":"53e5de531609823bf90000c9","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207276,"position":5,"parentId":"53e5c0891609823bf90000c2","content":"#### Comment: \n*you can generally ignore serialization time, except with the caveat that your serialization time cannot depend in any way on the total size of files that have been added, committed, etc.*"},{"_id":"53e5db871609823bf90000c8","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207279,"position":3,"parentId":"53e58b131609823bf90000b9","content":"### Thoughts and Personal Comments: "},{"_id":"53e5df5c1609823bf90000ca","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207285,"position":1,"parentId":"53e5db871609823bf90000c8","content":"#### Personal Comment: \nAs my serialization time cannot depend in any way on the total size of files in my commit tree, this means that I should obviously not serialize any of the files in my commit tree, but make sure to only have references to any files, and not have them ported into an internal data structure of mine. "},{"_id":"53e5e32a1609823bf90000cc","treeId":"53a0fe6e4e52b1a71e00000f","seq":2331681,"position":8.99609375,"parentId":null,"content":"## Data Structure and Approach:\n"},{"_id":"53e5e3841609823bf90000cd","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207319,"position":1,"parentId":"53e5e32a1609823bf90000cc","content":"### Necessary Data Structures"},{"_id":"53e5e4681609823bf90000ce","treeId":"53a0fe6e4e52b1a71e00000f","seq":2224494,"position":1,"parentId":"53e5e3841609823bf90000cd","content":"#### Field: `HashMap < String, Commit > branchMap` \nI need a way to store all of the open branches and associate them with their respective branch name. A HashMap seems to be most sane for that."},{"_id":"53e5ecc21609823bf90000cf","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207331,"position":2,"parentId":"53e5e3841609823bf90000cd","content":"#### Field: `HashMap < String, List< Commit>> commitMap`\nTo fulfill the linear speed requirement of `find ...` I will need a HashMap from commit messages to commits."},{"_id":"53e647341609823bf90000d8","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207401,"position":2.5,"parentId":"53e5e3841609823bf90000cd","content":"#### Field: `String currentCommit`\nSaves what commit is currently active, and which commit is synced with the current directory."},{"_id":"53e5f51e1609823bf90000d1","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207333,"position":3,"parentId":"53e5e3841609823bf90000cd","content":"#### Structure: `LinkedList`\nImplicit in my commit objects will be a LinkedList data structure."},{"_id":"540bb3ce9034670b06000114","treeId":"53a0fe6e4e52b1a71e00000f","seq":2225543,"position":4,"parentId":"53e5e3841609823bf90000cd","content":"#### **Field:** `HashMap< File, boolean> addedMap`\nMap to keep track off the files that are currently added. This gets emptied on checkout or commit."},{"_id":"540bedd29034670b06000116","treeId":"53a0fe6e4e52b1a71e00000f","seq":2225545,"position":5,"parentId":"53e5e3841609823bf90000cd","content":"#### **Field:** `HashMap< File, boolean> removeMap`\nMap to keep track off the files that are currently marked for removal. This gets emptied on checkout or commit. "},{"_id":"53e5efcd1609823bf90000d0","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207390,"position":2,"parentId":"53e5e32a1609823bf90000cc","content":"### Class: `Commit`\nI will need a commit class which captures the information of a single commit, and that implements my linked list Data Structure"},{"_id":"53e60f161609823bf90000d2","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207386,"position":1,"parentId":"53e5efcd1609823bf90000d0","content":"#### Field: `Integer ID`\n\nUnique integer id number"},{"_id":"53e624a01609823bf90000d3","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207397,"position":2,"parentId":"53e5efcd1609823bf90000d0","content":"#### Field: `String commitDate`\n\n**Comment on getting current time as String:**\n\n<pre><code>import java.text.DateFormat\nimport java.util.TimeZone\n...\nTimeZone tz = TimeZone.getDefault();\nDateFormat df = new SimpleDateFormat(\"yyyy-MM-dd'T'HH:mm'Z'\");\ndf.setTimeZone(tz);\nString nowAsISO = df.format(new Date());</pre></code>"},{"_id":"53e624e71609823bf90000d4","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207384,"position":3,"parentId":"53e5efcd1609823bf90000d0","content":"#### Field: `String message`\n\nMessage left by the user."},{"_id":"53e625901609823bf90000d5","treeId":"53a0fe6e4e52b1a71e00000f","seq":2207383,"position":4,"parentId":"53e5efcd1609823bf90000d0","content":"#### Field: `Commit parent`\n\nParent of the current commit"},{"_id":"53e64aa71609823bf90000da","treeId":"53a0fe6e4e52b1a71e00000f","seq":2224499,"position":5,"parentId":"53e5efcd1609823bf90000d0","content":"#### Field: `HashMap< Long, File> commitFiles`\nSaves the files in this commit, together with a hash of their contents, to check for collisions and modifications"}],"tree":{"_id":"53a0fe6e4e52b1a71e00000f","name":"Overview of Gitlet [Project 2 CS61B]","publicUrl":"overview-of-gitler-project-2-cs61b","latex":true}}