diff --git a/README.md b/README.md index 4314260..5eeb441 100644 --- a/README.md +++ b/README.md @@ -1 +1,8 @@ -# java_homeworks \ No newline at end of file +## Мой GIT. +Пример использования: + +``` + +./mygit commit "добавлен первый файл в репозиторий" + +``` diff --git a/hw_git/mygit b/hw_git/mygit new file mode 100755 index 0000000..d59a813 --- /dev/null +++ b/hw_git/mygit @@ -0,0 +1,3 @@ +#!/bin/bash + +java -jar target/hw_git-0.0.1-SNAPSHOT.jar "$@" diff --git a/hw_git/pom.xml b/hw_git/pom.xml new file mode 100644 index 0000000..2402663 --- /dev/null +++ b/hw_git/pom.xml @@ -0,0 +1,103 @@ + + 4.0.0 + + 1 + hw_git + 0.0.1-SNAPSHOT + jar + + hw_git + http://maven.apache.org + + + UTF-8 + + + + + maven-compiler-plugin + + 1.8 + 1.8 + UTF-8 + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.0.0-M1 + + + org.apache.maven.plugins + maven-jar-plugin + 3.1.0 + + + + true + hw_git.GitCli + hw_git + true + lib/ + + + + + + org.apache.maven.plugins + maven-dependency-plugin + 3.1.0 + + + copy-dependencies + package + + copy-dependencies + + + compile + ${project.build.directory}/lib + + + + + + + + + + junit + junit + 4.12 + test + + + + + + com.fasterxml.jackson.core + jackson-core + 2.9.6 + + + + com.fasterxml.jackson.core + jackson-annotations + 2.9.6 + + + + com.fasterxml.jackson.core + jackson-databind + 2.9.6 + + + + + org.apache.commons + commons-io + 1.3.2 + + + diff --git a/hw_git/src/main/java/hw_git/BranchProblemException.java b/hw_git/src/main/java/hw_git/BranchProblemException.java new file mode 100644 index 0000000..29024a1 --- /dev/null +++ b/hw_git/src/main/java/hw_git/BranchProblemException.java @@ -0,0 +1,8 @@ +package hw_git; + +public class BranchProblemException extends Exception { + String message; + public BranchProblemException(String s) { + message = s; + } +} diff --git a/hw_git/src/main/java/hw_git/GitCli.java b/hw_git/src/main/java/hw_git/GitCli.java new file mode 100644 index 0000000..82c3528 --- /dev/null +++ b/hw_git/src/main/java/hw_git/GitCli.java @@ -0,0 +1,125 @@ +package hw_git; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Scanner; +import java.util.stream.Collectors; + +import com.fasterxml.jackson.core.JsonGenerationException; +import com.fasterxml.jackson.databind.JsonMappingException; + +public class GitCli { + public static void printFile(Path p) { + try (Scanner in = new Scanner(new File(p.toString()))) { + System.out.println(p.toString()); + System.out.println("--------------------------------"); + while (in.hasNextLine()) { + System.out.println(in.nextLine()); + } + System.out.println("--------------------------------"); + } catch (FileNotFoundException e) { + System.out.println("File " + p.toString() + " not found"); + } + } + + public static void main(String[] args) throws JsonGenerationException, JsonMappingException { + System.out.println( + processArgs(args) + .stream() + .collect( + Collectors.joining("\n") + ) + ); + } + + public static ArrayList processArgs(String[] args) throws JsonGenerationException, JsonMappingException{ + + ArrayList res = new ArrayList<>(); + + GitCore core = new GitCore(); + int revision; + + try { + switch (args[0]) { + case "init": + core.makeInit(); + res.add("Repository initiated."); + break; + case "add": + res.add("Addition..."); + core.makeAdd(Arrays.copyOfRange(args, 1, args.length)); + break; + case "commit": + res.add("Commiting..."); + core.makeCommit(args[1]); + res.add("Commit made at revision " + (core.getCurrentRevision() + 1)); + break; + case "checkout": + if (args[1].equals("--")) { + res.add("Checking out files..."); + core.makeCheckout(Arrays.copyOfRange(args, 2, args.length)); + } else { + try { + revision = Integer.parseInt(args[1]); + res.add("Checkout to revision " + revision); + core.makeCheckout(revision - 1); + res.add("HEAD detached on revison " + revision); + } catch (NumberFormatException e) { + res.add("Checking out branch..."); + core.makeCheckout(args[1]); + } + } + break; + case "reset": + revision = Integer.parseInt(args[1]); + res.add("Performing reset to revision " + revision); + core.makeReset(revision - 1); + break; + case "log": + revision = args.length == 2 ? Integer.parseInt(args[1]) : 0; + res.add("Log:"); + res.addAll(core.getLog(revision - 1)); + break; + case "rm": + res.add("Removing..."); + core.makeRM(Arrays.copyOfRange(args, 1, args.length)); + break; + case "status": + res.addAll(core.getStatus()); + break; + case "branch": + if (args[1].equals("-d")) { + res.add("Deleting branch " + args[2]); + core.makeDeleteBranch(args[2]); + } else { + res.add("Making branch " + args[1]); + core.makeBranch(args[1]); + } + break; + case "merge": + res.add("Merging branch " + args[1] + " to current state." + + "\nYou should make commit then."); + res.addAll(core.makeMerge(args[1])); + break; + default: + res.add("Unknown argument: " + args[0]); + } + } catch (UnversionedException e) { + res.add("This directory is not versioned"); + } catch (BranchProblemException e) { + res.add(e.message); + } catch (FileNotFoundException e) { + res.add(e.getMessage()); + } catch (IOException e) { + res.add("IOException: " + e.getMessage()); + } catch (ArrayIndexOutOfBoundsException e) { + res.add("Lack of arguments"); + } + + return res; + } +} diff --git a/hw_git/src/main/java/hw_git/GitCore.java b/hw_git/src/main/java/hw_git/GitCore.java new file mode 100644 index 0000000..f07862f --- /dev/null +++ b/hw_git/src/main/java/hw_git/GitCore.java @@ -0,0 +1,600 @@ +package hw_git; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.io.PrintWriter; +import java.nio.file.FileVisitResult; +import java.nio.file.FileVisitor; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.attribute.BasicFileAttributes; +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.TreeMap; +import java.util.TreeSet; +import java.util.stream.Collectors; + +import org.apache.commons.io.FileUtils; + +import com.fasterxml.jackson.core.JsonGenerationException; +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +public class GitCore { + private RepInformation inform = null; + private Path informPath = null; + private final String infoFileName = ".myGitDataFile"; + private final String storageFolder = ".myGitDataStorage"; + private final String stageFolder = ".stageData"; + + void findRepInformation() throws JsonParseException, JsonMappingException, IOException, UnversionedException { + RepInformation result = null; + Path p = Paths.get(""); + + while (p != null && !Files.exists(p.resolve(infoFileName))) { + p = p.getParent(); + } + + if (p != null) { + ObjectMapper omapper = new ObjectMapper(); + result = omapper.readValue(p.resolve(infoFileName).toFile(), RepInformation.class); + } + + if (result == null) { + throw new UnversionedException(); + } + inform = result; + informPath = p; + } + + private void updateRepInformation() throws JsonGenerationException, JsonMappingException, IOException { + ObjectMapper omapper = new ObjectMapper(); + omapper.writeValue(informPath.resolve(infoFileName).toFile(), inform); + } + + void makeInit() throws JsonGenerationException, JsonMappingException, IOException, UnversionedException { + try { + findRepInformation(); + } catch (UnversionedException e) { + informPath = Paths.get(""); + inform = new RepInformation(); + updateRepInformation(); + } + } + + void addAtIdx(ArrayList list, int idx, int inc) { + list.set(idx, list.get(idx) + inc); + } + + private Path getKeyPath(String filename) { + return informPath.relativize(Paths.get(filename)); + } + + private Path getStoragePath(Path keyPath, int revision) { + return informPath.resolve(storageFolder) + .resolve(keyPath.toString() + "r" + revision); + } + + private Path cutRoot(Path p) { + return p.subpath(1, p.getNameCount()); + } + + private void addFileDuringCommit(Path filepath) throws IOException { + int revision = inform.revision; + Path keyPath = cutRoot(filepath); + Path storage = getStoragePath(keyPath, revision); + storage.getParent().toFile().mkdirs(); + Files.copy(filepath, storage); + String keyName = keyPath.toString(); + if (!inform.fileNumber.containsKey(keyName)) { + System.out.println(keyName); + inform.fileNumber.put(keyName, inform.nFiles++); + } + + Set currentRevisionFiles = inform.commitedFiles.get(revision); + assert inform.fileNumber.get(keyName) != null; + currentRevisionFiles.add(inform.fileNumber.get(keyName)); + + } + + void makeAdd(String[] filenames) throws IOException, UnversionedException { + findRepInformation(); + for (String fname : filenames) { + Path orig = getKeyPath(fname); + Path dest = informPath.resolve(stageFolder).resolve(orig); + FileUtils.copyFile(orig.toFile(), dest.toFile()); + } + } + + void makeCommit(String message) throws IOException, UnversionedException, BranchProblemException { + findRepInformation(); + + inform.revision = inform.nCommits; + inform.commitMessages.add(message); + inform.timestamps.add(new Timestamp(System.currentTimeMillis())); + + { + inform.nCommits++; + inform.commitedFiles.add(new TreeSet<>()); + inform.removedFiles.add(new TreeSet<>()); + inform.prevCommit.add(inform.branchEnds.get(inform.currentBranchNumber)); + inform.branchEnds.put(inform.currentBranchNumber, inform.revision); + inform.numberOfStartedBranchesAtRevision.add(0); + } + + inform.removedFiles.get(inform.revision).addAll(inform.stageRemovedFiles); + inform.stageRemovedFiles.clear(); + + Files.walkFileTree( + informPath.resolve(stageFolder), + new FileVisitor() { + + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + addFileDuringCommit(file); + Files.delete(file); + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException { + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { + return FileVisitResult.CONTINUE; + } + }); + + updateRepInformation(); + + + if (inform.currentBranchNumber == -1) { + throw new BranchProblemException("Staying not at end of some branch."); + } + + } + + private void deleteVersionedFiles(File root) { + if (root.isFile()) { + String key = informPath.toAbsolutePath() + .relativize(Paths.get(root.getAbsolutePath())) + .toString(); + + if (inform.fileNumber.containsKey(key)) { + root.delete(); + } + return; + } + if (root.isDirectory()) { + for (File f : root.listFiles()) { + if (f.getName().equals(infoFileName) || f.getName().equals(storageFolder)) { + continue; + } + deleteVersionedFiles(f); + } + } + } + + private void collectVersionedFiles(int revision, Map filesToRestore) throws IOException { + if (revision < 0) { + return; + } + + collectVersionedFiles(inform.prevCommit.get(revision), filesToRestore); + + for (Integer fileNumber : inform.commitedFiles.get(revision)) { + filesToRestore.put(fileNumber, revision); + } + + for (Integer fileNumber : inform.removedFiles.get(revision)) { + filesToRestore.remove(fileNumber); + } + + } + + private void restoreFile(Integer fileNumber, Integer revision) throws IOException { + String keyName = RepInformation.getKeyByValue(fileNumber, inform.fileNumber); + FileUtils.copyFile( + informPath.resolve(getStoragePath(Paths.get(keyName), revision)).toFile(), + informPath.resolve(keyName).toFile() + ); + } + + void makeCheckout(int revision) throws IOException, UnversionedException, BranchProblemException { + findRepInformation(); + if (revision >= inform.nCommits || revision < 0) { + throw new BranchProblemException("revision number" + (revision + 1) + " is incorrect"); + } + + deleteVersionedFiles(informPath.toAbsolutePath().toFile()); + + Map filesToRestore = new TreeMap<>(); + collectVersionedFiles(revision, filesToRestore); + + for (Entry fileEntry : filesToRestore.entrySet()) { + restoreFile(fileEntry.getKey(), fileEntry.getValue()); + } + + inform.revision = revision; + inform.detachedHeadRevision = revision; + inform.currentBranchNumber = -1; + + updateRepInformation(); + } + + void makeCheckout(String branchName) throws JsonParseException, JsonMappingException, IOException, UnversionedException, BranchProblemException { + findRepInformation(); + Integer branchNumber = inform.branchNumbers.get(branchName); + if (branchNumber == null) { + throw new BranchProblemException("Branch not exists: " + branchName); + } + + int branchRevision = inform.branchEnds.get(branchNumber); + + try { + makeCheckout(branchRevision); + } catch (BranchProblemException e) {} + + inform.currentBranchNumber = branchNumber; + updateRepInformation(); + } + + private int getLastRevisionOfFile(String keyName) { + int revision = inform.revision; + while (revision >= 0 && !inform.commitedFiles.get(revision) + .contains(inform.fileNumber.get(keyName))) { + revision = inform.prevCommit.get(revision); + } + return revision; + } + + private void checkoutFile(Path keyPath) throws IOException { + int revision = getLastRevisionOfFile(keyPath.toString()); + if (revision == -1) { + throw new FileNotFoundException("No file " + keyPath.toString() + " found"); + } + FileUtils.copyFile(getStoragePath(keyPath, revision).toFile(), + informPath.resolve(keyPath).toFile()); + } + + void makeCheckout(String[] files) throws JsonParseException, JsonMappingException, IOException, UnversionedException { + findRepInformation(); + for (String fname : files) { + System.out.println("checkout key: " + fname); + checkoutFile(getKeyPath(fname)); + } + } + + void makeReset(int revision) throws JsonParseException, JsonMappingException, IOException, UnversionedException, BranchProblemException { + findRepInformation(); + + int currBranchT = inform.currentBranchNumber; + int currDetachedPosT = inform.detachedHeadRevision; + makeCheckout(revision); + + FileUtils.cleanDirectory(Paths.get(stageFolder).toFile()); + inform.stageRemovedFiles.clear(); + + if (currBranchT != -1) { + inform.currentBranchNumber = currBranchT; + inform.branchEnds.put(currBranchT, revision); + } + inform.detachedHeadRevision = currDetachedPosT; + + updateRepInformation(); + } + + ArrayList getLog(int revision) throws JsonParseException, JsonMappingException, IOException, UnversionedException, BranchProblemException { + findRepInformation(); + + ArrayList result = new ArrayList<>(); + + if (revision < -1 || revision >= inform.nCommits) { + throw new BranchProblemException("revision number " + (revision + 1) + " is incorrect"); + } + + if (revision == -1) { + revision = inform.revision; + } + + if(revision == -1) { + result.add("Empty log"); + return result; + } + + while (revision >= 0) { + result.add( + "\nrevision: " + (revision + 1) + "\n" + + inform.commitMessages.get(revision) + ); + result.add(inform.timestamps.get(revision) + "\n"); + + revision = inform.prevCommit.get(revision); + } + + return result; + } + + List getDeletedFiles() throws JsonParseException, JsonMappingException, IOException, UnversionedException { + findRepInformation(); + + return inform.stageRemovedFiles.stream() + .map(i -> RepInformation.getKeyByValue(i, inform.fileNumber)) + .collect(Collectors.toList()); + } + + + List getChangedFiles() throws JsonParseException, JsonMappingException, IOException, UnversionedException { + ArrayList result = new ArrayList<>(); + findRepInformation(); + + int revision = inform.revision; + TreeMap filesToRestore = new TreeMap<>(); + collectVersionedFiles(revision, filesToRestore); + for (Entry fileEnt : filesToRestore.entrySet()) { + String keyName = RepInformation.getKeyByValue( + fileEnt.getKey(), inform.fileNumber); + if (!FileUtils.contentEquals( + informPath.resolve(keyName).toFile(), + getStoragePath(Paths.get(keyName), fileEnt.getValue()).toFile())) + { + result.add(keyName); + } + } + return result; + } + + List getStagedFiles() throws JsonParseException, JsonMappingException, IOException, UnversionedException { + ArrayList result = new ArrayList<>(); + Files.walkFileTree(informPath.resolve(stageFolder), new FileVisitor() { + + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + result.add(cutRoot(file).toString()); + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException { + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { + return FileVisitResult.CONTINUE; + } + }); + + return result; + } + + List getUntrackedFiles() throws IOException { + ArrayList result = new ArrayList<>(); + Files.walkFileTree(Paths.get(""), new FileVisitor() { + + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { + return dir.equals(Paths.get(stageFolder)) || dir.equals(Paths.get(storageFolder)) + ? FileVisitResult.SKIP_SUBTREE : FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + if (!inform.fileNumber.containsKey(informPath.relativize(file).toString())) { + result.add(file.toString()); + } + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException { + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { + return FileVisitResult.CONTINUE; + } + }); + + return result; + } + + int getCurrentRevision() { + return inform.revision; + } + + + private void removeFromRep(String filename) throws IOException { + Path keypath = getKeyPath(filename); + + Integer fileNumber = inform.fileNumber.get(keypath.toString()); + + if (fileNumber == null) { + throw new FileNotFoundException("File not versioned: " + filename); + } + + inform.stageRemovedFiles.add(fileNumber); + + Files.deleteIfExists(informPath.resolve(stageFolder).resolve(keypath)); + } + + void makeRM(String[] files) throws IOException, UnversionedException { + findRepInformation(); + for (String filename : files) { + removeFromRep(filename); + } + updateRepInformation(); + } + + void makeBranch(String branchName) throws JsonParseException, JsonMappingException, IOException, UnversionedException, BranchProblemException { + findRepInformation(); + if (inform.branchNumbers.containsKey(branchName)) { + throw new BranchProblemException("Branch already exists: " + branchName); + } + + Integer newBranchNumber = inform.nBranches++; + inform.branchNumbers.put(branchName, newBranchNumber); + inform.branchEnds.put(newBranchNumber, inform.revision); + addAtIdx(inform.numberOfStartedBranchesAtRevision, inform.revision, 1); + updateRepInformation(); + } + + void makeDeleteBranch(String branchName) throws JsonParseException, JsonMappingException, IOException, UnversionedException, BranchProblemException { + + findRepInformation(); + Integer branchNumber = inform.branchNumbers.get(branchName); + + if (branchNumber == null) { + throw new BranchProblemException("No such branch: " + branchName); + } + + if (branchNumber.equals(inform.currentBranchNumber)) { + throw new BranchProblemException("You can't delete this branch while staying on it."); + } + + inform.branchEnds.remove(branchNumber); + + inform.branchNumbers.remove(branchName); + + updateRepInformation(); + } + + private int findLCA(int u, int v) { + int ulen = 0, vlen = 0; + for (int u1 = u; u1 >= 0; u1 = inform.prevCommit.get(u1), ulen++); + for (int v1 = v; v1 >= 0; v1 = inform.prevCommit.get(v1), vlen++); + + for (;ulen > vlen; ulen--, u = inform.prevCommit.get(u)); + for (;vlen > ulen; vlen--, v = inform.prevCommit.get(v)); + + for(; u != v; + u = inform.prevCommit.get(u), + v = inform.prevCommit.get(v)); + return u; + } + + ArrayList makeMerge(String otherBranchName) throws JsonParseException, JsonMappingException, IOException, UnversionedException, BranchProblemException { + findRepInformation(); + + Integer otherBranchNumber = inform.branchNumbers.get(otherBranchName); + if (otherBranchNumber == null) { + throw new BranchProblemException("No branch with name " + otherBranchName + " found."); + } + + ArrayList res = new ArrayList<>(); + res.add("Please, resolve conflicts in these files, and \"add\" them to commit:"); + + int currRev = inform.revision; + int otherRev = inform.branchEnds.get(otherBranchNumber); + + //fileNumber, revision + Map + filesToRestoreCurrent = new TreeMap<>(), + filesToRestoreOther = new TreeMap<>(); + + collectVersionedFiles(currRev, filesToRestoreCurrent); + collectVersionedFiles(otherRev, filesToRestoreOther); + + Integer lca = findLCA(currRev, otherRev); + + for (Entry fileEntry : filesToRestoreCurrent.entrySet()) { + Integer thisEntRevision = fileEntry.getValue(); + Integer otherEntRevision = filesToRestoreOther.get(fileEntry.getKey()); + Integer entFileNum = fileEntry.getKey(); + String keyName = RepInformation.getKeyByValue(entFileNum, inform.fileNumber); + + if (otherEntRevision == null + || otherEntRevision <= lca) { + } else if (thisEntRevision <= lca && otherEntRevision > lca) { + restoreFile(entFileNum, otherEntRevision); + makeAdd(new String[] {keyName}); + } else { + + File fCurr = getStoragePath(Paths.get(keyName), thisEntRevision).toFile(); + File fOth = getStoragePath(Paths.get(keyName), otherEntRevision).toFile(); + + try (PrintWriter out = new PrintWriter(new File(keyName))) { + out.println("===============Content from revision " + (thisEntRevision + 1) + " ========"); + try (BufferedReader in = new BufferedReader(new FileReader(fCurr))) { + in.lines().forEach(l -> out.println(l)); + } + out.println("===============Content from revision " + (otherEntRevision + 1) + " ========"); + try (BufferedReader in = new BufferedReader(new FileReader(fOth))) { + in.lines().forEach(l -> out.println(l)); + } + } + + res.add(keyName); + + } + } + + for (Entry fileEntry : filesToRestoreOther.entrySet()) { + if (!filesToRestoreCurrent.containsKey(fileEntry.getKey())) { + restoreFile(fileEntry.getKey(), fileEntry.getValue()); + String keyName = RepInformation.getKeyByValue( + fileEntry.getKey(), inform.fileNumber); + makeAdd(new String[] {keyName}); + } + } + + updateRepInformation(); + + if (res.size() == 1) + res.clear(); + return res; + } + + String getCurrentBranchName() throws JsonParseException, JsonMappingException, IOException, UnversionedException { + findRepInformation(); + return inform.getCurrentBranchName(); + } + + public ArrayList getStatus() throws JsonParseException, JsonMappingException, IOException, UnversionedException { + ArrayList result = new ArrayList<>(); + + findRepInformation(); + result.add("Status:"); + if (inform.currentBranchNumber != -1) { + result.add("branch " + inform.getCurrentBranchName()); + } else { + result.add("detached head at revision " + (inform.detachedHeadRevision + 1)); + } + + result.add("Staged files:\n________________"); + result.addAll(getStagedFiles()); + + result.add("Deleted files:\n________________"); + result.addAll(getDeletedFiles()); + + result.add("Changed files:\n________________"); + result.addAll(getChangedFiles()); + + result.add("Untracked files:\n________________"); + result.addAll(getUntrackedFiles()); + + return result; + } +} diff --git a/hw_git/src/main/java/hw_git/RepInformation.java b/hw_git/src/main/java/hw_git/RepInformation.java new file mode 100644 index 0000000..9e7d942 --- /dev/null +++ b/hw_git/src/main/java/hw_git/RepInformation.java @@ -0,0 +1,193 @@ +package hw_git; + +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.TreeMap; + +public class RepInformation { + + int revision = -1; + int currentBranchNumber = 0; + int detachedHeadRevision; + + int nCommits = 0; + int nFiles = 0; + int nBranches = 1; + + ArrayList commitMessages = new ArrayList<>(); + + ArrayList timestamps = new ArrayList<>(); + + ArrayList prevCommit = new ArrayList<>(); + + ArrayList> commitedFiles = new ArrayList<>(); + + ArrayList> removedFiles = new ArrayList<>(); + + TreeMap fileNumber = new TreeMap<>(); + + TreeMap branchNumbers = new TreeMap<>(); + + TreeMap branchEnds = new TreeMap<>(); + + ArrayList numberOfStartedBranchesAtRevision = new ArrayList<>(); + + ArrayList stageRemovedFiles = new ArrayList<>(); + + public RepInformation() { + branchNumbers.put("master", 0); + branchEnds.put(0, -1); + } + + static K getKeyByValue(V i, Map map) { + for (Entry ent : map.entrySet()) { + if (ent.getValue().equals(i)) { + return ent.getKey(); + } + } + + return null; + } + + String getCurrentBranchName() { + return getKeyByValue(currentBranchNumber, branchNumbers); + } + + int currentBranchLastRevision() { + return branchEnds.get(currentBranchNumber); + } + +// Automatic getters and setters + + public int getRevision() { + return revision; + } + + public void setRevision(int revision) { + this.revision = revision; + } + + public int getCurrentBranchNumber() { + return currentBranchNumber; + } + + public void setCurrentBranchNumber(int currentBranchNumber) { + this.currentBranchNumber = currentBranchNumber; + } + + public int getnCommits() { + return nCommits; + } + + public void setnCommits(int nCommits) { + this.nCommits = nCommits; + } + + public ArrayList getCommitMessages() { + return commitMessages; + } + + public void setCommitMessages(ArrayList commitMessages) { + this.commitMessages = commitMessages; + } + + public ArrayList getTimestamps() { + return timestamps; + } + + public void setTimestamps(ArrayList timestamps) { + this.timestamps = timestamps; + } + + public ArrayList getPrevCommit() { + return prevCommit; + } + + public void setPrevCommit(ArrayList prevCommit) { + this.prevCommit = prevCommit; + } + + public ArrayList> getCommitedFiles() { + return commitedFiles; + } + + public void setCommitedFiles(ArrayList> commitedFiles) { + this.commitedFiles = commitedFiles; + } + + public ArrayList> getRemovedFiles() { + return removedFiles; + } + + public void setRemovedFiles(ArrayList> removedFiles) { + this.removedFiles = removedFiles; + } + + public TreeMap getFileNumber() { + return fileNumber; + } + + public void setFileNumber(TreeMap fileNumber) { + this.fileNumber = fileNumber; + } + + public TreeMap getBranchNumbers() { + return branchNumbers; + } + + public void setBranchNumbers(TreeMap branchNumbers) { + this.branchNumbers = branchNumbers; + } + + public TreeMap getBranchEnds() { + return branchEnds; + } + + public void setBranchEnds(TreeMap branchEnds) { + this.branchEnds = branchEnds; + } + + public int getnFiles() { + return nFiles; + } + + public void setnFiles(int nFiles) { + this.nFiles = nFiles; + } + + public int getnBranches() { + return nBranches; + } + + public void setnBranches(int lastBranchNumber) { + this.nBranches = lastBranchNumber; + } + + public ArrayList getNumberOfStartedBranchesAtRevision() { + return numberOfStartedBranchesAtRevision; + } + + public void setNumberOfStartedBranchesAtRevision(ArrayList numberOfStartedBrancesAtRevision) { + this.numberOfStartedBranchesAtRevision = numberOfStartedBrancesAtRevision; + } + + public int getDetachedHeadRevision() { + return detachedHeadRevision; + } + + public void setDetachedHeadRevision(int detachedHeadRevision) { + this.detachedHeadRevision = detachedHeadRevision; + } + + public ArrayList getStageRemovedFiles() { + return stageRemovedFiles; + } + + public void setStageRemovedFiles(ArrayList stageRemovedFiles) { + this.stageRemovedFiles = stageRemovedFiles; + } + +} diff --git a/hw_git/src/main/java/hw_git/UnversionedException.java b/hw_git/src/main/java/hw_git/UnversionedException.java new file mode 100644 index 0000000..33f9cf9 --- /dev/null +++ b/hw_git/src/main/java/hw_git/UnversionedException.java @@ -0,0 +1,5 @@ +package hw_git; + +public class UnversionedException extends Exception { + +} diff --git a/hw_git/src/test/java/hw_git/AppTest.java b/hw_git/src/test/java/hw_git/AppTest.java new file mode 100644 index 0000000..3069599 --- /dev/null +++ b/hw_git/src/test/java/hw_git/AppTest.java @@ -0,0 +1,379 @@ +package hw_git; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.PrintWriter; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Scanner; +import org.apache.commons.io.FileUtils; + + +import com.fasterxml.jackson.core.JsonGenerationException; +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.databind.JsonMappingException; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +public class AppTest extends Assert { + + public static final String storageFilename = ".myGitDataFile"; + public static final String storageFolder = ".myGitDataStorage"; + public static final String stageFolder = ".stageData"; + + //files + final String file1 = "testdir/file.txt"; + final String file2 = "testdir/dir1/file_d1.txt"; + + @Before + public void setUp() throws IOException { + //System.out.println("setUp"); + Files.createDirectories(Paths.get("testdir/dir1")); + Files.createFile(Paths.get(file2)); + Files.createFile(Paths.get(file1)); + Files.createFile(Paths.get("one.txt")); + } + + @After + public void tearDown() throws IOException { + //System.out.println("tearDown"); + FileUtils.deleteDirectory(Paths.get("testdir").toFile()); + FileUtils.deleteDirectory(Paths.get(storageFolder).toFile()); + FileUtils.deleteDirectory(Paths.get(stageFolder).toFile()); + if (Files.exists(Paths.get(storageFilename))) { + Files.delete(Paths.get(storageFilename)); + } + if (Files.exists(Paths.get("one.txt"))) { + Files.delete(Paths.get("one.txt")); + } + } + + @Test + public void testInformationLoad() throws JsonGenerationException, JsonMappingException, IOException, UnversionedException { + GitCli.main(new String[] {"init"}); + GitCore core = new GitCore(); + core.findRepInformation(); + assertEquals(core.getCurrentRevision(), -1); + } + + @Test(expected = UnversionedException.class) + public void testUnversioned() throws IOException, UnversionedException { + GitCore core = new GitCore(); + core.findRepInformation(); + } + + @Test + public void testCheckout() throws JsonGenerationException, JsonMappingException, IOException { + GitCli.main(new String[] {"init"}); + GitCli.main(new String[] {"add", file1}); + GitCli.main(new String[] {"commit", "message 1"}); + GitCli.main(new String[] {"add", file2}); + GitCli.main(new String[] {"commit", "message 2"}); + + GitCli.main(new String[] {"checkout", "1"}); + assertTrue(Files.exists(Paths.get(file1))); + assertFalse(Files.exists(Paths.get(file2))); + + GitCli.main(new String[] {"checkout", "2"}); + assertTrue(Files.exists(Paths.get(file1))); + assertTrue(Files.exists(Paths.get(file2))); + } + + @Test + public void testCheckoutFiles() throws JsonGenerationException, JsonMappingException, IOException { + GitCli.main(new String[] {"init"}); + try (PrintWriter out = new PrintWriter(new File(file1))) { + out.println("text 1"); + } + GitCli.main(new String[] {"add", file1}); + GitCli.main(new String[] {"commit", "message 1"}); + try (PrintWriter out = new PrintWriter(new File(file1))) { + out.println("text 2"); + } + + GitCli.main(new String[] {"checkout", "--", file1}); + try (Scanner in = new Scanner(new File(file1))) { + assertTrue(in.nextLine().equals("text 1")); + } + } + + @Test + public void testFileChangeBetweenCommits() throws JsonGenerationException, JsonMappingException, IOException { + GitCli.main(new String[] {"init"}); + try (PrintWriter out = new PrintWriter(new File(file1))) { + out.print("commit 1 content"); + } + GitCli.main(new String[] {"add", file1, file2}); + GitCli.main(new String[] {"commit", "message 1"}); + + try (PrintWriter out = new PrintWriter(new File(file1))) { + out.print("commit 2 content"); + } + + GitCli.main(new String[] {"add", file1}); + GitCli.main(new String[] {"commit", "message 2"}); + + GitCli.main(new String[] {"checkout", "1"}); + try (Scanner in = new Scanner(new File(file1))) { + assertEquals(in.nextLine(), "commit 1 content"); + } + + GitCli.main(new String[] {"checkout", "2"}); + try (Scanner in = new Scanner(new File(file1))) { + assertEquals(in.nextLine(), "commit 2 content"); + } + } + + @Test + public void testRM() throws JsonGenerationException, JsonMappingException, IOException { + GitCli.main(new String[] {"init"}); + GitCli.main(new String[] {"add", file1}); + GitCli.main(new String[] {"commit", "message 1"}); + GitCli.main(new String[] {"add", file1}); + GitCli.main(new String[] {"commit", "message 2"}); + Files.delete(Paths.get(file1)); + GitCli.main(new String[] {"rm", file1}); + GitCli.main(new String[] {"commit", "remove file1"}); + GitCli.main(new String[] {"checkout", "2"}); + assertTrue(Files.exists(Paths.get(file1))); + + GitCli.main(new String[] {"checkout", "1"}); + assertTrue(Files.exists(Paths.get(file1))); + GitCli.main(new String[] {"checkout", "master"}); + assertFalse(Files.exists(Paths.get(file1))); + } + + @Test + public void testStatus() throws JsonGenerationException, JsonMappingException, IOException, UnversionedException { + GitCli.main(new String[] {"init"}); + GitCli.main(new String[] {"add", file1}); + GitCli.main(new String[] {"commit", "message 1"}); + + GitCore core = new GitCore(); + core.findRepInformation(); + + assertEquals(core.getChangedFiles(), Arrays.asList()); + //assertEquals(core.getUntrackedFiles(), Arrays.asList(file2)); + assertEquals(core.getDeletedFiles(), Arrays.asList()); + + try (PrintWriter out = new PrintWriter(new File(file1))) { + out.print("commit 3 content"); + } + + assertEquals(core.getChangedFiles(), Arrays.asList(file1)); + + Files.delete(Paths.get(file1)); + + assertEquals(core.getChangedFiles(), Arrays.asList(file1)); + + } + + @Test + public void testAddBranch() throws UnversionedException, JsonParseException, IOException { + GitCore core = new GitCore(); + GitCli.main(new String[] {"init"}); + GitCli.main(new String[] {"add", file1}); + GitCli.main(new String[] {"commit", "message 1"}); + + assertEquals("master", core.getCurrentBranchName()); + + GitCli.main(new String[] {"branch", "b1"}); + + assertEquals("master", core.getCurrentBranchName()); + + GitCli.main(new String[] {"checkout", "b1"}); + + assertEquals("b1", core.getCurrentBranchName()); + } + + @Test + public void testFilesInBranches() throws JsonGenerationException, JsonMappingException, FileNotFoundException { + GitCli.main(new String[] {"init"}); + GitCli.main(new String[] {"add", file1}); + GitCli.main(new String[] {"commit", "message 1"}); + + GitCli.main(new String[] {"branch", "b1"}); + GitCli.main(new String[] {"checkout", "b1"}); + + try(PrintWriter out = new PrintWriter(new File(file2))) { + out.println("b1 content"); + } + GitCli.main(new String[] {"add", file2}); + GitCli.main(new String[] {"commit", "message 2"}); + + assertTrue(Files.exists(Paths.get(file1))); + assertTrue(Files.exists(Paths.get(file2))); + + GitCli.main(new String[] {"checkout", "master"}); + + assertFalse(Files.exists(Paths.get(file2))); + + GitCli.main(new String[] {"branch", "b2"}); + GitCli.main(new String[] {"checkout", "b2"}); + + try(PrintWriter out = new PrintWriter(new File(file2))) { + out.println("b2 content"); + } + GitCli.main(new String[] {"add", file2}); + GitCli.main(new String[] {"commit", "message 3"}); + + GitCli.main(new String[] {"checkout", "b1"}); + try (Scanner in = new Scanner(new File(file2))) { + assertEquals("b1 content", in.nextLine()); + } + GitCli.main(new String[] {"checkout", "b2"}); + try (Scanner in = new Scanner(new File(file2))) { + assertEquals("b2 content", in.nextLine()); + } + } + + public void testIncorrectBranchRm() throws JsonParseException, IOException, UnversionedException, BranchProblemException { + GitCli.main(new String[] {"init"}); + assertEquals( + "You can't delete this branch while staying on it.", + GitCli.processArgs(new String[] {"branch", "-d", "master"}) + .get(0)); + } + + @Test + public void testBranchDeleteAndRestore() throws JsonParseException, IOException, UnversionedException, BranchProblemException { + GitCli.main(new String[] {"init"}); + GitCli.main(new String[] {"add", file1}); + GitCli.main(new String[] {"commit", "message 1"}); + + GitCli.main(new String[] {"branch", "b1"}); + GitCli.main(new String[] {"checkout", "b1"}); + + GitCli.main(new String[] {"add", file2}); + GitCli.main(new String[] {"commit", "message 2"}); + + GitCli.main(new String[] {"checkout", "master"}); + GitCli.main(new String[] {"branch", "-d", "b1"}); + + GitCli.main(new String[] {"checkout", "0"}); + GitCli.main(new String[] {"branch", "b1"}); + GitCli.main(new String[] {"checkout", "b1"}); + + assertFalse(Files.exists(Paths.get(file2))); + assertTrue(Files.exists(Paths.get(file1))); + } + + @Test + public void testLogBasic() throws JsonParseException, IOException, UnversionedException, BranchProblemException { + GitCli.main(new String[] {"init"}); + GitCli.main(new String[] {"add", file1}); + + assertEquals("Empty log", + GitCli.processArgs(new String[] {"log"}).get(1)); + GitCli.main(new String[] {"commit", "message 1"}); + + assertEquals("\nrevision: 1\nmessage 1", + GitCli.processArgs(new String[] {"log"}).get(1)); + assertEquals( + GitCli.processArgs(new String[] {"log"}), + GitCli.processArgs(new String[] {"log", "1"})); + } + + + public void testIncorrectBigCheckoutRevision() throws JsonParseException, IOException, UnversionedException, BranchProblemException { + GitCli.main(new String[] {"init"}); + GitCli.main(new String[] {"add", file1}); + GitCli.main(new String[] {"commit", "message 1"}); + + assertEquals( + "revision number" + 2 + " is incorrect", + GitCli.processArgs(new String[] {"checkout", "2"}).get(0)); + + assertEquals( + "revision number" + 0 + " is incorrect", + GitCli.processArgs(new String[] {"checkout", "0"}).get(0)); + + assertEquals( + "revision number" + 2 + " is incorrect", + GitCli.processArgs(new String[] {"reset", "2"}).get(0)); + + assertEquals( + "revision number" + 0 + " is incorrect", + GitCli.processArgs(new String[] {"reset", "0"}).get(0)); + } + + @Test + public void testDetachedState() throws JsonParseException, IOException, UnversionedException, BranchProblemException { + GitCli.main(new String[] {"init"}); + GitCli.main(new String[] {"add", file1}); + GitCli.main(new String[] {"commit", "message 1"}); + + GitCli.main(new String[] {"add", file1}); + GitCli.main(new String[] {"commit", "message 2"}); + + assertEquals("HEAD detached on revison 1", + GitCli.processArgs(new String[] {"checkout", "1"}).get(1)); + + assertEquals("Staying not at end of some branch.", + GitCli.processArgs(new String[] {"commit", "abc"}).get(1)); + } + + @Test + public void testEmptyCommitNotFails() throws JsonParseException, IOException, UnversionedException, BranchProblemException { + GitCli.main(new String[] {"init"}); + GitCli.main(new String[] {"add", file1}); + GitCli.main(new String[] {"commit", "message 1"}); + + GitCli.main(new String[] {"commit", "message 2"}); + + ArrayList logRes = GitCli.processArgs(new String[] {"log"}); + assertEquals("\nrevision: 2\nmessage 2", logRes.get(1)); + assertEquals("\nrevision: 1\nmessage 1", logRes.get(3)); + } + + @Test + public void testCommitFileInRoot() throws JsonParseException, IOException, UnversionedException, BranchProblemException { + GitCli.main(new String[] {"init"}); + GitCli.main(new String[] {"add", "one.txt"}); + GitCli.main(new String[] {"commit", "message 2"}); + + ArrayList logRes = GitCli.processArgs(new String[] {"log"}); + assertEquals("\nrevision: 1\nmessage 2", logRes.get(1)); + } + + @Test + public void testResetClearsStage() throws JsonGenerationException, JsonMappingException { + GitCli.main(new String[] {"init"}); + GitCli.main(new String[] {"add", "one.txt"}); + GitCli.main(new String[] {"commit", "message 1"}); + + GitCli.main(new String[] {"add", "testdir/file.txt"}); + GitCli.main(new String[] {"rm", "one.txt"}); + + assertEquals( + Arrays.asList( + "Status:", + "branch master", + "Staged files:\n________________", + "testdir/file.txt", + "Deleted files:\n________________", + "one.txt", + "Changed files:\n________________", + "Untracked files:\n________________"), + GitCli.processArgs(new String[] {"status"}).subList(0, 8)); + + GitCli.main(new String[] {"reset", "1"}); + + assertEquals( + Arrays.asList( + "Status:", + "branch master", + "Staged files:\n________________", + "Deleted files:\n________________", + "Changed files:\n________________", + "Untracked files:\n________________"), + GitCli.processArgs(new String[] {"status"}).subList(0, 6)); + } + +} diff --git a/hw_git/src/test/java/hw_git/ComplexTests.java b/hw_git/src/test/java/hw_git/ComplexTests.java new file mode 100644 index 0000000..ac72a78 --- /dev/null +++ b/hw_git/src/test/java/hw_git/ComplexTests.java @@ -0,0 +1,572 @@ +package hw_git; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Test; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.PrintWriter; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Scanner; +import java.util.stream.Collectors; + +import org.apache.commons.io.FileUtils; + +public class ComplexTests extends Assert { + + final String testdir = "testdir/"; + static ArrayList removeTimestampsFromLog(ArrayList orig) { + ArrayList res = new ArrayList<>(); + res.add(orig.get(0)); + for (int i = 1; i < orig.size(); i += 2) { + res.add(orig.get(i)); + } + return res; + } + + @After + public void deleteTestFiles() throws IOException { + FileUtils.deleteDirectory(new File(testdir)); + FileUtils.deleteDirectory(new File(AppTest.storageFolder)); + FileUtils.deleteDirectory(new File(AppTest.stageFolder)); + FileUtils.forceDelete(new File(AppTest.storageFilename)); + } + + @Test + public void testCheckoutWithLogAndStatus() throws IOException { + FileUtils.touch(Paths.get(testdir).resolve("file1").toFile()); + + assertEquals("This directory is not versioned", + GitCli.processArgs(new String[] {"add", "testdir/file1"}).get(1)); + + assertEquals("This directory is not versioned", + GitCli.processArgs(new String[] {"status"}).get(0)); + assertEquals("This directory is not versioned", + GitCli.processArgs(new String[] {"log"}).get(1)); + + assertEquals("Repository initiated.", + GitCli.processArgs(new String[] {"init"}).get(0)); + + assertEquals( + Arrays.asList( + "Status:", + "branch master", + "Staged files:\n________________", + "Deleted files:\n________________", + "Changed files:\n________________", + "Untracked files:\n________________"), + GitCli.processArgs(new String[] {"status"}).subList(0, 6)); + + assertEquals("Empty log", + GitCli.processArgs(new String[] {"log"}).get(1)); + + GitCli.main(new String[] {"add", testdir + "file1"}); + + assertEquals("Empty log", + GitCli.processArgs(new String[] {"log"}).get(1)); + + + assertEquals( + Arrays.asList( + "Status:", + "branch master", + "Staged files:\n________________", + "testdir/file1", + "Deleted files:\n________________", + "Changed files:\n________________", + "Untracked files:\n________________"), + GitCli.processArgs(new String[] {"status"}).subList(0, 7)); + + GitCli.main(new String[] {"commit", "mes 1"}); + + assertEquals( + Arrays.asList( + "Log:", + "\nrevision: 1\nmes 1"), + GitCli.processArgs(new String[] {"log"}).subList(0, 2)); + + assertEquals( + Arrays.asList( + "Status:", + "branch master", + "Staged files:\n________________", + "Deleted files:\n________________", + "Changed files:\n________________", + "Untracked files:\n________________"), + GitCli.processArgs(new String[] {"status"}).subList(0, 6)); + + assertEquals(Arrays.asList( + "Commiting...", + "Commit made at revision 2"), + GitCli.processArgs(new String[] {"commit", "empty commit"})); + + assertEquals( + Arrays.asList( + "Log:", + "\nrevision: 2\nempty commit", + "\nrevision: 1\nmes 1"), + removeTimestampsFromLog(GitCli.processArgs(new String[] {"log"}))); + + assertEquals(Arrays.asList( + "Checkout to revision 1", + "HEAD detached on revison 1"), + GitCli.processArgs(new String[] {"checkout", "1"})); + + assertEquals( + Arrays.asList( + "Status:", + "detached head at revision 1", + "Staged files:\n________________", + "Deleted files:\n________________", + "Changed files:\n________________", + "Untracked files:\n________________"), + GitCli.processArgs(new String[] {"status"}).subList(0, 6)); + + assertEquals( + Arrays.asList( + "Log:", + "\nrevision: 1\nmes 1"), + removeTimestampsFromLog(GitCli.processArgs(new String[] {"log"}))); + + assertEquals(Arrays.asList( + "Checkout to revision 2", + "HEAD detached on revison 2"), + GitCli.processArgs(new String[] {"checkout", "2"})); + + assertEquals( + Arrays.asList( + "Status:", + "detached head at revision 2", + "Staged files:\n________________", + "Deleted files:\n________________", + "Changed files:\n________________", + "Untracked files:\n________________"), + GitCli.processArgs(new String[] {"status"}).subList(0, 6)); + + assertEquals( + Arrays.asList( + "Log:", + "\nrevision: 2\nempty commit", + "\nrevision: 1\nmes 1"), + removeTimestampsFromLog(GitCli.processArgs(new String[] {"log"}))); + + assertEquals(Arrays.asList( + "Checking out branch..."), + GitCli.processArgs(new String[] {"checkout", "master"})); + + assertEquals( + Arrays.asList( + "Log:", + "\nrevision: 2\nempty commit", + "\nrevision: 1\nmes 1"), + removeTimestampsFromLog(GitCli.processArgs(new String[] {"log"}))); + + GitCli.main(new String[] {"checkout", "1"}); + GitCli.main(new String[] {"branch", "b1"}); + GitCli.main(new String[] {"checkout", "b1"}); + + assertEquals( + Arrays.asList( + "Status:", + "branch b1", + "Staged files:\n________________", + "Deleted files:\n________________", + "Changed files:\n________________", + "Untracked files:\n________________"), + GitCli.processArgs(new String[] {"status"}).subList(0, 6)); + + assertEquals( + Arrays.asList( + "Log:", + "\nrevision: 1\nmes 1"), + removeTimestampsFromLog(GitCli.processArgs(new String[] {"log"}))); + + } + + @Test + public void testResetWithLogAndStatus() throws IOException { + Files.createDirectory(Paths.get(testdir)); + + try (PrintWriter out = new PrintWriter(new File("testdir/file1"))) { + out.println("initial content"); + } + + assertEquals("Repository initiated.", + GitCli.processArgs(new String[] {"init"}).get(0)); + + GitCli.main(new String[] {"add", "testdir/file1"}); + + GitCli.main(new String[] {"commit", "mes 1"}); + + try (PrintWriter out = new PrintWriter("testdir/file1")) { + out.println("revision 2 content"); + } + + GitCli.main(new String[] {"add", "testdir/file1"}); + + assertEquals(Arrays.asList( + "Commiting...", + "Commit made at revision 2"), + GitCli.processArgs(new String[] {"commit", "second commit"})); + + try (Scanner in = new Scanner(new File("testdir/file1"))) { + assertEquals("revision 2 content", in.nextLine()); + } + + assertEquals( + Arrays.asList( + "Performing reset to revision 1"), + removeTimestampsFromLog(GitCli.processArgs(new String[] {"reset", "1"}))); + + assertEquals( + Arrays.asList( + "Status:", + "branch master", + "Staged files:\n________________", + "Deleted files:\n________________", + "Changed files:\n________________", + "Untracked files:\n________________"), + GitCli.processArgs(new String[] {"status"}).subList(0, 6)); + + assertEquals( + Arrays.asList( + "Log:", + "\nrevision: 1\nmes 1"), + removeTimestampsFromLog(GitCli.processArgs(new String[] {"log"}))); + + try (Scanner in = new Scanner(new File("testdir/file1"))) { + assertEquals("initial content", in.nextLine()); + } + + try (PrintWriter out = new PrintWriter("testdir/file1")) { + out.println("reset state content"); + } + + assertEquals( + Arrays.asList( + "Status:", + "branch master", + "Staged files:\n________________", + "Deleted files:\n________________", + "Changed files:\n________________", + "testdir/file1", + "Untracked files:\n________________"), + GitCli.processArgs(new String[] {"status"}).subList(0, 7)); + + GitCli.main(new String[] {"add", "testdir/file1"}); + assertEquals(Arrays.asList( + "Commiting...", + "Commit made at revision 3"), + GitCli.processArgs(new String[] {"commit", "third commit"})); + + try (Scanner in = new Scanner(new File("testdir/file1"))) { + assertEquals("reset state content", in.nextLine()); + } + + assertEquals( + Arrays.asList( + "Log:", + "\nrevision: 3\nthird commit", + "\nrevision: 1\nmes 1"), + removeTimestampsFromLog(GitCli.processArgs(new String[] {"log"}))); + + GitCli.main(new String[] {"reset", "2"}); + + try (Scanner in = new Scanner(new File("testdir/file1"))) { + assertEquals("revision 2 content", in.nextLine()); + } + + assertEquals( + Arrays.asList( + "Status:", + "branch master", + "Staged files:\n________________", + "Deleted files:\n________________", + "Changed files:\n________________", + "Untracked files:\n________________"), + GitCli.processArgs(new String[] {"status"}).subList(0, 6)); + + GitCli.main(new String[] {"checkout", "1"}); + GitCli.main(new String[] {"reset", "3"}); + + assertEquals( + Arrays.asList( + "Log:", + "\nrevision: 3\nthird commit", + "\nrevision: 1\nmes 1"), + removeTimestampsFromLog(GitCli.processArgs(new String[] {"log"}))); + + assertEquals( + Arrays.asList( + "Status:", + "detached head at revision 1", + "Staged files:\n________________", + "Deleted files:\n________________", + "Changed files:\n________________", + "Untracked files:\n________________"), + GitCli.processArgs(new String[] {"status"}).subList(0, 6)); + + try (Scanner in = new Scanner(new File("testdir/file1"))) { + assertEquals("reset state content", in.nextLine()); + } + + GitCli.main(new String[] {"checkout", "master"}); + + try (Scanner in = new Scanner(new File("testdir/file1"))) { + assertEquals("revision 2 content", in.nextLine()); + } + } + + @Test + public void testBranchRM() throws IOException, UnversionedException, BranchProblemException { + Files.createDirectory(Paths.get(testdir)); + + try (PrintWriter out = new PrintWriter(new File("testdir/file1"))) { + out.println("initial content"); + } + + assertEquals("Repository initiated.", + GitCli.processArgs(new String[] {"init"}).get(0)); + + GitCli.main(new String[] {"add", "testdir/file1"}); + + GitCli.main(new String[] {"commit", "mes 1"}); + + try (PrintWriter out = new PrintWriter("testdir/file1")) { + out.println("revision 2 content"); + } + + GitCli.main(new String[] {"add", "testdir/file1"}); + + GitCli.main(new String[] {"commit", "mes 2"}); + GitCli.main(new String[] {"checkout", "1"}); + + GitCli.main(new String[] {"branch", "b1"}); + GitCli.main(new String[] {"checkout", "b1"}); + + try (PrintWriter out = new PrintWriter("testdir/file1")) { + out.println("revision 3 content"); + } + + GitCli.main(new String[] {"commit", "mes 3"}); + + ArrayList beforeRMLog = GitCli.processArgs(new String[] {"log"}); + assertEquals(beforeRMLog, GitCli.processArgs(new String[] {"log", "3"})); + + assertEquals( + Arrays.asList( + "Deleting branch b1", + "You can't delete this branch while staying on it."), + GitCli.processArgs(new String[] {"branch", "-d", "b1"})); + + GitCli.main(new String[] {"checkout", "master"}); + try (Scanner in = new Scanner(new File("testdir/file1"))) { + assertEquals("revision 2 content", in.nextLine()); + } + + assertEquals( + Arrays.asList( + "Deleting branch b1"), + GitCli.processArgs(new String[] {"branch", "-d", "b1"})); + + assertEquals(beforeRMLog, GitCli.processArgs(new String[] {"log", "3"})); + assertEquals( + Arrays.asList( + "Checking out branch...", + "Branch not exists: b1"), + GitCli.processArgs(new String[] {"checkout", "b1"})); + GitCli.main(new String[] {"checkout", "3"}); + + assertEquals(beforeRMLog, GitCli.processArgs(new String[] {"log"})); + GitCore core = new GitCore(); + core.makeBranch("b1"); + core.makeCheckout("b1"); + } + + @Test + public void testMergeNoConflict() throws IOException, UnversionedException, BranchProblemException { + Files.createDirectory(Paths.get(testdir)); + try (PrintWriter out = new PrintWriter(new File("testdir/file1"))) { + out.println("initial 1 content"); + } + try (PrintWriter out = new PrintWriter(new File("testdir/file2"))) { + out.println("initial 2 content"); + } + + GitCli.main(new String[] {"init"}); + GitCli.main(new String[] {"add", "testdir/file1", "testdir/file2"}); + GitCli.main(new String[] {"commit", "mes 1"}); + + try (PrintWriter out = new PrintWriter(new File("testdir/file2"))) { + out.println("2 content"); + } + + GitCli.main(new String[] {"add", "testdir/file2"}); + GitCli.main(new String[] {"commit", "mes 2"}); + + GitCli.main(new String[] {"checkout", "1"}); + GitCli.main(new String[] {"branch", "b1"}); + GitCli.main(new String[] {"checkout", "b1"}); + + try (PrintWriter out = new PrintWriter(new File("testdir/file3"))) { + out.println("3 content"); + } + + GitCli.main(new String[] {"add", "testdir/file3"}); + GitCli.main(new String[] {"commit", "mes 3"}); + + GitCli.main(new String[] {"checkout", "master"}); + + assertEquals(Arrays.asList("Merging branch b1 to current state." + + "\nYou should make commit then."), + GitCli.processArgs(new String[] {"merge", "b1"})); + + //new GitCore().makeMerge("b1"); + + assertEquals( + Arrays.asList( + "Status:", + "branch master", + "Staged files:\n________________", + "testdir/file3", + "Deleted files:\n________________", + "Changed files:\n________________", + "Untracked files:\n________________"), + GitCli.processArgs(new String[] {"status"}).subList(0, 7)); + } + + @Test + public void testMergeWithConflicts() throws IOException, UnversionedException, BranchProblemException { + Files.createDirectory(Paths.get(testdir)); + try (PrintWriter out = new PrintWriter(new File("testdir/file1"))) { + out.println("initial 1 content"); + } + + try (PrintWriter out = new PrintWriter(new File("testdir/file2"))) { + out.println("initial 2 content"); + } + + try (PrintWriter out = new PrintWriter(new File("testdir/file3"))) { + out.println("initial 3 content"); + } + + try (PrintWriter out = new PrintWriter(new File("testdir/file4"))) { + out.println("initial 4 content"); + } + + try (PrintWriter out = new PrintWriter(new File("testdir/file5"))) { + out.println("initial 5 content"); + } + + try (PrintWriter out = new PrintWriter(new File("testdir/file6"))) { + out.println("initial 6 content"); + } + + try (PrintWriter out = new PrintWriter(new File("testdir/file7"))) { + out.println("initial 7 content"); + } + + GitCli.main(new String[] {"init"}); + GitCli.main(new String[] {"add", "testdir/file1", "testdir/file2", "testdir/file5"}); + GitCli.main(new String[] {"add", "testdir/file3", "testdir/file4", "testdir/file7"}); + GitCli.main(new String[] {"commit", "mes 1"}); + + try (PrintWriter out = new PrintWriter(new File("testdir/file2"))) { + out.println("2 content"); + } + + try (PrintWriter out = new PrintWriter(new File("testdir/file4"))) { + out.println("4 content"); + } + + GitCli.main(new String[] {"rm", "testdir/file7"}); + GitCli.main(new String[] {"add", "testdir/file2", "testdir/file4"}); + GitCli.main(new String[] {"commit", "mes 2"}); + + GitCli.main(new String[] {"checkout", "1"}); + GitCli.main(new String[] {"branch", "b1"}); + GitCli.main(new String[] {"checkout", "b1"}); + + try (PrintWriter out = new PrintWriter(new File("testdir/file3"))) { + out.println("3 from b1 content"); + } + + GitCli.main(new String[] {"rm", "testdir/file5"}); + + try (PrintWriter out = new PrintWriter(new File("testdir/file4"))) { + out.println("4 b1 content"); + } + GitCli.main(new String[] {"add", "testdir/file3", "testdir/file4", "testdir/file6"}); + GitCli.main(new String[] {"commit", "mes 3"}); + + + GitCli.main(new String[] {"rm", "testdir/file6"}); + GitCli.main(new String[] {"commit", "mes 4"}); + + try (Scanner in = new Scanner(new File("testdir/file6"))) { + assertEquals("initial 6 content", in.nextLine()); + } + + GitCli.main(new String[] {"checkout", "master"}); + + assertEquals(Arrays.asList("Merging branch b1 to current state." + + "\nYou should make commit then.", + "Please, resolve conflicts in these files, and \"add\" them to commit:", + "testdir/file4"), + GitCli.processArgs(new String[] {"merge", "b1"})); + + //new GitCore().makeMerge("b1"); + + assertEquals( + Arrays.asList( + "Status:", + "branch master", + "Staged files:\n________________", + "testdir/file3", + "testdir/file7", + "Deleted files:\n________________", + "Changed files:\n________________", + "testdir/file3", + "testdir/file4", + "Untracked files:\n________________"), + GitCli.processArgs(new String[] {"status"}).subList(0, 10)); + + try (Scanner in = new Scanner(new File("testdir/file1"))) { + assertEquals("initial 1 content", in.nextLine()); + } + + try (Scanner in = new Scanner(new File("testdir/file2"))) { + assertEquals("2 content", in.nextLine()); + } + + try (Scanner in = new Scanner(new File("testdir/file3"))) { + assertEquals("3 from b1 content", in.nextLine()); + } + + try (BufferedReader in = new BufferedReader(new FileReader(new File("testdir/file4")))) { + + assertEquals(Arrays.asList( + "===============Content from revision 2 ========", + "4 content", + "===============Content from revision 3 ========", + "4 b1 content") + , in.lines().collect(Collectors.toList())); + } + + try (Scanner in = new Scanner(new File("testdir/file5"))) { + assertEquals("initial 5 content", in.nextLine()); + } + + assertFalse(Files.exists(Paths.get("testdir/file6"))); + + try (Scanner in = new Scanner(new File("testdir/file7"))) { + assertEquals("initial 7 content", in.nextLine()); + } + } +}