From 82f6ee8fa587f654a94a3e782aee0d49df7e336b Mon Sep 17 00:00:00 2001 From: Hunter Madden Date: Thu, 9 Oct 2025 08:03:49 -0700 Subject: [PATCH 1/4] GP-4.1 Implemented a tester for the makeIndexTree method, and amended makeIndexTree to return the string of the root hash. see README for more details --- Git.java | 3 ++- README.md | 9 ++++++++- Tester.java | 48 ++++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 54 insertions(+), 6 deletions(-) diff --git a/Git.java b/Git.java index f6a74a6..68c9434 100644 --- a/Git.java +++ b/Git.java @@ -307,7 +307,7 @@ public static String makeTree(String filePath, String fileName){ //returns hash //NOTE: Does not automatically BLOB everything inside of it! //That should have been done when indexed anyway. - public static void makeIndexTree(){ + public static String makeIndexTree(){ StringBuilder rootTreeContents = new StringBuilder(); String indexFile = readFile("git/objects", "index"); String[] entriesArr = indexFile.split("\n"); @@ -336,6 +336,7 @@ public static void makeIndexTree(){ String hash = hash(contents); makeFile(null, hash); writeToFile(null, hash, contents); + return hash; } //returns tree hash diff --git a/README.md b/README.md index cd48774..6f04725 100644 --- a/README.md +++ b/README.md @@ -84,4 +84,11 @@ decompress: takes a compressed String and returns the original GP 3.2 -makeTree: from file path, recursively creates tree by makign subtrees and blobifying subfiles. returns the name of the hash! \ No newline at end of file +makeTree: from file path, recursively creates tree by makign subtrees and blobifying subfiles. returns the name of the hash! + +-- + +GP 4.1 + +makeIndexTree now returns the hash of the root tree +ʕ●.●ʔ \ No newline at end of file diff --git a/Tester.java b/Tester.java index bb5ea45..668d9c9 100644 --- a/Tester.java +++ b/Tester.java @@ -215,12 +215,52 @@ public static void indexTester(){ Git.deleteDirectory(null, "testFiles"); System.out.println("PASSED"); } + public static void indexTreeTester(){ + System.out.println("Index Tree Test"); + //make a bunch of files and folders to test different combinations of files and folders + Git.intializeRepo(); + Git.makeDirectory(null, "indexTreeTest"); + Git.makeDirectory("indexTreeTest", "firstLayer"); + Git.makeDirectory("firstLayer", "secondLayer"); + Git.makeDirectory("indexTreeTest", "altLayer"); + Git.makeFile("indexTreeTest", "testFile1"); + Git.blobify("indexTreetest", "testFile1"); + Git.index("indexTreeTest", "testFile1"); + Git.makeFile("indexTreeTest", "testFile2"); + Git.blobify("indexTreetest", "testFile2"); + Git.index("indexTreeTest", "testFile2"); + Git.makeFile("indexTreeTest/firstLayer", "testFile3"); + Git.blobify("indexTreetest/firstLayer", "testFile3"); + Git.index("indexTreeTest/firstLayer", "testFile3"); + Git.makeFile("indexTreeTest/firstlayer/secondLayer", "testFile4"); + Git.blobify("indexTreeTest/firstlayer/secondLayer", "testFile4"); + Git.index("indexTreeTest/firstlayer/secondLayer", "testFile4"); + Git.makeFile("indexTreeTest/altLayer", "testFile5"); + Git.blobify("indexTreeTest/altLayer", "testFile5"); + Git.index("indexTreeTest/altLayer", "testFile5"); + recursiveTreeTest(Git.makeIndexTree()); + } + public static void recursiveTreeTest(String treePath){ + String contents = Git.readFile("git/objects", treePath); + String [] contentsArr = contents.split("\n"); + for(String e : contentsArr){ + if(e.split(" ")[1].equals("tree")){ + recursiveTreeTest(e.split(" ")[2]); + } + else{ + if(Git.fileExists("git/objects", e.split(" ")[2])){ + System.out.println("File "+e.split(" ")[3]+" correctly blobbed, indexed, and treed"); + } + } + } + } public static void main(String[] args){ - initializationTester(); - hashTester(); - blobTester(); - indexTester(); + // initializationTester(); + // hashTester(); + // blobTester(); + // indexTester(); + indexTreeTester(); Git.cleanGit(); } } From b0c29073fc413bc8852ee359bbe27ddd9306f4bb Mon Sep 17 00:00:00 2001 From: Hunter Madden Date: Thu, 9 Oct 2025 09:10:34 -0700 Subject: [PATCH 2/4] GP-4.2 Link Commit File to Root new methods makeCommit and retrieveLastCommit. see README for more details --- Git.java | 32 ++++++++++++++++++++++++++++---- README.md | 8 +++++++- 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/Git.java b/Git.java index 68c9434..5ec620b 100644 --- a/Git.java +++ b/Git.java @@ -1,13 +1,15 @@ import java.io.*; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; -import java.util.Stack; +import java.util.*; import java.util.ArrayList; import java.util.Base64; import java.util.Comparator; import java.util.HashSet; import java.util.zip.Deflater; import java.util.zip.Inflater; +import java.time.*; +import java.time.format.*; //LOOK AT TOP OF README FOR AN IMPORTANT EXPLANATION! @@ -38,7 +40,10 @@ public static void makeDirectory(String filePath, String folderName){ File newDir = new File(path); newDir.mkdir(); } - + //IF THE HEAD EVER HAS STUFF OTHER THAN THE LATEST COMMIT, EDIT THIS METHOD + public static String retrieveLatestCommit(){ + return readFile("git", "HEAD"); + } private static void deleteDirectory(File file) { for (File subfile : file.listFiles()) { @@ -373,7 +378,26 @@ public static String makeIndexTreeHelper(ArrayList entries, String direc writeToFile("git/objects", treeHash, entryContent); return treeHash; } - + public static String makeCommit(){ + StringBuilder outputSB = new StringBuilder(); + outputSB.append("tree: "+makeIndexTree()+"\n"); + outputSB.append("parent: "+retrieveLatestCommit()+"\n"); + Scanner sc = new Scanner(System.in); + System.out.println("Enter author name:"); + outputSB.append("author: "+sc.nextLine()+"\n"); + sc.reset(); + //i hope geeksForGeeks was right about what this code does + outputSB.append("date: "+ LocalDate.now().format(DateTimeFormatter.ofPattern("dd-MM-yyyy HH:mm:ss")) + "\n"); + System.out.println("Enter commit message:"); + outputSB.append("message: "+sc.nextLine()); + sc.close(); + String commitHash = hash(outputSB.toString()); + makeFile("git/objects", commitHash); + writeToFile("git/objects", commitHash, outputSB.toString()); + // change this if more info needs to be stored on the HEAD + writeToFile("git", "HEAD", commitHash); + return commitHash; + } public static void main(String[] args){ cleanGit(); intializeRepo(); @@ -382,6 +406,6 @@ public static void main(String[] args){ index("A/C", "f4"); index("A", "f1"); index(null, ".gitignore"); - makeIndexTree(); + makeIndexTree(); } } \ No newline at end of file diff --git a/README.md b/README.md index 6f04725..e971976 100644 --- a/README.md +++ b/README.md @@ -91,4 +91,10 @@ makeTree: from file path, recursively creates tree by makign subtrees and blobif GP 4.1 makeIndexTree now returns the hash of the root tree -ʕ●.●ʔ \ No newline at end of file + +-- + +GP 4.2 + +makeCommit: Generates a new commit file by calling makeIndexTree to make a new root hash and prompting the user for the author name and commit message. appends the HEAD with the commit's hash +retriveLastCommit: reads the HEAD (which shoudl have the most recent commit hash) CHANGE THIS IF ANY OTHER STUFF IS IN THE HEAD FILE \ No newline at end of file From 22e3432e8708984b996876253460b52ef07b3734 Mon Sep 17 00:00:00 2001 From: Hunter Madden Date: Sun, 12 Oct 2025 14:27:32 -0700 Subject: [PATCH 3/4] GP-4.3 Implement a Wrapper New wrapper class for Git that mainly-reimplements previous methods see README for more details. --- GitWrapper.java | 80 +++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 11 ++++++- 2 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 GitWrapper.java diff --git a/GitWrapper.java b/GitWrapper.java new file mode 100644 index 0000000..d033b1c --- /dev/null +++ b/GitWrapper.java @@ -0,0 +1,80 @@ +import java.nio.file.Paths; +import java.time.*; +import java.time.format.*; +public class GitWrapper { + /** + * Initializes a new Git repository. + * This method creates the necessary directory structure + * and initial files (index, HEAD) required for a Git repository. + */ + public void init() { + Git.intializeRepo(); + }; + + /** + * Stages a file for the next commit. + * This method adds a file to the index file. + * If the file does not exist, it throws an IOException. + * If the file is a directory, it throws an IOException. + * If the file is already in the index, it does nothing. + * If the file is successfully staged, it creates a blob for the file. + * @param filePath The path to the file to be staged. + */ + public void add(String filePath) { + //Shimone, this is what you get when you split the parent path, and file name + String parentPath = Paths.get(filePath).getParent().toString(); + String childPath = Paths.get(filePath).getFileName().toString(); + Git.index(parentPath, childPath); + Git.blobify(parentPath, childPath); + }; + + /** + * Creates a commit with the given author and message. + * It should capture the current state of the repository by building trees based on the index file, + * writing the tree to the objects directory, + * writing the commit to the objects directory, + * updating the HEAD file, + * and returning the commit hash. + * + * The commit should be formatted as follows: + * tree: + * parent: + * author: + * date: + * summary: + * + * @param author The name of the author making the commit. + * @param message The commit message describing the changes. + * @return The SHA1 hash of the new commit. + */ + public String commit(String author, String message) { + //this is literally a ctrl c + ctrl v without the scanner + StringBuilder outputSB = new StringBuilder(); + outputSB.append("tree: "+Git.makeIndexTree()+"\n"); + outputSB.append("parent: "+Git.retrieveLatestCommit()+"\n"); + outputSB.append("author: "+author+"\n"); + outputSB.append("date: "+ LocalDate.now().format(DateTimeFormatter.ofPattern("dd-MM-yyyy HH:mm:ss")) + "\n"); + outputSB.append("message: "+message); + String commitHash = Git.hash(outputSB.toString()); + Git.makeFile("git/objects", commitHash); + Git.writeToFile("git/objects", commitHash, outputSB.toString()); + // change this if more info needs to be stored on the HEAD + Git.writeToFile("git", "HEAD", commitHash); + return commitHash; + }; + + /** + * EXTRA CREDIT: + * Checks out a specific commit given its hash. + * This method should read the HEAD file to determine the "checked out" commit. + * Then it should update the working directory to match the + * state of the repository at that commit by tracing through the root tree and + * all its children. + * + * @param commitHash The SHA1 hash of the commit to check out. + */ + public void checkout(String commitHash) { + // to-do: implement functionality here + + }; +} \ No newline at end of file diff --git a/README.md b/README.md index e971976..d723b98 100644 --- a/README.md +++ b/README.md @@ -97,4 +97,13 @@ makeIndexTree now returns the hash of the root tree GP 4.2 makeCommit: Generates a new commit file by calling makeIndexTree to make a new root hash and prompting the user for the author name and commit message. appends the HEAD with the commit's hash -retriveLastCommit: reads the HEAD (which shoudl have the most recent commit hash) CHANGE THIS IF ANY OTHER STUFF IS IN THE HEAD FILE \ No newline at end of file +retriveLastCommit: reads the HEAD (which shoudl have the most recent commit hash) CHANGE THIS IF ANY OTHER STUFF IS IN THE HEAD FILE + +-- + +GP 4.3 +GitWrapper - new wrapper class for Git +init: calls Git.initRepo +add: indexes and blobs the filepath given +commit: copy of Git.makeCommit without the scanner +checkout: WIP (I'm not quite sure how it's supposed to work) \ No newline at end of file From 5f4a443b3b6820d99ba82454946bd65fb77f8956 Mon Sep 17 00:00:00 2001 From: Hunter Madden Date: Sun, 12 Oct 2025 21:32:31 -0700 Subject: [PATCH 4/4] Final Touches Wrapper tester added. Various bugfixes. see README for more details. --- .gitignore | 1 + Git.java | 12 +++++++----- GitWrapper.java | 4 +++- README.md | 11 ++++++++++- Tester.java | 46 ++++++++++++++++++++++++++++++++++------------ 5 files changed, 55 insertions(+), 19 deletions(-) diff --git a/.gitignore b/.gitignore index 2ad6453..0d7fbd2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /git .DS_Store +*.txt \ No newline at end of file diff --git a/Git.java b/Git.java index 5ec620b..1dfccab 100644 --- a/Git.java +++ b/Git.java @@ -168,9 +168,9 @@ && directoryExists("git", "objects") && directoryExists(null, "git")){ deleteDirectory("git", "index"); //in case "cloggs" options makeFile("git/objects", "index"); - + //FIXED: THE HEAD FILE ISN'T SUPPOSED TO BE IN THE OBEJCTS FOLDER deleteDirectory("git", "HEAD"); //in case "cloggs" options - makeFile("git/objects", "HEAD"); + makeFile("git", "HEAD"); } public static void blobify(String filePath, String fileName){ @@ -339,8 +339,8 @@ public static String makeIndexTree(){ } String contents = rootTreeContents.toString(); String hash = hash(contents); - makeFile(null, hash); - writeToFile(null, hash, contents); + makeFile("git/objects", hash); + writeToFile("git/objects", hash, contents); return hash; } @@ -387,7 +387,9 @@ public static String makeCommit(){ outputSB.append("author: "+sc.nextLine()+"\n"); sc.reset(); //i hope geeksForGeeks was right about what this code does - outputSB.append("date: "+ LocalDate.now().format(DateTimeFormatter.ofPattern("dd-MM-yyyy HH:mm:ss")) + "\n"); + LocalDateTime localDateTime = LocalDateTime.now(); + DateTimeFormatter validDateTimeFormatter = DateTimeFormatter.ofPattern("dd-MM-yyyy HH:mm:ss"); + outputSB.append("date: "+ localDateTime.format(validDateTimeFormatter) + "\n"); System.out.println("Enter commit message:"); outputSB.append("message: "+sc.nextLine()); sc.close(); diff --git a/GitWrapper.java b/GitWrapper.java index d033b1c..c974b22 100644 --- a/GitWrapper.java +++ b/GitWrapper.java @@ -53,7 +53,9 @@ public String commit(String author, String message) { outputSB.append("tree: "+Git.makeIndexTree()+"\n"); outputSB.append("parent: "+Git.retrieveLatestCommit()+"\n"); outputSB.append("author: "+author+"\n"); - outputSB.append("date: "+ LocalDate.now().format(DateTimeFormatter.ofPattern("dd-MM-yyyy HH:mm:ss")) + "\n"); + LocalDateTime localDateTime = LocalDateTime.now(); + DateTimeFormatter validDateTimeFormatter = DateTimeFormatter.ofPattern("dd-MM-yyyy HH:mm:ss"); + outputSB.append("date: "+ localDateTime.format(validDateTimeFormatter) + "\n"); outputSB.append("message: "+message); String commitHash = Git.hash(outputSB.toString()); Git.makeFile("git/objects", commitHash); diff --git a/README.md b/README.md index d723b98..50f765a 100644 --- a/README.md +++ b/README.md @@ -102,8 +102,17 @@ retriveLastCommit: reads the HEAD (which shoudl have the most recent commit hash -- GP 4.3 + GitWrapper - new wrapper class for Git init: calls Git.initRepo add: indexes and blobs the filepath given commit: copy of Git.makeCommit without the scanner -checkout: WIP (I'm not quite sure how it's supposed to work) \ No newline at end of file +checkout: WIP (I'm not quite sure how it's supposed to work) + +-- + +Final Testing + Extras + +FIXED: HEAD is no longer in the objects folder +FIXED: Date no longer throws exceptions when making commits +full wraper test implemented \ No newline at end of file diff --git a/Tester.java b/Tester.java index 668d9c9..fc65b74 100644 --- a/Tester.java +++ b/Tester.java @@ -219,27 +219,30 @@ public static void indexTreeTester(){ System.out.println("Index Tree Test"); //make a bunch of files and folders to test different combinations of files and folders Git.intializeRepo(); - Git.makeDirectory(null, "indexTreeTest"); - Git.makeDirectory("indexTreeTest", "firstLayer"); - Git.makeDirectory("firstLayer", "secondLayer"); - Git.makeDirectory("indexTreeTest", "altLayer"); - Git.makeFile("indexTreeTest", "testFile1"); + makeLargeFileSystem(); Git.blobify("indexTreetest", "testFile1"); Git.index("indexTreeTest", "testFile1"); - Git.makeFile("indexTreeTest", "testFile2"); Git.blobify("indexTreetest", "testFile2"); Git.index("indexTreeTest", "testFile2"); - Git.makeFile("indexTreeTest/firstLayer", "testFile3"); Git.blobify("indexTreetest/firstLayer", "testFile3"); Git.index("indexTreeTest/firstLayer", "testFile3"); - Git.makeFile("indexTreeTest/firstlayer/secondLayer", "testFile4"); Git.blobify("indexTreeTest/firstlayer/secondLayer", "testFile4"); Git.index("indexTreeTest/firstlayer/secondLayer", "testFile4"); - Git.makeFile("indexTreeTest/altLayer", "testFile5"); Git.blobify("indexTreeTest/altLayer", "testFile5"); Git.index("indexTreeTest/altLayer", "testFile5"); recursiveTreeTest(Git.makeIndexTree()); } + public static void makeLargeFileSystem(){ + Git.makeDirectory(null, "indexTreeTest"); + Git.makeDirectory("indexTreeTest", "firstLayer"); + Git.makeDirectory("indexTreeTest/firstLayer", "secondLayer"); + Git.makeDirectory("indexTreeTest", "altLayer"); + Git.makeFile("indexTreeTest", "testFile1"); + Git.makeFile("indexTreeTest", "testFile2"); + Git.makeFile("indexTreeTest/firstLayer", "testFile3"); + Git.makeFile("indexTreeTest/firstLayer/secondLayer", "testFile4"); + Git.makeFile("indexTreeTest/altLayer", "testFile5"); + } public static void recursiveTreeTest(String treePath){ String contents = Git.readFile("git/objects", treePath); String [] contentsArr = contents.split("\n"); @@ -254,13 +257,32 @@ public static void recursiveTreeTest(String treePath){ } } } - + public static void gitWrapperTest(){ + GitWrapper git = new GitWrapper(); + System.out.println("Full Wrapper Test Started"); + System.out.println("Testing Repo Initialization"); + git.init(); + if (Git.fileExists("git/objects", "index") && Git.fileExists("git/objects", "HEAD") + && Git.directoryExists("git", "objects") && Git.directoryExists(null, "git")){ + System.out.println("Repo initialization sucess"); + } + makeLargeFileSystem(); + System.out.println("File System created"); + git.add("indexTreeTest/testFile1"); + git.add("indexTreeTest/testFile2"); + git.add("indexTreeTest/firstLayer/testFile3"); + git.add("indexTreeTest/firstLayer/secondLayer/testFile4"); + System.out.println("File adding sucessful"); + String commithash = git.commit("Hunter Madden", "THIS IS A TEST"); + System.out.println("COMMIT EXISTS: " + Git.fileExists("git/objects", commithash)); + } public static void main(String[] args){ // initializationTester(); // hashTester(); // blobTester(); // indexTester(); - indexTreeTester(); - Git.cleanGit(); + // indexTreeTester(); + gitWrapperTest(); + //Git.cleanGit(); } }