diff --git a/GitHash.class b/GitHash.class new file mode 100644 index 0000000..a52c60a Binary files /dev/null and b/GitHash.class differ diff --git a/GitHash.java b/GitHash.java index 75aec7c..4e4d972 100644 --- a/GitHash.java +++ b/GitHash.java @@ -8,20 +8,26 @@ import java.nio.file.Files; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Comparator; +import java.util.Date; import java.util.PriorityQueue; public class GitHash { - // Create directories - public static void gitRepoInit() throws IOException { + public static void init() throws IOException { GitHash.createDirectoryIfMissing("git"); GitHash.createDirectoryIfMissing("git/objects"); GitHash.createFileIfMissing("git/HEAD"); GitHash.createFileIfMissing("git/INDEX"); + } + + // Create directories + public static void gitRepoInit() throws IOException { + init(); System.out.println("Git Repository Created"); } @@ -50,8 +56,34 @@ public static void createFileIfMissing(String path) throws IOException { public static void add(String filePath) throws IOException { File f = new File(filePath); - createBLOBAndAddToObjects(f); - addBLOBEntryToIndex(filePath); + if (!f.exists()) { + throw new IOException("File does not exist: " + filePath); + } + if (f.isDirectory()) { + throw new IOException("Cannot add directory: " + filePath); + } + + if (!isFileInIndex(filePath)) { + createBLOBAndAddToObjects(f); + addBLOBEntryToIndex(filePath); + } + } + + public static boolean isFileInIndex(String filePath) throws IOException { + File indexFile = new File("git/INDEX"); + if (!indexFile.exists()) { + return false; + } + BufferedReader br = new BufferedReader(new FileReader(indexFile)); + String line; + while ((line = br.readLine()) != null) { + if (line.contains(" ./" + filePath)) { + br.close(); + return true; + } + } + br.close(); + return false; } public static String generateSHA1FromString(String contents) throws IOException { @@ -190,19 +222,24 @@ public static PriorityQueue convertIndexToWorkingDirectory // Goes through dirs in priority order (as mentioned above) and converts the contents into object public static void convertNestedDirs(PriorityQueue wd) throws IOException { - if (wd.peek().size() != -1) { + if (!wd.isEmpty()) { ArrayList nestedFolders = new ArrayList<>(); nestedFolders.add(wd.poll()); WorkingDirectoryInfo currFolder = nestedFolders.get(0); - while (wd.peek() != null && wd.peek().isInSameFolder(currFolder)) { + while (!wd.isEmpty() && wd.peek().isInSameFolder(currFolder)) { nestedFolders.add(wd.poll()); } - String hash = writeTreeFile(convertContentsToString(nestedFolders)); - wd.add(new WorkingDirectoryInfo("tree", hash, currFolder.getStringPath())); - convertNestedDirs(wd); + if (currFolder.size() == -1) { + writeWorkingListFile(convertContentsToString(nestedFolders)); + return; + } else { + String hash = writeTreeFile(convertContentsToString(nestedFolders)); + wd.add(new WorkingDirectoryInfo("tree", hash, currFolder.getStringPath())); + convertNestedDirs(wd); + } } } @@ -278,4 +315,53 @@ public static String writeTreeFile(String contents) throws IOException { } + // writes the final working list to a file named "workingList" + public static void writeWorkingListFile(String contents) throws IOException { + File workingListFile = new File("./git/objects/workingList"); + BufferedWriter bw = new BufferedWriter(new FileWriter(workingListFile)); + bw.write(contents); + bw.close(); + } + + // creates a commit file with author, message, root tree hash, and previous commit + public static String commit(String author, String message) throws IOException { + stageFolders(); + BufferedReader br = new BufferedReader(new FileReader("./git/objects/workingList")); + String rootTreeContent = br.readLine(); + br.close(); + String rootTreeHash = generateSHA1FromString(rootTreeContent); + String previousCommit = ""; + File headFile = new File("./git/HEAD"); + if (headFile.exists() && headFile.length() > 0) { + BufferedReader headReader = new BufferedReader(new FileReader(headFile)); + String headContent = headReader.readLine(); + headReader.close(); + if (headContent != null && !headContent.trim().isEmpty()) { + File previousCommitFile = new File("./git/objects/" + headContent.trim()); + if (previousCommitFile.exists()) { + previousCommit = headContent.trim(); + } + } + } + + // https://docs.oracle.com/javase/8/docs/api/java/text/SimpleDateFormat.html + SimpleDateFormat sdf = new SimpleDateFormat("EEE MMM dd HH:mm:ss z yyyy"); + String currentDate = sdf.format(new Date()); + StringBuilder commitContent = new StringBuilder(); + commitContent.append("tree: ").append(rootTreeHash).append("\n"); + commitContent.append("parent: ").append(previousCommit).append("\n"); + commitContent.append("author: ").append(author).append("\n"); + commitContent.append("date: ").append(currentDate).append("\n"); + commitContent.append("summary: ").append(message); + String commitHash = generateSHA1FromString(commitContent.toString()); + File commitFile = new File("./git/objects/" + commitHash); + BufferedWriter commitWriter = new BufferedWriter(new FileWriter(commitFile)); + commitWriter.write(commitContent.toString()); + commitWriter.close(); + BufferedWriter headWriter = new BufferedWriter(new FileWriter("./git/HEAD")); + headWriter.write(commitHash); + headWriter.close(); + return commitHash; + } + } diff --git a/GitHashTester.class b/GitHashTester.class new file mode 100644 index 0000000..de08edf Binary files /dev/null and b/GitHashTester.class differ diff --git a/GitHashTester.java b/GitHashTester.java index 9d4a116..ba2c36e 100644 --- a/GitHashTester.java +++ b/GitHashTester.java @@ -11,6 +11,7 @@ public static void main(String[] args) throws IOException { GitHash.add("goofy_ahh/BEANS/dawg.txt"); GitHash.add("goofy_ahh/goofy_nahhh/snog/dog.txt"); GitHash.stageFolders(); + GitHash.commit("Samuel Bernardo", "Initial commit with test files"); // PriorityQueue q = GitHash.convertIndexToWorkingDirectory(); // System.out.println(q.poll().size()); diff --git a/GitTester.class b/GitTester.class new file mode 100644 index 0000000..22a559c Binary files /dev/null and b/GitTester.class differ diff --git a/GitTester.java b/GitTester.java new file mode 100644 index 0000000..322b7e1 --- /dev/null +++ b/GitTester.java @@ -0,0 +1,24 @@ +import java.io.IOException; + +public class GitTester { + + public static void main(String args[]) throws IOException { + + GitHash.deleteGitDirectory(); + + GitWrapper gw = new GitWrapper(); + gw.init(); + + gw.add("myProgram/hello.txt"); + gw.add("myProgram/inner/world.txt"); + + String commitHash = gw.commit("John Doe", "Initial commit"); + System.out.println("Commit created with hash: " + commitHash); + + gw.add("myProgram/hello.txt"); + System.out.println("Duplicate add attempt - should not add again"); + + System.out.println("Git functionality test completed"); + } + +} \ No newline at end of file diff --git a/GitWrapper.class b/GitWrapper.class new file mode 100644 index 0000000..61180f6 Binary files /dev/null and b/GitWrapper.class differ diff --git a/GitWrapper.java b/GitWrapper.java new file mode 100644 index 0000000..d4f1a76 --- /dev/null +++ b/GitWrapper.java @@ -0,0 +1,64 @@ +import java.io.IOException; + +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() throws IOException { + GitHash.init(); + } + + /** + * 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) throws IOException { + GitHash.add(filePath); + } + + /** + * 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) throws IOException { + return GitHash.commit(author, message); + } + + /** + * 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 f33e26e..3e1970b 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,9 @@ I meant working list here but I'm too lazy to change it now. Creates a priority **convertNestedDirs()** Using a priority queue of sorted based on file depths, adds folders in the same path to an arraylist and then converts all those folders into a tree object. repeats until Priority Queue is used up and all folders have been stored. +**Max's Note About Tree Functionality** +When running the tester prior to my fixes, it would create trees for all files but not make a final "Working List" with the root tree and its hash in it. Fixed now + **convertContentsToString()** takes tree info (generally longer form info that has different lines / multiple data poibts) and turns into a string @@ -95,6 +98,9 @@ stages ALL CONTENTS inside a folder NOT RELATED TO INDEX as trees in objects fol creates a filename based on hashed contents and then writes those contents into the a file in the objects folder. +**Commit Functionality** + +commit method in GitHash.java takes in the author of the commit and the commit message as strings. When called, it makes an up to date working list of the directory, and writes it to a file named as the hash of the commit file contents. The most recent commit is stored in the HEAD file, and each commit includes the previous commit's hash (so it can be found in the objects folder), leading up to the initial commit. --- (Sam's) NOTES TO SELF: diff --git a/WorkingDirectoryInfo.class b/WorkingDirectoryInfo.class new file mode 100644 index 0000000..db532dc Binary files /dev/null and b/WorkingDirectoryInfo.class differ diff --git a/git/HEAD b/git/HEAD index e69de29..b82ef5e 100644 --- a/git/HEAD +++ b/git/HEAD @@ -0,0 +1 @@ +5cd696bcdcde35f53a3731745d10f3e46ac1370d \ No newline at end of file diff --git a/git/INDEX b/git/INDEX index e69de29..11ba5c9 100644 --- a/git/INDEX +++ b/git/INDEX @@ -0,0 +1,2 @@ +ea7b8499509da0261a19e48a8631a6a506f0de0a ./myProgram/hello.txt +b245828b90464cecb0c07ee6e61491ee2dc0c00e ./myProgram/inner/world.txt diff --git a/git/objects/008eee1073f2cfbdac1be079e6bbea7037832695 b/git/objects/008eee1073f2cfbdac1be079e6bbea7037832695 new file mode 100644 index 0000000..e516b5a --- /dev/null +++ b/git/objects/008eee1073f2cfbdac1be079e6bbea7037832695 @@ -0,0 +1 @@ +blob 70260c5719d16e1f1d7ce2168377eb52a32d438f dawg.txt \ No newline at end of file diff --git a/git/objects/0bc2179e8827414a3bc71984585b4a9044945f4d b/git/objects/0bc2179e8827414a3bc71984585b4a9044945f4d new file mode 100644 index 0000000..e5eca2b --- /dev/null +++ b/git/objects/0bc2179e8827414a3bc71984585b4a9044945f4d @@ -0,0 +1,3 @@ +tree 179acc793851176faa924288cb66af6cf9f99256 goofy_nahhh +tree 008eee1073f2cfbdac1be079e6bbea7037832695 BEANS +blob 70260c5719d16e1f1d7ce2168377eb52a32d438f wow.txt \ No newline at end of file diff --git a/git/objects/179acc793851176faa924288cb66af6cf9f99256 b/git/objects/179acc793851176faa924288cb66af6cf9f99256 new file mode 100644 index 0000000..960a6d0 --- /dev/null +++ b/git/objects/179acc793851176faa924288cb66af6cf9f99256 @@ -0,0 +1 @@ +tree a91e5e5ee24e1cf52367bded740c0424a4b29cd7 snog \ No newline at end of file diff --git a/git/objects/5cd696bcdcde35f53a3731745d10f3e46ac1370d b/git/objects/5cd696bcdcde35f53a3731745d10f3e46ac1370d new file mode 100644 index 0000000..93887e9 --- /dev/null +++ b/git/objects/5cd696bcdcde35f53a3731745d10f3e46ac1370d @@ -0,0 +1,5 @@ +tree: d8259ac2dd220dfb92c86eb978592c0661467d07 +parent: +author: John Doe +date: Sun Oct 12 07:13:21 PDT 2025 +summary: Initial commit \ No newline at end of file diff --git a/git/objects/70260c5719d16e1f1d7ce2168377eb52a32d438f b/git/objects/70260c5719d16e1f1d7ce2168377eb52a32d438f new file mode 100644 index 0000000..d670460 --- /dev/null +++ b/git/objects/70260c5719d16e1f1d7ce2168377eb52a32d438f @@ -0,0 +1 @@ +test content diff --git a/git/objects/76f1f0fed543c6cf133d919e540df11e0cbbcbed b/git/objects/76f1f0fed543c6cf133d919e540df11e0cbbcbed new file mode 100644 index 0000000..37e7663 --- /dev/null +++ b/git/objects/76f1f0fed543c6cf133d919e540df11e0cbbcbed @@ -0,0 +1,5 @@ +tree: d8259ac2dd220dfb92c86eb978592c0661467d07 +parent: +author: John Doe +date: Sun Oct 12 07:08:18 PDT 2025 +summary: Initial commit \ No newline at end of file diff --git a/git/objects/a2fd0600daef89a553a9f4489c72b8bcc069e975 b/git/objects/a2fd0600daef89a553a9f4489c72b8bcc069e975 new file mode 100644 index 0000000..3ab03a2 --- /dev/null +++ b/git/objects/a2fd0600daef89a553a9f4489c72b8bcc069e975 @@ -0,0 +1,5 @@ +tree: 9e00765445c7f8a55abf47df54880d9c454fe8ca +parent: +author: Samuel Bernardo +date: Sun Oct 12 06:59:32 PDT 2025 +message: Initial commit with test files \ No newline at end of file diff --git a/git/objects/a91e5e5ee24e1cf52367bded740c0424a4b29cd7 b/git/objects/a91e5e5ee24e1cf52367bded740c0424a4b29cd7 new file mode 100644 index 0000000..bb2e52f --- /dev/null +++ b/git/objects/a91e5e5ee24e1cf52367bded740c0424a4b29cd7 @@ -0,0 +1 @@ +blob 70260c5719d16e1f1d7ce2168377eb52a32d438f dog.txt \ No newline at end of file diff --git a/git/objects/b245828b90464cecb0c07ee6e61491ee2dc0c00e b/git/objects/b245828b90464cecb0c07ee6e61491ee2dc0c00e new file mode 100644 index 0000000..13138cd --- /dev/null +++ b/git/objects/b245828b90464cecb0c07ee6e61491ee2dc0c00e @@ -0,0 +1 @@ +Inner World! diff --git a/git/objects/b29b66f5a083ef031a56ebb437e7f58f9b664744 b/git/objects/b29b66f5a083ef031a56ebb437e7f58f9b664744 new file mode 100644 index 0000000..1df4a3d --- /dev/null +++ b/git/objects/b29b66f5a083ef031a56ebb437e7f58f9b664744 @@ -0,0 +1 @@ +blob b245828b90464cecb0c07ee6e61491ee2dc0c00e world.txt \ No newline at end of file diff --git a/git/objects/b34d976be64c07a6de9824d1ede86e66d1184112 b/git/objects/b34d976be64c07a6de9824d1ede86e66d1184112 new file mode 100644 index 0000000..c67444e --- /dev/null +++ b/git/objects/b34d976be64c07a6de9824d1ede86e66d1184112 @@ -0,0 +1,2 @@ +blob ea7b8499509da0261a19e48a8631a6a506f0de0a hello.txt +tree b29b66f5a083ef031a56ebb437e7f58f9b664744 inner \ No newline at end of file diff --git a/git/objects/ea7b8499509da0261a19e48a8631a6a506f0de0a b/git/objects/ea7b8499509da0261a19e48a8631a6a506f0de0a new file mode 100644 index 0000000..980a0d5 --- /dev/null +++ b/git/objects/ea7b8499509da0261a19e48a8631a6a506f0de0a @@ -0,0 +1 @@ +Hello World! diff --git a/git/objects/workingList b/git/objects/workingList new file mode 100644 index 0000000..992f01d --- /dev/null +++ b/git/objects/workingList @@ -0,0 +1 @@ +tree b34d976be64c07a6de9824d1ede86e66d1184112 myProgram \ No newline at end of file diff --git a/goofy_ahh/BEANS/dawg.txt b/goofy_ahh/BEANS/dawg.txt new file mode 100644 index 0000000..d670460 --- /dev/null +++ b/goofy_ahh/BEANS/dawg.txt @@ -0,0 +1 @@ +test content diff --git a/goofy_ahh/goofy_nahhh/snog/dog.txt b/goofy_ahh/goofy_nahhh/snog/dog.txt new file mode 100644 index 0000000..d670460 --- /dev/null +++ b/goofy_ahh/goofy_nahhh/snog/dog.txt @@ -0,0 +1 @@ +test content diff --git a/goofy_ahh/wow.txt b/goofy_ahh/wow.txt new file mode 100644 index 0000000..d670460 --- /dev/null +++ b/goofy_ahh/wow.txt @@ -0,0 +1 @@ +test content diff --git a/myProgram/hello.txt b/myProgram/hello.txt new file mode 100644 index 0000000..980a0d5 --- /dev/null +++ b/myProgram/hello.txt @@ -0,0 +1 @@ +Hello World! diff --git a/myProgram/inner/world.txt b/myProgram/inner/world.txt new file mode 100644 index 0000000..13138cd --- /dev/null +++ b/myProgram/inner/world.txt @@ -0,0 +1 @@ +Inner World! diff --git a/temp b/temp new file mode 100644 index 0000000..93887e9 --- /dev/null +++ b/temp @@ -0,0 +1,5 @@ +tree: d8259ac2dd220dfb92c86eb978592c0661467d07 +parent: +author: John Doe +date: Sun Oct 12 07:13:21 PDT 2025 +summary: Initial commit \ No newline at end of file