Skip to content

Instantly share code, notes, and snippets.

@ayah527
Created May 8, 2021
Embed
What would you like to do?
package gitlet;
import java.io.File;
import java.io.Serializable;
import static gitlet.Repo.CWD;
import static gitlet.Repo.BLOB_FOLDER;
/**
* This class stores the filename and file byte array in each blob.
* @author Ayah Abushama, Brian Laus, Mourad Zeynalov
*/
public class Blob implements Serializable {
/** Stores name of file. */
private String fileName;
/** Stores contents of file. */
private byte[] blobContents;
/**Blob constructor.*/
public Blob(String name) {
fileName = name;
File inCWD = Utils.join(CWD, name);
if (!(inCWD.exists())) {
System.out.println("File does not exist.");
System.exit(0);
}
blobContents = Utils.readContents(inCWD);
}
public String getName() {
return fileName;
}
public byte[] getBlobContents() {
return blobContents;
}
public static Blob fromBlobFolder(String sha) {
File blobName = Utils.join(BLOB_FOLDER, sha);
if (blobName.exists()) {
return Utils.readObject(blobName, Blob.class);
}
return null;
}
}
package gitlet;
import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.*;
public class Commit implements Serializable {
/**Instance variables for a Commit*/
SimpleDateFormat ft = new SimpleDateFormat("E MMM d HH:mm:ss yyyy Z");
TimeZone pstTime = Calendar.getInstance().getTimeZone();
public String timeStamp;
public String message;
public ArrayList<String> parentCommitID = new ArrayList<>();
//blobMap key is filename and value is blobSha
public Map<String, String> blobMap = new HashMap<>();
public String splitPointID;
/**Construct a commit object passed in a commit message and a boolean identifying
* whether or not it is the first commit.
*/
public Commit (String commitMsg, String headID, boolean isTrue){
ft.setTimeZone(pstTime);
message = commitMsg;
parentCommitID.add(headID);
if (isTrue) {
timeStamp = ft.format(new Date(0));
} else {
timeStamp = ft.format(new Date());
}
}
public Commit (String commitMsg, ArrayList<String> headIDs, boolean isTrue){
ft.setTimeZone(pstTime);
message = commitMsg;
parentCommitID.addAll(headIDs);
if (isTrue) {
timeStamp = ft.format(new Date(0));
} else {
timeStamp = ft.format(new Date());
}
}
public ArrayList<String> getParentCommitID() {
return parentCommitID;
}
public String getMessage() {
return message;
}
public String getTimeStamp() {
return timeStamp;
}
public void setBlobMap (Map<String, String> addStageBlobMap) {
blobMap = addStageBlobMap;
}
//Returns string of BlobSha if given name
public String getBlobSha(String filename) {
return blobMap.getOrDefault(filename, null);
}
public Map<String, String> getBlobMap () {
return blobMap;
}
public void logCommit(String iterateCommitID) {
System.out.println("===");
System.out.println("commit " + iterateCommitID);
System.out.println("Date: " + timeStamp);
System.out.println(message);
System.out.println();
}
public void logMergeCommit (String iterateCommitID){
System.out.println("===");
System.out.println("commit " + iterateCommitID);
System.out.print("Merge: ");
System.out.print(parentCommitID.get(0).substring(0, 7) + " ");
System.out.print(parentCommitID.get(1).substring(0, 7));
System.out.println("\nDate: " + timeStamp);
System.out.println(message);
System.out.println();
}
}
package gitlet;
import java.io.IOException;
import static gitlet.Repo.*;
/** Driver class for Gitlet, the tiny stupid version-control system.
* @author Ayah Abushama, Brian Laus, Mourad Zeynalov
*/
public class Main {
/** Usage: java gitlet.Main ARGS, where ARGS contains
* <COMMAND> <OPERAND> .... */
public static void main(String... args) throws IOException {
if (args.length == 0) {
System.out.println("Please enter a command.");
System.exit(0);
}
if (!args[0].equals("init") && !GITLET_FOLDER.exists()) {
System.out.println("Not in an initialized gitlet directory.");
return;
}
if (args.length != 2 && !args[0].equals("checkout")
&& !args[0].equals("init") && !args[0].equals("status")
&& !args[0].equals("global-log") && !args[0].equals("log")
&& !args[0].equals("push") && !args[0].equals("add-remote")) {
System.out.println("Incorrect operands.");
System.exit(0);
}
switch (args[0]) {
case "init":
Repo.init();
break;
case "add":
Repo.add(args[1]);
break;
case "commit":
Repo.commit(args[1], false, null);
break;
case "log":
Repo.log();
break;
case "global-log":
Repo.globalLog();
break;
case "checkout":
if (args.length == 3 && args[1].equals("--")) {
Repo.checkoutFile(args[2]);
} else if (args.length == 4 && (args[2].equals("--"))) {
Repo.checkoutCommit(args[3], args[1]);
} else if (args.length == 2) {
Repo.checkoutBranch(args[1]);
} else {
System.out.println("Incorrect operands.");
System.exit(0);
}
break;
case "rm":
Repo.rm(args[1]);
break;
case "branch":
Repo.branch(args[1]);
break;
case "rm-branch":
Repo.rmBranch(args[1]);
break;
case "find":
Repo.find(args[1]);
break;
case "reset":
Repo.reset(args[1]);
break;
case "merge":
Repo.merge(args[1]);
break;
case "status":
Repo.status();
break;
case "add-remote":
if (args.length == 3) {
Remote.addRemote(args[1], args[2]);
} else {
System.out.println("Incorrect operands.");
System.exit(0);
}
break;
case "rm-remote":
Remote.rmRemote(args[1]);
break;
case "push":
if (args.length == 3) {
Remote.push(args[1], args[2]);
} else {
System.out.println("Incorrect operands.");
System.exit(0);
}
break;
case "fetch":
if (args.length == 3) {
Remote.fetch(args[1], args[2]);
} else {
System.out.println("Incorrect operands.");
System.exit(0);
}
break;
case "pull":
if (args.length == 3) {
Remote.pull(args[1], args[2]);
} else {
System.out.println("Incorrect operands.");
System.exit(0);
}
break;
default:
System.out.println("No command with that name exists.");
System.exit(0);
}
}
}
package gitlet;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import static gitlet.Repo.*;
public class Remote {
static final File REMOTE_FOLDER = Utils.join(GITLET_FOLDER, "remote");
static void addRemote(String remoteName, String path) {
File urlFile = Utils.join(REMOTE_FOLDER, remoteName);
if (urlFile.exists()) {
System.out.println("A remote with that name already exists.");
System.exit(0);
}
Utils.writeContents(urlFile, path.getBytes());
}
static void rmRemote(String remoteName) {
File remFile = Utils.join(REMOTE_FOLDER, remoteName);
if (!remFile.isFile()) {
System.out.print("A remote with that name already exists.");
System.exit(0);
}
remFile.delete();
}
//Attempts to append the current branch’s commits to the end of the given branch at the given remote.
static void push(String remoteName, String remBranchName) {
if (!GITLET_FOLDER.exists()) {
System.out.println("Remote directory not found.");
System.exit(0);
}
File urlFile = Utils.join(REMOTE_FOLDER, remoteName);
String remoteUrl = Utils.readContentsAsString(urlFile);
Path remBranch = Paths.get(remoteUrl);
File currBranch = Utils.join(BRANCH_FOLDER, Utils.readContentsAsString
(Utils.join(BRANCH_FOLDER, "currentBranch")));
String currCommit = Utils.readContentsAsString(currBranch);
ArrayList<String> remSplits = getBranchSplitPoints(remBranchName); // Rem should be behind curr.
ArrayList<String> currSplits = getBranchSplitPoints(remBranchName);
boolean pull = false;
String splitPoint = "";
outer: for (String item : currSplits) {
for (String item2 : remSplits) {
if (remSplits.contains(item)) {
pull = true;
}
if (item.equals(item2)) {
splitPoint = item;
break outer;
}
}
}
if (!pull) {
System.out.println("Please pull down remote changes before pushing.");
System.exit(0);
}
checkoutBranch(remBranchName);
File headPointer = Utils.join(COMMIT_FOLDER, currCommit);
String iteratePointer = Utils.readContentsAsString(headPointer);
do {
Commit commit = getCommit(iteratePointer);
Commit newCommit = commit(commit.message, false, null);
newCommit.timeStamp = commit.timeStamp;
newCommit.parentCommitID = commit.parentCommitID;
newCommit.blobMap = commit.blobMap;
iteratePointer = commit.getParentCommitID().get(0);
} while (!iteratePointer.equals(splitPoint));
}
static void fetch(String remoteName, String remBranchName) {
File urlFile = Utils.join(REMOTE_FOLDER, remoteName);
File remBranch = Utils.join(BRANCH_FOLDER, remBranchName);
File currBranch = Utils.join(BRANCH_FOLDER, Utils.readContentsAsString
(Utils.join(BRANCH_FOLDER, "currentBranch")));
String currCommit = Utils.readContentsAsString(currBranch);
if (!urlFile.isFile()) {
System.out.println("Remote directory not found");
System.exit(0);
}
if (!remBranch.isFile()) {
System.out.println("That remote does not have that branch.");
System.exit(0);
}
ArrayList<String> remSplits = getBranchSplitPoints(remBranchName); // Rem should be behind curr.
ArrayList<String> currSplits = getBranchSplitPoints(remBranchName);
String splitPoint = "";
outer: for (String item : currSplits) {
for (String item2 : remSplits) {
if (item.equals(item2)) {
splitPoint = item;
break outer;
}
}
}
checkoutBranch(currBranch.getName());
File headPointer = Utils.join(COMMIT_FOLDER, currCommit);
String iteratePointer = Utils.readContentsAsString(headPointer);
do {
Commit commit = getCommit(iteratePointer);
Commit newCommit = commit(commit.message, false, null);
newCommit.timeStamp = commit.timeStamp;
newCommit.parentCommitID = commit.parentCommitID;
newCommit.blobMap = commit.blobMap;
iteratePointer = commit.getParentCommitID().get(0);
} while (!iteratePointer.equals(splitPoint));
}
//Assuming merge works.
static void pull(String remoteName, String remBranchName) {
fetch(remoteName, remBranchName);
try {
Repo.merge(remoteName + "/" + remBranchName);
} catch (IOException e) {
e.printStackTrace();
}
}
}
package gitlet;
import java.io.File;
import java.io.IOException;
import java.lang.*;
import java.util.*;
/* Implements the repository functionality, along with the branching. */
import static java.util.Arrays.sort;
/**
* Online references:
* https://stackoverflow.com/questions/27749755/files-move-replace-existing-cannot-be-resolved-to-a-variable
* https://www.baeldung.com/java-map-key-from-value
*/
public class Repo {
//Current Working Directory.
static final File CWD = new File(".");
//Main metadata folder.
static final File GITLET_FOLDER = Utils.join(CWD, ".gitlet");
static final File BLOB_FOLDER = Utils.join(GITLET_FOLDER, "blob");
static final File ADD_STAGE_FOLDER = Utils.join(GITLET_FOLDER, "forAddition");
static final File REMOVE_STAGE_FOLDER = Utils.join(GITLET_FOLDER, "forRemoval");
static final File GLOBAL_LOG_COMMITS = Utils.join(GITLET_FOLDER, "allCommits");
static final File COMMIT_FOLDER = Utils.join(GITLET_FOLDER, "commit");
static final File BRANCH_FOLDER = Utils.join(GITLET_FOLDER, "branches");
static final File BRANCH_SPLITS = Utils.join(GITLET_FOLDER, "splitPoints");
static final File headPointer = Utils.join(COMMIT_FOLDER, "headPointer");
static final File currentBranch = Utils.join(BRANCH_FOLDER, "currentBranch");
static final File master = Utils.join(BRANCH_FOLDER, "master");
static ArrayList<String> trackedModified = new ArrayList<>();
static ArrayList<String> trackedDeleted = new ArrayList<>();
static ArrayList<String> untracked = new ArrayList<>();
//Create repository in working directory.
public static void init() throws IOException {
if (GITLET_FOLDER.exists()) {
System.out.println("A Gitlet version-control system already exists in the current directory.");
System.exit(0);
}
GITLET_FOLDER.mkdir();
BLOB_FOLDER.mkdir();
ADD_STAGE_FOLDER.mkdir();
REMOVE_STAGE_FOLDER.mkdir();
COMMIT_FOLDER.mkdir();
BRANCH_FOLDER.mkdir();
BRANCH_SPLITS.mkdir();
GLOBAL_LOG_COMMITS.mkdir();
//File that contains headPointer to most recent commit.
headPointer.createNewFile();
Utils.writeContents(headPointer, "");
//File that contains masterBranch pointer, which initially is the same as headPointer.
master.createNewFile();
Utils.writeContents(master, Utils.readContentsAsString(headPointer));
//File that contains name of currentBranch, which is initially master.
currentBranch.createNewFile();
Utils.writeContents(currentBranch, "master");
//Set the master branch's split pointer to an empty arrayList
File masterSplit = Utils.join(BRANCH_SPLITS, "master");
masterSplit.createNewFile();
Utils.writeObject(masterSplit, new ArrayList<String>());
commit("initial commit", true, null);
}
public static void branch(String bName) throws IOException {
File branch = Utils.join(BRANCH_FOLDER, bName);
if (branch.exists()) {
System.out.println("A branch with that name already exists.");
System.exit(0);
}
ArrayList<String> splitList;
File headPointer = Utils.join(COMMIT_FOLDER, "headPointer");
//Branch points at where headPointer does.
Utils.writeContents(branch, Utils.readContentsAsString(headPointer));
//Set the headCommit's splitPointerID to the headCommitID
getHeadCommit().splitPointID = Utils.readContentsAsString(headPointer);
// Split pointer, named after the new branch.
if (Utils.join(BRANCH_SPLITS, Utils.readContentsAsString(currentBranch)).exists()) {
File currentSplitPoint = Utils.join(BRANCH_SPLITS, Utils.readContentsAsString(currentBranch));
splitList = Utils.readObject(currentSplitPoint, ArrayList.class);
} else {
splitList = new ArrayList<String>();
}
splitList.add(0, Utils.readContentsAsString(headPointer));
File split = Utils.join(BRANCH_SPLITS, bName);
split.createNewFile();
Utils.writeObject(split, splitList);
}
public static Commit commit(String message, boolean isInitialCommit, String otherParentID) {
//Create a new commit with a message and hash a commitID(sha1 code) for the object
if (message.equals("")) {
System.out.println("Please enter a commit message");
System.exit(0);
} else if (ADD_STAGE_FOLDER.listFiles().length == 0 && REMOVE_STAGE_FOLDER.listFiles().length == 0
&& !isInitialCommit) {
System.out.println("No changes added to the commit");
System.exit(0);
}
File headPointer = Utils.join(COMMIT_FOLDER, "headPointer");
String headPointerString = Utils.readContentsAsString(headPointer);
ArrayList<String> parentIDs = new ArrayList<>();
parentIDs.add(headPointerString);
if (otherParentID != null) {
parentIDs.add(otherParentID);
}
Commit commit = new Commit(message, parentIDs, isInitialCommit);
if (!isInitialCommit && getHeadCommit().getBlobMap().size() > 0) {
commit.blobMap.putAll(getHeadCommit().getBlobMap());
}
for (File file : REMOVE_STAGE_FOLDER.listFiles()) {
if (commit.getBlobMap().containsKey(file.getName())) {
commit.blobMap.remove(file.getName());
}
}
commit.blobMap.putAll(readAddStageBlobMap());
byte[] commitSerialized = Utils.serialize(commit);
String commitID = Utils.sha1(commitSerialized);
//Rewrite headPointer with current commitID.
Utils.writeContents(headPointer, commitID);
//Update current branch pointer to match headPointer.
updateCurrentBranchPointer();
//Create a new commitFile w/ commitID as filename and join the Commit object into it
File commitFile = Utils.join(COMMIT_FOLDER, commitID);
Utils.writeObject(commitFile, commit);
//Add commit file to folder that stores every commit for global log.
File GLOBAL_LOG_COMMITS = Utils.join(GITLET_FOLDER, "allCommits");
File gCommitFile = Utils.join(GLOBAL_LOG_COMMITS, commitID);
Utils.writeObject(gCommitFile, commit);
//Remove all files that were in the removal folder from the CWD
clearRemovesFromCWD(REMOVE_STAGE_FOLDER.listFiles());
//Clear out the staged for removal folder(MUST BE LAST)
clearFolder(REMOVE_STAGE_FOLDER);
clearFolder(ADD_STAGE_FOLDER);
return commit;
}
public static void commitMerge(String message, ArrayList<String> parents) {
//Create a new commit with a message and hash a commitID(sha1 code) for the object
File headPointer = Utils.join(COMMIT_FOLDER, "headPointer");
String headPointerString = Utils.readContentsAsString(headPointer);
Commit commit = new Commit(message, parents, false);
for (File file : REMOVE_STAGE_FOLDER.listFiles()) {
if (commit.getBlobMap().containsKey(file.getName())) {
commit.blobMap.remove(file.getName());
}
}
commit.blobMap.putAll(readAddStageBlobMap());
byte[] commitSerialized = Utils.serialize(commit);
String commitID = Utils.sha1(commitSerialized);
//Rewrite headPointer with current commitID.
Utils.writeContents(headPointer, commitID);
//Update current branch pointer to match headPointer.
updateCurrentBranchPointer();
//Create a new commitFile w/ commitID as filename and join the Commit object into it
File commitFile = Utils.join(COMMIT_FOLDER, commitID);
Utils.writeObject(commitFile, commit);
//Add commit file to folder that stores every commit for global log.
File GLOBAL_LOG_COMMITS = Utils.join(GITLET_FOLDER, "allCommits");
File gCommitFile = Utils.join(GLOBAL_LOG_COMMITS, commitID);
Utils.writeObject(gCommitFile, commit);
//Remove all files that were in the removal folder from the CWD
clearRemovesFromCWD(REMOVE_STAGE_FOLDER.listFiles());
//Clear out the staged for removal folder(MUST BE LAST)
clearFolder(REMOVE_STAGE_FOLDER);
clearFolder(ADD_STAGE_FOLDER);
}
public static void add(String filename) {
//Failure case.
if (!Utils.join(CWD, filename).isFile()) {
System.out.println("File does not exist.");
System.exit(0);
}
Blob blobToAdd = new Blob(filename);
//Read HashMap of blob SHA1s in most recent commit.
Commit commitLoaded = getHeadCommit();
//Find blob's SHA1.
byte[] blobSerialized = Utils.serialize(blobToAdd);
String blobSha = Utils.sha1(blobSerialized);
//Removes file in add_stage_folder if a previous version was staged.
File blobInStage = Utils.join(ADD_STAGE_FOLDER, filename);
if (blobInStage.exists()) {
blobInStage.delete();
}
//Check if blobToAdd's SHA1 differs from recent commit.
if (commitLoaded.blobMap.containsValue(blobSha)) {
File inRemove = Utils.join(REMOVE_STAGE_FOLDER, filename);
if (inRemove.exists()) {
inRemove.delete();
}
return;
}
//Saves blob to add_stage_folder.
Utils.writeObject(blobInStage, blobToAdd);
//Save blob to blob folder, in a file named after its SHA1.
File blobInBF = Utils.join(BLOB_FOLDER, blobSha);
Utils.writeObject(blobInBF, blobToAdd);
}
public static void rm(String filename) throws IOException {
if (!inAddStage(filename) && !isCommitBlob(filename)) {
System.out.println("No reason to remove the file.");
System.exit(0);
}
//Add the blob object to the REMOVE_STAGE_FOLDER
if (inAddStage(filename)) {
//remove the file from the ADD_STAGE_FOLDER
File blob = Utils.join(ADD_STAGE_FOLDER, filename);
blob.delete();
}
if (getHeadCommit().getBlobSha(filename) != null) {
File removeBlob = Utils.join(REMOVE_STAGE_FOLDER, filename);
removeBlob.createNewFile();
//Remove the file from the working directory
File removeFile = Utils.join(CWD, filename);
if (removeFile.exists()) {
Utils.writeObject(removeBlob, Utils.readContentsAsString(Utils.join(CWD, filename)));
removeFile.delete();
}
}
}
public static void rmBranch(String bName) {
File branch = Utils.join(BRANCH_FOLDER, bName);
if (!(branch.exists())) {
System.out.println("A branch with that name does not exist.");
System.exit(0);
}
//Check if bName is the current branch.
File currentBranch = Utils.join(BRANCH_FOLDER, "currentBranch");
if (Utils.readContentsAsString(currentBranch).equals(bName)) {
System.out.println("Cannot remove the current branch.");
System.exit(0);
}
branch.delete();
}
/**
* Prints all the commitIDs with the given commit message
*/
public static void find(String commitMsg) {
boolean flag = false;
for (File commit : GLOBAL_LOG_COMMITS.listFiles()) {
Commit commitObject = Utils.readObject(commit, Commit.class);
if (commitMsg.equals(commitObject.getMessage())) {
System.out.println(commit.getName());
flag = true;
}
}
if (!flag) {
System.out.println("Found no commit with that message");
}
}
public static void reset(String commitID) {
if (commitID.length() < 40) {
commitID = getLongID(commitID);
}
if (getCommit(commitID) == null) {
System.out.println("No commit with that id exists.");
System.exit(0);
}
Commit commit = getCommit(commitID);
Map<String, String> blobMap = commit.getBlobMap();
for (Map.Entry<String, String> entry : blobMap.entrySet()) {
File cwdFile = Utils.join(CWD, entry.getKey());
if (cwdFile.exists()) {
if (!(java.util.Arrays.equals(getBlob(entry.getValue()).getBlobContents(), Utils.readContents(cwdFile)))) {
System.out.println("There is an untracked file in the way; delete it, or add and commit it first.");
System.exit(0);
}
}
checkoutCommit(entry.getKey(), commitID);
}
//remove the files that are tracked but are not in the specified commit
for (File file : CWD.listFiles()) {
File added = Utils.join(ADD_STAGE_FOLDER, file.getName());
File removed = Utils.join(REMOVE_STAGE_FOLDER, file.getName());
Boolean inCommit = commit.getBlobMap().containsKey(file.getName());
if (!inCommit && !added.isFile() && !removed.isFile()) {
file.delete();
Commit headCommit = getHeadCommit();
for (Map.Entry<String, String> set : headCommit.getBlobMap().entrySet()) {
if (!blobMap.containsKey(set.getKey())) {
File inCWD = Utils.join(CWD, set.getKey());
inCWD.delete();
}
}
}
}
//Update the head and branch pointers
Utils.writeContents(headPointer, commitID);
updateCurrentBranchPointer();
clearFolder(ADD_STAGE_FOLDER);
clearFolder(REMOVE_STAGE_FOLDER);
}
/**
* Logs the commits starting at the current head pointer
*/
public static void log() {
File headPointer = Utils.join(COMMIT_FOLDER, "headPointer");
String iteratePointer = Utils.readContentsAsString(headPointer);
while (!iteratePointer.equals("")) {
Commit commit = getCommit(iteratePointer);
if (commit.getParentCommitID().size() == 1) {
commit.logCommit(iteratePointer);
iteratePointer = commit.getParentCommitID().get(0);
}
if (commit.getParentCommitID().size() > 1) {
commit.logMergeCommit(iteratePointer);
iteratePointer = commit.getParentCommitID().get(commit.getParentCommitID().size() - 1);
}
}
}
public static void globalLog() {
for (File commit : GLOBAL_LOG_COMMITS.listFiles()) {
Commit commitObject = Utils.readObject(commit, Commit.class);
commitObject.logCommit(commit.getName());
}
}
/*
* Takes the version of the file as it exists in the head commit,
* the front of the current branch, and puts it in the working directory,
* overwriting the version of the file that’s already there if there is one.
* The new version of the file is not staged.
*/
public static void checkoutFile(String filename) {
Commit commit = getHeadCommit();
String sha = commit.getBlobSha(filename);
//Got blob sha.
if (sha == null) {
System.out.println("File does not exist in that commit.");
System.exit(0);
}
File temp = Utils.join(BLOB_FOLDER, sha);
Blob oldBlob = Utils.readObject(temp, Blob.class);
//Deserializing blob.
byte[] bytes = oldBlob.getBlobContents();
File file = new File(filename);
Utils.writeContents(file, bytes);
Utils.join(file, System.getProperty("user.dir"));
clearFolder(ADD_STAGE_FOLDER);
clearFolder(REMOVE_STAGE_FOLDER);
}
/* Takes the version of the file as it exists in the commit
*with the given id, and puts it in the working directory,
*overwriting the version of the file that’s already there if
*there is one. The new version of the file is not staged. */
public static void checkoutCommit(String filename, String commitID) {
//Get commit.
if (commitID.length() < 40) {
commitID = getLongID(commitID);
}
Commit commit = getCommit(commitID);
if (commit == null) {
System.out.println("No commit with that id exists.");
System.exit(0);
} else {
//get file name
String sha = commit.getBlobSha(filename);
if (sha == null) {
System.out.println("File does not exist in that commit.");
System.exit(0);
}
File temp = Utils.join(BLOB_FOLDER, sha);
Blob oldBlob = Utils.readObject(temp, Blob.class);
byte[] bytes = oldBlob.getBlobContents();
File file = new File(filename);
Utils.writeContents(file, bytes);
Utils.join(CWD, filename);
}
}
public static void checkoutBranch(String bName) {
//don't overwrite unchecked files
//all tracked versions are equal
File branchHead = Utils.join(BRANCH_FOLDER, bName);
if (Utils.readContentsAsString(currentBranch).equals(bName)) {
System.out.println("No need to checkout the current branch.");
System.exit(0);
} else if (!branchHead.isFile()) {
System.out.println("No such branch exists.");
System.exit(0);
}
File currentBranch = Utils.join(BRANCH_FOLDER, "currentBranch");
Commit oldHeadCommit = getHeadCommit();
Map<String, String> oldFiles = oldHeadCommit.getBlobMap();
String bCommitHead = Utils.readContentsAsString(branchHead);
File commitFile = Utils.join(COMMIT_FOLDER, bCommitHead);
Commit commit = getCommit(commitFile.getName());
Map<String, String> files = commit.getBlobMap();
// Exit if a working file is untracked in the current branch
// and would be overwritten by the checkout.
for (Map.Entry<String, String> file : files.entrySet()) {
File inCWD = Utils.join(CWD, file.getKey());
if (inCWD.exists() && !oldFiles.containsKey(file.getKey())) {
System.out.println("There is an untracked file in the way; " +
"delete it, or add and commit it first.");
System.exit(0);
}
}
// Takes all files in the commit at the head of the given branch, and puts in CWD.
// Learned this from educative.io's article on how to iterate through HashMap.
files.forEach((key, value) -> checkoutCommit(key, bCommitHead));
//getlongid
// Given branch will now be considered the current branch.
Utils.writeContents(currentBranch, bName);
// Deletes previously tracked files that are not tracked in checked out branch.
for (Map.Entry<String, String> set : oldFiles.entrySet()) {
if (!(files.containsKey(set.getKey()))) {
File inCWD = Utils.join(CWD, set.getKey());
inCWD.delete();
}
}
// Ensure headPointer points to branch head.
File headPointer = Utils.join(COMMIT_FOLDER, "headPointer");
Utils.writeContents(headPointer, bCommitHead);
// Clear staging area.
clearFolder(ADD_STAGE_FOLDER);
clearFolder(REMOVE_STAGE_FOLDER);
}
public static void merge(String bName) throws IOException {
File givBranch = Utils.join(BRANCH_FOLDER, bName);
//If a branch with the given name does not exist, print the error message
if (!givBranch.exists()) {
System.out.println("A branch with that name does not exist.");
System.exit(0);
}
// If there are staged additions or removals present, print the error message
if (ADD_STAGE_FOLDER.listFiles().length != 0 || REMOVE_STAGE_FOLDER.listFiles().length != 0) {
System.out.println("You have uncommitted changes.");
System.exit(0);
}
track();
if (!untracked.isEmpty()) {
System.out.println("There is an untracked file in the way; delete it, " +
"or add and commit it first.");
System.exit(0);
}
File currBranch = Utils.join(BRANCH_FOLDER, "currentBranch");
String currBranchName = Utils.readContentsAsString(currBranch);
if (bName.equals(currBranchName)) {
System.out.println("Cannot merge a branch with itself.");
System.exit(0);
}
String givBranchCommitID = Utils.readContentsAsString(givBranch);
String splitPointCommitID = "";
boolean encounteredMergeConflict = false;
String currBranchID = Utils.readContentsAsString(Utils.join(BRANCH_FOLDER, currBranchName));
File cBranchCommit = Utils.join(COMMIT_FOLDER, currBranchID);
Commit currCommit = Utils.readObject(cBranchCommit, Commit.class);
Commit givBranchCommit = getCommit(givBranchCommitID);
//If attempting to merge a branch with itself, print the error message
/**Setting up the splitPointCommitID */
//These two special cases function when one of the two branches is master, the sPCI will be the last commitID
//in the non-master branch split array
if (bName.equals("master")) {
if (getBranchSplitPoints(currBranchName).size() != 0) {
splitPointCommitID = getBranchSplitPoints(currBranchName).get(getBranchSplitPoints(currBranchName).size() - 1);
} else {
splitPointCommitID = getBranchSplitPoints(currBranchName).get(0);
}
} else if (currBranchName.equals("master")) {
if (getBranchSplitPoints(currBranchName).size() != 0) {
splitPointCommitID = getBranchSplitPoints(bName).get(getBranchSplitPoints(bName).size() - 1);
} else {
splitPointCommitID = getBranchSplitPoints(bName).get(0);
}
} else {
//This nested loop will set sPCI to the first splitID that the current branch and the given branch share
outer: for (String cbSplitID : getBranchSplitPoints(currBranchName)) {
for (String bSplitID : getBranchSplitPoints(bName)) {
if (cbSplitID.equals(bSplitID)) {
splitPointCommitID = cbSplitID;
break outer;
}
}
}
}
// If the split point is the same commit as the given branch, then do nothing; the merge is complete,
// and the operation ends
if (splitPointCommitID.equals(currBranchID)) {
checkoutBranch(bName);
System.out.println("Current branch fast-forwarded.");
System.exit(0);
}
if (splitPointCommitID.equals(givBranchCommitID)) {
System.out.println("Given branch is an ancestor of the current branch.");
System.exit(0);
}
// If the split point is the same commit as the given branch, then do nothing; the merge is complete,
// and the operation ends
if (splitPointCommitID.equals(currBranchID)) {
checkoutBranch(bName);
System.out.println("Current branch fast-forwarded.");
System.exit(0);
}
if (splitPointCommitID.equals(givBranchCommitID)) {
System.out.println("Given branch is an ancestor of the current branch.");
System.exit(0);
}
String iteratePointer = currBranchID;
ArrayList<String> splitCurrBranchIDList = new ArrayList<>();
ArrayList<String> normalCurrBranchIDList = new ArrayList<>();
while (!iteratePointer.equals("")) {
Commit commit = getCommit(iteratePointer);
normalCurrBranchIDList.add(commit.getParentCommitID().get(0));
iteratePointer = commit.getParentCommitID().get(0);
}
iteratePointer = currBranchID;
while (!iteratePointer.equals("")) {
Commit commit = getCommit(iteratePointer);
if (commit.getParentCommitID().size()==1) {
splitCurrBranchIDList.add(commit.getParentCommitID().get(0));
iteratePointer = commit.getParentCommitID().get(0);
}
if (commit.getParentCommitID().size() > 1){
splitCurrBranchIDList.add(commit.getParentCommitID().get(commit.getParentCommitID().size()-1));
iteratePointer = commit.getParentCommitID().get(commit.getParentCommitID().size()-1);
}
}
iteratePointer = givBranchCommitID;
ArrayList<String> splitBranchIDList = new ArrayList<>();
ArrayList<String> normalBranchIDList = new ArrayList<>();
while (!iteratePointer.equals("")) {
Commit commit = getCommit(iteratePointer);
normalCurrBranchIDList.add(commit.getParentCommitID().get(0));
iteratePointer = commit.getParentCommitID().get(0);
}
iteratePointer = givBranchCommitID;
while (!iteratePointer.equals("")) {
Commit commit = getCommit(iteratePointer);
if (commit.getParentCommitID().size() == 1) {
splitBranchIDList.add(commit.getParentCommitID().get(0));
iteratePointer = commit.getParentCommitID().get(0);
}
if (commit.getParentCommitID().size() > 1) {
splitBranchIDList.add(commit.getParentCommitID().get(commit.getParentCommitID().size() - 1));
iteratePointer = commit.getParentCommitID().get(commit.getParentCommitID().size() - 1);
}
}
int index1 = 0;
String crossID1 = "";
int index2 = 0;
String crossID2 = "";
for (String commitID : splitCurrBranchIDList) {
if (normalBranchIDList.contains(commitID)){
index1 = splitCurrBranchIDList.indexOf(commitID);
crossID1 = commitID;
break;
}
}
for (String commitID : splitBranchIDList) {
if (normalCurrBranchIDList.contains(commitID)){
index2 = splitBranchIDList.indexOf(commitID);
crossID2 = commitID;
break;
}
}
if (index1 < index2){
if (!crossID1.equals("")){
if (isBeforeCommit(crossID1, splitPointCommitID)){
splitPointCommitID = crossID1;
}
}
} else if (index1 == index2) {
if (!crossID1.equals("")){
if (isBeforeCommit(crossID1, splitPointCommitID)){
splitPointCommitID = crossID1;
}
} else {
splitPointCommitID = crossID2;
}
} else {
if (!crossID2.equals("")){
if (isBeforeCommit(crossID2, splitPointCommitID)){
splitPointCommitID = crossID2;
}
}
}
Commit splitPointCommit = getCommit(splitPointCommitID);
//If the split point is the current branch, then the effect is to check out the given branch
//Loop through the branchCommit and the currCommit and put all their file's names into a Set
Set<String> filesInCommits = new HashSet();
for (Map.Entry<String, String> entry : givBranchCommit.getBlobMap().entrySet()) {
filesInCommits.add(entry.getKey());
}
for (Map.Entry<String, String> entry : currCommit.getBlobMap().entrySet()) {
filesInCommits.add(entry.getKey());
}
/** Main loop for merging */
//Check the files in the latest commit in the given branch
for (String filename : filesInCommits) {
//If the file is contained in all three maps
if (splitPointCommit.getBlobMap().containsKey(filename) &&
givBranchCommit.getBlobMap().containsKey(filename) &&
currCommit.getBlobMap().containsKey(filename)) {
//Any files that have been modified in the given branch since the split point, but not modified in the
// current branch since the split point should be changed to their versions in the given branch
if (fileChangedBetweenCommits(filename, splitPointCommit, givBranchCommit) &&
!fileChangedBetweenCommits(filename, splitPointCommit, currCommit)) {
checkoutCommit(filename, givBranchCommitID);
add(filename);
}
//Any files that have been modified in the current branch but not in the given branch since the
//split point should stay as they are.
if (!fileChangedBetweenCommits(filename, splitPointCommit, givBranchCommit) &&
fileChangedBetweenCommits(filename, splitPointCommit, currCommit)) {
add(filename);
}
//Any files that have been modified in both the current and given branch in the same way
// (i.e., both to files with the same content or both removed) are left unchanged by the merge.
if (fileChangedBetweenCommits(filename, splitPointCommit, givBranchCommit) &&
fileChangedBetweenCommits(filename, splitPointCommit, currCommit)) {
if ((getBlob(currCommit.getBlobSha(filename))
.getBlobContents().equals(getBlob(givBranchCommit.getBlobSha(filename)).getBlobContents()))) {
}
//Any files modified in different ways in the current and given branches are in conflict.
//In this case, replace the contents of the conflicted file
if (!(getBlob(currCommit.getBlobSha(filename))
.getBlobContents().equals(getBlob(givBranchCommit.getBlobSha(filename)).getBlobContents()))) {
//Merge the file contents
encounteredMergeConflict = true;
File CWDFile = Utils.join(CWD, filename);
CWDFile.createNewFile();
Utils.writeContents(CWDFile,
("<<<<<<< HEAD\n" +
new String(getBlob(currCommit.getBlobSha(filename)).getBlobContents()) +
"=======\n" +
new String(getBlob(givBranchCommit.getBlobSha(filename)).getBlobContents()) +
">>>>>>>\n"));
add(CWDFile.getName());
}
}
} else if (splitPointCommit.getBlobMap().containsKey(filename) &&
!givBranchCommit.getBlobMap().containsKey(filename) &&
currCommit.getBlobMap().containsKey(filename) &&
fileChangedBetweenCommits(filename, splitPointCommit, givBranchCommit) &&
fileChangedBetweenCommits(filename, splitPointCommit, currCommit)) {
encounteredMergeConflict = true;
File CWDFile = Utils.join(CWD, filename);
CWDFile.createNewFile();
Utils.writeContents(CWDFile,
("<<<<<<< HEAD\n" +
new String(getBlob(currCommit.getBlobSha(filename)).getBlobContents()) +
"=======\n" +
"" +
">>>>>>>\n"));
add(CWDFile.getName());
} else if (splitPointCommit.getBlobMap().containsKey(filename) &&
givBranchCommit.getBlobMap().containsKey(filename) &&
!currCommit.getBlobMap().containsKey(filename) &&
fileChangedBetweenCommits(filename, splitPointCommit, currCommit) &&
fileChangedBetweenCommits(filename, splitPointCommit, givBranchCommit)) {
encounteredMergeConflict = true;
File CWDFile = Utils.join(CWD, filename);
CWDFile.createNewFile();
Utils.writeContents(CWDFile,
("<<<<<<< HEAD\n" +
"" +
"=======\n" +
new String(getBlob(givBranchCommit.getBlobSha(filename)).getBlobContents()) +
">>>>>>>\n"));
add(CWDFile.getName());
}
//Any files that were not present at the split point and are present only in the current branch
//should remain as they are.
if (!splitPointCommit.getBlobMap().containsKey(filename) &&
!givBranchCommit.getBlobMap().containsKey(filename) &&
currCommit.getBlobMap().containsKey(filename)) {
add(filename);
}
//Any files that were not present at the split point and are present only in the given branch
//should be checked out and staged.
if (!splitPointCommit.getBlobMap().containsKey(filename) &&
givBranchCommit.getBlobMap().containsKey(filename) &&
!currCommit.getBlobMap().containsKey(filename)) {
checkoutCommit(filename, givBranchCommitID);
add(filename);
}
//Any files present at the split point, unmodified in the current branch, and absent in the given branch
// should be removed (and untracked).
if (splitPointCommit.getBlobMap().containsKey(filename) &&
!givBranchCommit.getBlobMap().containsKey(filename) &&
currCommit.getBlobMap().containsKey(filename) &&
!fileChangedBetweenCommits(filename, splitPointCommit, currCommit)) {
rm(filename);
}
//Any files present at the split point, unmodified in the given branch, and absent in the
// current branch should remain absent.
if (splitPointCommit.getBlobMap().containsKey(filename) &&
givBranchCommit.getBlobMap().containsKey(filename) &&
!currCommit.getBlobMap().containsKey(filename) &&
!fileChangedBetweenCommits(filename, splitPointCommit, givBranchCommit)) {
}
}
if (encounteredMergeConflict) {
System.out.println("Encountered a merge conflict.");
}
//Once files have been updated according to the above merge automatically commits with the log message
commit("Merged " + bName + " into " + currBranchName, false, givBranchCommitID);
}
/*
Should remain as they are if:
1. files that were not present at the split point and are present only in the current branch should remain as
they are.
2. Any files that have been modified in the current branch but not in the given branch since the split point
should stay as they are.
3. Any files that have been modified in both the current and given branch in the same
way (i.e., both to files with the same content or both removed) are left unchanged by the merge.
4. If a file is removed in both, but a file of that name is present in the working directory that file is not removed from
the working directory (but it continues to be absent<—>not staged<—>in the merge).
5. Any files present at the split point, unmodified in the given branch, and absent in the current branch
should remain absent.
*/
/** Any files that were not present at the split point and are present only in the given branch should be
* checked out and staged.
*
*/
/**Any files present at the split point, unmodified in the current branch, and absent in the given branch
* should be removed (and untracked).
*
*/
/**
* Any files modified in different ways in the current and given branches are in conflict, concatenate contents
* of past into current branch.
*/
public static void status() {
track();
File currentBranch = Utils.join(BRANCH_FOLDER, "currentBranch");
String currBranchName = Utils.readContentsAsString(currentBranch);
System.out.println("=== Branches ===");
System.out.println("*" + currBranchName);
for (File branch : BRANCH_FOLDER.listFiles()) {
if (!branch.getName().equals(currBranchName) && !branch.getName().equals("currentBranch")) {
System.out.println(branch.getName());
}
}
System.out.println();
System.out.println("=== Staged Files ===");
for (File added : ADD_STAGE_FOLDER.listFiles()) {
String name = added.getName();
if (!trackedModified.contains(name) && !trackedDeleted.contains(name) && !untracked.contains(name)) {
System.out.println(name);
}
}
System.out.println();
System.out.println("=== Removed Files ===");
for (File removed : REMOVE_STAGE_FOLDER.listFiles()) {
String name = removed.getName();
System.out.println(name);
}
System.out.println();
System.out.println("=== Modifications Not Staged For Commit ===");
// Collections.sort(trackedDeleted);
// Collections.sort(trackedModified);
// trackedDeleted.forEach((String newName) -> System.out.println(newName + " (deleted)"));
// trackedModified.forEach((String newName) -> System.out.println(newName + " (modified)"));
System.out.println();
System.out.println("=== Untracked Files ===");
}
/**
* ALL HELPER METHODS IN REPO
*/
public static Commit getHeadCommit() {
String headPointer = Utils.readContentsAsString(Utils.join(COMMIT_FOLDER, "headPointer"));
return Utils.readObject(Utils.join(COMMIT_FOLDER, headPointer), Commit.class);
}
/**
* Reads through the staging area and creates a commit object based on the contents and the message
*/
public static Map<String, String> readAddStageBlobMap() {
//Read the sha1's of the blobs in the Stage directory as a byte array.
Map<String, String> addStageBlobMap = new HashMap<>();
File[] addFiles = ADD_STAGE_FOLDER.listFiles();
for (File blobName : addFiles) {
if (blobName.exists()) {
byte[] blobSerialized = Utils.serialize(Utils.readObject(blobName, Blob.class));
addStageBlobMap.put(blobName.getName(), Utils.sha1(blobSerialized));
blobName.delete();
}
}
return addStageBlobMap;
}
//Reads currentBranch and sets that branch pointer to headPointer.
public static void updateCurrentBranchPointer() {
File headPointer = Utils.join(COMMIT_FOLDER, "headPointer");
File currentBranch = Utils.join(BRANCH_FOLDER, "currentBranch");
File curr = Utils.join(BRANCH_FOLDER, Utils.readContentsAsString(currentBranch));
Utils.writeContents(curr, Utils.readContentsAsString(headPointer));
}
/**
* Helper method: pass in a commitID and return the commit object
*/
public static Commit getCommit(String commitID) {
if (commitID.length() < 40) {
commitID = getLongID(commitID);
} else if (commitID.equals("headPointer")) {
commitID = Utils.readContentsAsString(Utils.join(COMMIT_FOLDER, commitID));
}
for (File commit : COMMIT_FOLDER.listFiles()) {
if (commitID.equals(commit.getName())) {
return Utils.readObject(commit, Commit.class);
}
}
return null;
}
/**
* Helper method: pass in a filename and return the blob object from blob folder
*/
public static Blob getBlob(String sha1) {
File blobFile = Utils.join(BLOB_FOLDER, sha1);
return Utils.readObject(blobFile, Blob.class);
}
/**
* Helper method: pass in the names of the remove stage files and remove them from the CWD
*/
public static void clearRemovesFromCWD(File[] removeFiles) {
if (removeFiles.length == 0) {
return;
}
for (File f : removeFiles) {
f.delete();
}
}
/**
* Helper method: clear all files from the given folder
*/
public static void clearFolder(File folder) {
File[] files = folder.listFiles();
if (files.length == 0) {
return;
}
for (File file : files) {
file.delete();
}
}
public static boolean fileHasChanged(File fileName, byte[] currContents) {
String sha = getHeadCommit().getBlobSha(fileName.getName());
if (sha == null) {
return false;
}
Blob oldBlob = Utils.readObject(Utils.join(BLOB_FOLDER, sha), Blob.class);
byte[] oldBlobContents = oldBlob.getBlobContents();
return currContents.equals(oldBlobContents);
}
//Check if a file has changed between two commits
public static boolean fileChangedBetweenCommits(String filename, Commit commit1, Commit commit2) {
if (commit1.getBlobMap().containsKey(filename) && commit2.getBlobMap().containsKey(filename)) {
Blob blob1 = Utils.readObject(Utils.join(BLOB_FOLDER, commit1.getBlobMap().get(filename)), Blob.class);
Blob blob2 = Utils.readObject(Utils.join(BLOB_FOLDER, commit2.getBlobMap().get(filename)), Blob.class);
return !Arrays.equals(blob1.getBlobContents(), blob2.getBlobContents());
} else if (commit1.getBlobMap().containsKey(filename) && !commit2.getBlobMap().containsKey(filename)){
Blob blob1 = Utils.readObject(Utils.join(BLOB_FOLDER, commit1.getBlobMap().get(filename)), Blob.class);
return !Arrays.equals(blob1.getBlobContents(), null);
} else if (!commit1.getBlobMap().containsKey(filename) && commit2.getBlobMap().containsKey(filename)){
Blob blob2 = Utils.readObject(Utils.join(BLOB_FOLDER, commit2.getBlobMap().get(filename)), Blob.class);
return !Arrays.equals(blob2.getBlobContents(), null);
} else {
return false;
}
}
public static boolean fileHasChanged(byte[] inpFile, byte[] currContents) {
return !Arrays.equals(currContents,inpFile);
}
/**
* Adds all modified, deleted, tracked files to the respective arraylist. Status helper
*/
public static void track() {
trackedDeleted.clear();
trackedModified.clear();
untracked.clear();
Commit headCommit = getHeadCommit();
//Staged for addition, but deleted in the working directory
for (File fileName : ADD_STAGE_FOLDER.listFiles()) {
File cwdFile = Utils.join(CWD, fileName.getName());
if (!cwdFile.isFile()) {
trackedDeleted.add(fileName.getName());
}
}
//Not staged for removal, but tracked in the current commit and
//deleted from the working directory.
ArrayList<String> list = new ArrayList<>(headCommit.blobMap.keySet());
for (String blobCom : list) {
File blobCWD = Utils.join(CWD, blobCom);
File blobRemove = Utils.join(REMOVE_STAGE_FOLDER, blobCom);
if (!blobRemove.isFile() && !blobCWD.isFile()) {
trackedDeleted.add(blobCom);
}
}
for (File file : CWD.listFiles()) {
if (!file.isDirectory()) {
String fileName = file.getName();
byte[] cwdBytes = Utils.readContents(file);
File addedFile = Utils.join(ADD_STAGE_FOLDER, fileName);
File removedFile = Utils.join(REMOVE_STAGE_FOLDER, fileName);
//Tracked in current commit but changed in working directory
if (headCommit.getBlobSha(fileName) != null) {
File comBlob = Utils.join(BLOB_FOLDER, headCommit.getBlobSha(fileName));
Blob commitBlob = Utils.readObject(comBlob, Blob.class);
if (comBlob.exists() && fileHasChanged(commitBlob.getBlobContents(), cwdBytes) && !addedFile.exists()) {
trackedModified.add(fileName);
}
} else if (addedFile.isFile() && fileHasChanged(file, cwdBytes)) {
//Staged for addition, but with different contents than working directory
trackedModified.add(fileName);
} else if (!addedFile.isFile() && !trackedModified.contains(fileName)
&& !trackedDeleted.contains(fileName) && !getHeadCommit().getBlobMap().containsKey(fileName)) {
untracked.add(fileName);
}
}
}
}
public static boolean inAddStage(String filename) {
return new File(ADD_STAGE_FOLDER, filename).exists();
}
public static boolean isCommitBlob(String filename) {
return getHeadCommit().getBlobSha(filename) != null;
}
public static String getLongID(String commitID) {
if (commitID.equals("headPointer")) {
return Utils.readContentsAsString(Utils.join(COMMIT_FOLDER, commitID));
}
if (commitID.length() < 40) {
for (File file : COMMIT_FOLDER.listFiles()) {
String shaID = file.getName();
String shortUID = shaID.substring(0, commitID.length());
if (commitID.equals(shortUID)) {
return shaID;
}
}
}
return "";
}
/**
* Returns the arrayList of split points of the given branch
*/
public static ArrayList<String> getBranchSplitPoints(String branch) {
File splitPoint = Utils.join(BRANCH_SPLITS, branch);
return Utils.readObject(splitPoint, ArrayList.class);
}
public static void addPrevPointer(File currCommit, String newCommitID) {
if (currCommit.isFile() && newCommitID != null) {
Commit newCommit = Utils.readObject(currCommit, Commit.class);
newCommit.parentCommitID.add(newCommitID);
Utils.writeObject(currCommit, newCommit);
}
}
public static boolean isBeforeCommit (String commitID1, String commitID2) {
String iteratePointer = commitID1;
while (!iteratePointer.equals("")) {
Commit commit = getCommit(iteratePointer);
if (commit.getParentCommitID().get(0).equals(commitID2)){
return true;
}
iteratePointer = commit.getParentCommitID().get(0);
}
return false;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment