diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..e7d2d7e --- /dev/null +++ b/build.gradle @@ -0,0 +1,15 @@ +group 'com.github.nizshee' +version '1.0-SNAPSHOT' + +apply plugin: 'java' + +sourceCompatibility = 1.8 + +repositories { + mavenCentral() +} + +dependencies { + compile group: 'berkeleydb', name: 'je', version: '3.2.76' + testCompile group: 'junit', name: 'junit', version: '4.11' +} \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..9c2a576 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Fri Sep 16 16:31:37 MSK 2016 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-2.13-bin.zip diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..27309d9 --- /dev/null +++ b/gradlew @@ -0,0 +1,164 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..f6d5974 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..8f10ea3 --- /dev/null +++ b/settings.gradle @@ -0,0 +1,3 @@ +rootProject.name = 'lazy-factory' +rootProject.name = 'hw11' + diff --git a/src/main/java/com/github/nizshee/Main.java b/src/main/java/com/github/nizshee/Main.java new file mode 100644 index 0000000..1e9bab1 --- /dev/null +++ b/src/main/java/com/github/nizshee/Main.java @@ -0,0 +1,73 @@ +package com.github.nizshee; + + +import com.github.nizshee.cvs.CVS; +import com.github.nizshee.exception.StateException; +import com.github.nizshee.exception.WorkspaceException; +import com.github.nizshee.frontend.Frontend; +import com.github.nizshee.index.InMemoryIndex; +import com.github.nizshee.tags.InMemoryTags; +import com.github.nizshee.tags.Tags; +import com.github.nizshee.workspace.FileWorkspace; + +import java.io.*; +import java.nio.file.Paths; +import java.util.*; + +public class Main { + + private final static String CVS = ".cvs"; + private final static String TAGS = ".tags"; + private final static String WORKDIR = "workdir"; + + @SuppressWarnings("ResultOfMethodCallIgnored") + public static void main(String args[]) throws Exception { + + Paths.get(WORKDIR).toFile().mkdirs(); + + Tags tags = restoreTags(); + CVS cvs = restoreCVS(); + + + Frontend frontend = new Frontend(tags, cvs); + frontend.eval(args); + + if (cvs.current() != null) { + dumpTags(tags); + dumpCVS(cvs); + } + } + + private static CVS restoreCVS() throws IOException, ClassNotFoundException, WorkspaceException, StateException { + try (FileInputStream fis = new FileInputStream(WORKDIR + "/" + CVS)) { + ObjectInputStream ois = new ObjectInputStream(fis); + return (CVS) ois.readObject(); + } catch (FileNotFoundException ignore) { + return new CVS(new FileWorkspace(WORKDIR), new InMemoryIndex(new HashMap<>()), + Arrays.asList(CVS, TAGS)); + } + } + + private static void dumpCVS(CVS cvs) throws IOException { + FileOutputStream fos = new FileOutputStream(WORKDIR + "/" + CVS); + ObjectOutputStream oos = new ObjectOutputStream(fos); + oos.writeObject(cvs); + fos.close(); + } + + private static Tags restoreTags() throws IOException, ClassNotFoundException { + try (FileInputStream fis = new FileInputStream(WORKDIR + "/" + TAGS)) { + ObjectInputStream ois = new ObjectInputStream(fis); + return (Tags) ois.readObject(); + } catch (FileNotFoundException ignore) { + return new InMemoryTags(); + } + } + + private static void dumpTags(Tags tags) throws IOException { + FileOutputStream fos = new FileOutputStream(WORKDIR + "/" + TAGS); + ObjectOutputStream oos = new ObjectOutputStream(fos); + oos.writeObject(tags); + fos.close(); + } +} diff --git a/src/main/java/com/github/nizshee/cvs/CVS.java b/src/main/java/com/github/nizshee/cvs/CVS.java new file mode 100644 index 0000000..160ba5e --- /dev/null +++ b/src/main/java/com/github/nizshee/cvs/CVS.java @@ -0,0 +1,213 @@ +package com.github.nizshee.cvs; + +import com.github.nizshee.message.CommitMessage; +import com.github.nizshee.exception.CVSException; +import com.github.nizshee.exception.WorkspaceException; +import com.github.nizshee.exception.StateException; +import com.github.nizshee.index.Index; +import com.github.nizshee.node.Commit; +import com.github.nizshee.node.Init; +import com.github.nizshee.node.Merge; +import com.github.nizshee.node.Node; +import com.github.nizshee.state.State; +import com.github.nizshee.workspace.Workspace; + +import java.io.Serializable; +import java.util.*; +import java.util.stream.Collectors; + +public class CVS implements Serializable { + + private String currentHash; + private State current; + private State stage; + private final Workspace workspace; + private final Index index; + private final Set ignorePaths; + + public CVS(Workspace workspace, Index index, List ignore) throws StateException { + this.workspace = workspace; + this.index = index; + this.ignorePaths = new HashSet<>(ignore); + } + + public String commit(String comment) throws WorkspaceException, StateException { + Set toRemove = current.created(stage); + Set toChange = current.changed(stage); + Set toCreate = current.removed(stage); + + if (toCreate.isEmpty() && toRemove.isEmpty() && toChange.isEmpty()) + throw new WorkspaceException("No changes."); + + Set changes = new HashSet<>(); + + try { + for (String fileName : toRemove) { + changes.add(new Commit.Remove(fileName)); + } + + for (String fileName : toCreate) { + changes.add(new Commit.Create(fileName, workspace.get(fileName))); + } + + for (String fileName : toChange) { + changes.add(new Commit.Change(fileName, workspace.get(fileName))); + } + + } catch (WorkspaceException ignore) { + ignore.printStackTrace(); + } + + Node node = new Commit(comment, currentHash, changes); + currentHash = index.addNode(node); + current = node.change(Collections.singletonList(current)); + + return currentHash; + } + + public String mergeWith(String hash) throws WorkspaceException, StateException, CVSException { + Node node = new Merge(currentHash, hash); + List states = Arrays.asList(current, index.getState(hash)); + current = node.change(states); + stage = current.copy(); + currentHash = index.addNode(node); + current.changeWorkspace(workspace); + return currentHash; + } + + public void checkout(String hash) throws WorkspaceException, StateException, CVSException { + checkCompatible(hash); + State state = index.getState(hash); + state.changeWorkspace(workspace); + current = state; + stage = current.copy(); + currentHash = hash; + } + + public Set log() throws StateException { + return log(currentHash); + } + + @SuppressWarnings("all") + public Set log(String hash) throws StateException { + Node node = index.getNode(hash); + List dependencies = node.getDependencies(); + Set result = new HashSet<>(); + for (String dependency : dependencies) { + result.addAll(log(dependency)); + } + result.add(node.message(hash)); + return result; + } + + public void change(String hash) throws WorkspaceException, StateException { + current = index.getState(hash); + stage = current.copy(); + currentHash = hash; + } + + public Set changedToCommit() throws WorkspaceException { + return stage.changed(current).stream() + .filter(path -> !ignorePaths.contains(path)) + .collect(Collectors.toSet()); + } + + public Set createdToCommit() throws WorkspaceException { + return stage.created(current).stream() + .filter(path -> !ignorePaths.contains(path)) + .collect(Collectors.toSet()); + } + + public Set removedToCommit() throws WorkspaceException { + return stage.removed(current).stream() + .filter(path -> !ignorePaths.contains(path)) + .collect(Collectors.toSet()); + } + + public Set notTracked() throws WorkspaceException { + return workspace.getFiles().stream() + .filter(path -> !ignorePaths.contains(path)) + .filter(path -> !current.contains(path) && !stage.contains(path)) + .collect(Collectors.toSet()); + } + + public Set created() throws WorkspaceException { + return stage.created(workspace).stream() + .filter(current::contains) + .filter(path -> !ignorePaths.contains(path)) + .collect(Collectors.toSet()); + } + + public Set changed() throws WorkspaceException { + return stage.changed(workspace).stream() + .filter(path -> !ignorePaths.contains(path)) + .collect(Collectors.toSet()); + } + + public Set removed() throws WorkspaceException { + return stage.removed(workspace).stream() + .filter(path -> !ignorePaths.contains(path)) + .collect(Collectors.toSet()); + } + + public void add(String fileName) throws WorkspaceException, StateException, CVSException { + Set workspaceFiles = workspace.getFiles(); + if (stage.contains(fileName) && workspaceFiles.contains(fileName)) { + stage.put(fileName, workspace.get(fileName)); + } else if (stage.contains(fileName) && !workspaceFiles.contains(fileName)) { + stage.remove(fileName); + } else if (!stage.contains(fileName) && workspaceFiles.contains(fileName)) { + stage.create(fileName, workspace.get(fileName)); + } else { + throw new CVSException("No file " + fileName + " found."); + } + } + + public void reset(String fileName) throws StateException { + stage.syncFile(fileName, current); + } + + public void clean() throws WorkspaceException { + for (String fileName : notTracked()) { + workspace.remove(fileName); + } + } + + public void rm(String fileName) throws WorkspaceException, StateException { + if (stage.contains(fileName)) + stage.remove(fileName); + if (workspace.getFiles().contains(fileName)) + workspace.remove(fileName); + } + + public void init() throws StateException, CVSException { + if (currentHash != null) throw new CVSException("Already init."); + Node node = new Init(); + currentHash = index.addNode(node); + current = node.change(Collections.emptyList()); + stage = current.copy(); + } + + public String current() { + return currentHash; + } + + @SuppressWarnings("all") + public boolean saved() throws WorkspaceException { + return changedToCommit().isEmpty() && createdToCommit().isEmpty() && removedToCommit().isEmpty(); + } + + private boolean checkCompatible(String hash) throws StateException, WorkspaceException, CVSException { + State state = index.getState(hash); + Set bad = notTracked().stream().filter(state::contains).collect(Collectors.toSet()); + if (!bad.isEmpty()) + throw new CVSException("" + hash + " contains untrack files."); + if (!changedToCommit().isEmpty() || !removedToCommit().isEmpty() || !createdToCommit().isEmpty()) + throw new CVSException("Not committed changes."); + if (changed().stream().filter(state::contains).findAny().isPresent()) + throw new CVSException("Files presented in " + hash + "changed."); + if (created().stream().filter(state::contains).findAny().isPresent()) + throw new CVSException("Files presented in " + hash + "created."); + return true; + } +} diff --git a/src/main/java/com/github/nizshee/exception/CVSException.java b/src/main/java/com/github/nizshee/exception/CVSException.java new file mode 100644 index 0000000..ffc8517 --- /dev/null +++ b/src/main/java/com/github/nizshee/exception/CVSException.java @@ -0,0 +1,8 @@ +package com.github.nizshee.exception; + + +public class CVSException extends Exception { + public CVSException(String s) { + super(s); + } +} diff --git a/src/main/java/com/github/nizshee/exception/StateException.java b/src/main/java/com/github/nizshee/exception/StateException.java new file mode 100644 index 0000000..7b06a8c --- /dev/null +++ b/src/main/java/com/github/nizshee/exception/StateException.java @@ -0,0 +1,8 @@ +package com.github.nizshee.exception; + + +public class StateException extends Exception { + public StateException(String s) { + super(s); + } +} diff --git a/src/main/java/com/github/nizshee/exception/TagException.java b/src/main/java/com/github/nizshee/exception/TagException.java new file mode 100644 index 0000000..4d487cc --- /dev/null +++ b/src/main/java/com/github/nizshee/exception/TagException.java @@ -0,0 +1,8 @@ +package com.github.nizshee.exception; + + +public class TagException extends Exception { + public TagException(String s) { + super(s); + } +} diff --git a/src/main/java/com/github/nizshee/exception/WorkspaceException.java b/src/main/java/com/github/nizshee/exception/WorkspaceException.java new file mode 100644 index 0000000..446a53f --- /dev/null +++ b/src/main/java/com/github/nizshee/exception/WorkspaceException.java @@ -0,0 +1,8 @@ +package com.github.nizshee.exception; + + +public class WorkspaceException extends Exception { + public WorkspaceException(String s) { + super(s); + } +} diff --git a/src/main/java/com/github/nizshee/frontend/Frontend.java b/src/main/java/com/github/nizshee/frontend/Frontend.java new file mode 100644 index 0000000..e2a3153 --- /dev/null +++ b/src/main/java/com/github/nizshee/frontend/Frontend.java @@ -0,0 +1,115 @@ +package com.github.nizshee.frontend; + + +import com.github.nizshee.cvs.CVS; +import com.github.nizshee.tags.Tags; + +import java.util.Set; + + +/** + * Frontend for class {@code CVS}. + * Adds names for identificators with {@code Tags}. + */ +@SuppressWarnings("all") +public class Frontend { + + private final Tags tags; + private final CVS cvs; + + public Frontend(Tags tags, CVS cvs) { + this.tags = tags; + this.cvs = cvs; + } + + public void eval(String args[]) throws Exception { + + + if (cvs.current() == null) { + if (args.length == 1 && args[0].equals("init")) { + cvs.init(); + tags.create("master", cvs.current()); + tags.setCurrent("master"); + } else + throw new Exception("Need init."); + } else if (args.length == 1 && args[0].equals("status")) { + System.out.println("On branch: " + tags.current()); + System.out.println("Hash: " + cvs.current()); + + Set files; + files = cvs.notTracked(); + if (!files.isEmpty()) { + System.out.println("Not indexed:"); + files.forEach(System.out::println); + } + + files = cvs.createdToCommit(); + if (!files.isEmpty()) { + System.out.println("Files to commit, created :"); + files.forEach(System.out::println); + } + + files = cvs.changedToCommit(); + if (!files.isEmpty()) { + System.out.println("Files to commit, changed:"); + files.forEach(System.out::println); + } + + files = cvs.removedToCommit(); + if (!files.isEmpty()) { + System.out.println("Files to commit, removed:"); + files.forEach(System.out::println); + } + + files = cvs.removed(); + if (!files.isEmpty()) { + System.out.println("Files removed:"); + files.forEach(System.out::println); + } + + files = cvs.created(); + if (!files.isEmpty()) { + System.out.println("Files created:"); + files.forEach(System.out::println); + } + + files = cvs.changed(); + if (!files.isEmpty()) { + System.out.println("Files changed:"); + files.forEach(System.out::println); + } + } else if (args.length == 2 && args[0].equals("commit")) { + String hash = cvs.commit(args[1]); + tags.changeCurrent(hash); + System.out.println(hash); + } else if (args.length == 2 && args[0].equals("branch")) { + tags.create(args[1], cvs.current()); + } else if (args.length == 2 && args[0].equals("remove")) { + tags.remove(args[1]); + } else if (args.length == 2 && args[0].equals("checkout")) { + String hash = tags.getHash(args[1]); + cvs.checkout(hash); + tags.setCurrent(args[1]); + } else if (args.length == 1 && args[0].equals("log")) { + cvs.log().forEach(msg -> System.out.println(msg.hash + " - " + msg.message)); + } else if (args.length == 2 && args[0].equals("merge")) { + String hash = tags.getHash(args[1]); + String newHash = cvs.mergeWith(hash); + tags.changeCurrent(newHash); + System.out.println(newHash); + } else if (args.length == 2 && args[0].equals("add")) { + cvs.add(args[1]); + } else if (args.length == 2 && args[0].equals("reset")) { + cvs.reset(args[1]); + } else if (args.length == 2 && args[0].equals("rm")) { + cvs.rm(args[1]); + } else if (args.length == 1 && args[0].equals("clean")) { + cvs.clean(); + } else { + System.out.println("Unknown command."); + } + + } + + +} diff --git a/src/main/java/com/github/nizshee/index/InMemoryIndex.java b/src/main/java/com/github/nizshee/index/InMemoryIndex.java new file mode 100644 index 0000000..1f18969 --- /dev/null +++ b/src/main/java/com/github/nizshee/index/InMemoryIndex.java @@ -0,0 +1,43 @@ +package com.github.nizshee.index; + + +import com.github.nizshee.exception.StateException; +import com.github.nizshee.node.Node; +import com.github.nizshee.state.State; + +import java.io.Serializable; +import java.util.*; + +public class InMemoryIndex implements Index, Serializable { + + private final Map nodes; + + public InMemoryIndex(Map nodes) { + this.nodes = new HashMap<>(nodes); + } + + @Override + public String addNode(Node node) { + String hash = UUID.randomUUID().toString(); + nodes.put(hash, node); + return hash; + } + + @Override + public Node getNode(String hash) throws StateException { + if (!nodes.containsKey(hash)) throw new StateException("Key " + hash + " not found."); + return nodes.get(hash); + } + + @Override + public State getState(String hash) throws StateException { + Node node = getNode(hash); + List dependencies = node.getDependencies(); + List states = new LinkedList<>(); + for (String dependencyHash : dependencies) { + State state = getState(dependencyHash); + states.add(state); + } + return node.change(states); + } +} diff --git a/src/main/java/com/github/nizshee/index/Index.java b/src/main/java/com/github/nizshee/index/Index.java new file mode 100644 index 0000000..abd0808 --- /dev/null +++ b/src/main/java/com/github/nizshee/index/Index.java @@ -0,0 +1,18 @@ +package com.github.nizshee.index; + + +import com.github.nizshee.exception.StateException; +import com.github.nizshee.node.Node; +import com.github.nizshee.state.State; + +/** + * Class to store {@code Node}. + */ +public interface Index { + + String addNode(Node node); + + Node getNode(String hash) throws StateException; + + State getState(String hash) throws StateException; +} diff --git a/src/main/java/com/github/nizshee/message/CommitMessage.java b/src/main/java/com/github/nizshee/message/CommitMessage.java new file mode 100644 index 0000000..f4b3a76 --- /dev/null +++ b/src/main/java/com/github/nizshee/message/CommitMessage.java @@ -0,0 +1,28 @@ +package com.github.nizshee.message; + + +public class CommitMessage { + public final String message; + public final String hash; + + public CommitMessage(String message, String hash) { + this.message = message; + this.hash = hash; + } + + @Override + public boolean equals(Object o) { + return o instanceof CommitMessage && + message.equals(((CommitMessage) o).message) && hash.equals(((CommitMessage) o).hash); + } + + @Override + public int hashCode() { + return message.hashCode() + hash.hashCode(); + } + + @Override + public String toString() { + return "(" + hash + ", " + message + ")"; + } +} diff --git a/src/main/java/com/github/nizshee/node/Commit.java b/src/main/java/com/github/nizshee/node/Commit.java new file mode 100644 index 0000000..96fa265 --- /dev/null +++ b/src/main/java/com/github/nizshee/node/Commit.java @@ -0,0 +1,93 @@ +package com.github.nizshee.node; + + +import com.github.nizshee.message.CommitMessage; +import com.github.nizshee.exception.StateException; +import com.github.nizshee.state.State; + +import java.io.Serializable; +import java.util.*; + +public class Commit implements Node { + + private final String prev; + private final Set changes; + private final String message; + + public Commit(String message, String prev, Set changes) { + this.message = message; + this.prev = prev; + this.changes = changes; + } + + @Override + public CommitMessage message(String hash) { + return new CommitMessage(message, hash); + } + + @Override + public List getDependencies() { + return Collections.singletonList(prev); + } + + @Override + public State change(List states) throws StateException { + if (states.size() != 1) throw new StateException("Need only one changeCurrent for hash"); + State state = states.get(0); + for (Diff diff: changes) { + diff.change(state); + } + return state; + } + + + public interface Diff extends Serializable { + void change(State state) throws StateException; + } + + public static class Change implements Diff { + + private final String fileName; + private final List post; + + public Change(String fileName, List post) { + this.fileName = fileName; + this.post = post; + } + + @Override + public void change(State state) throws StateException { + state.put(fileName, post); + } + } + + public static class Create implements Diff { + + private final String fileName; + private final List content; + + public Create(String fileName, List content) { + this.fileName = fileName; + this.content = new LinkedList<>(content); + } + + @Override + public void change(State state) throws StateException { + state.create(fileName, content); + } + } + + public static class Remove implements Diff { + + private final String fileName; + + public Remove(String fileName) { + this.fileName = fileName; + } + + @Override + public void change(State state) throws StateException { + state.remove(fileName); + } + } +} diff --git a/src/main/java/com/github/nizshee/node/Init.java b/src/main/java/com/github/nizshee/node/Init.java new file mode 100644 index 0000000..02e031d --- /dev/null +++ b/src/main/java/com/github/nizshee/node/Init.java @@ -0,0 +1,26 @@ +package com.github.nizshee.node; + + +import com.github.nizshee.message.CommitMessage; +import com.github.nizshee.state.State; + +import java.util.Collections; +import java.util.List; + +public class Init implements Node { + + @Override + public List getDependencies() { + return Collections.emptyList(); + } + + @Override + public State change(List states) { + return new State(); + } + + @Override + public CommitMessage message(String hash) { + return new CommitMessage("init", hash); + } +} diff --git a/src/main/java/com/github/nizshee/node/Merge.java b/src/main/java/com/github/nizshee/node/Merge.java new file mode 100644 index 0000000..a174e82 --- /dev/null +++ b/src/main/java/com/github/nizshee/node/Merge.java @@ -0,0 +1,61 @@ +package com.github.nizshee.node; + + +import com.github.nizshee.message.CommitMessage; +import com.github.nizshee.exception.StateException; +import com.github.nizshee.state.State; + +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +public class Merge implements Node { + + private final String from; + private final String with; + + public Merge(String from, String with) { + this.from = from; + this.with = with; + } + + @Override + public List getDependencies() { + List list = new LinkedList<>(); + list.add(from); + list.add(with); + return list; + } + + @Override + public State change(List states) throws StateException { + if (states.size() != 2) throw new StateException("Merge need two states"); + State from = states.get(0); + State with = states.get(1); + + Set toAdd = from.removed(with); + + for (String fileName : toAdd) { + from.create(fileName, with.get(fileName)); + } + + Set toChange = with.changed(from); + + for (String fileName : toChange) { + List result = new LinkedList<>(); + result.add("<<<"); + result.addAll(from.get(fileName)); + result.add("==="); + result.addAll(with.get(fileName)); + result.add(">>>"); + from.put(fileName, result); + } + + return from; + } + + @Override + public CommitMessage message(String hash) { + return new CommitMessage(from + " with " + with, hash); + } +} diff --git a/src/main/java/com/github/nizshee/node/Node.java b/src/main/java/com/github/nizshee/node/Node.java new file mode 100644 index 0000000..a7075d8 --- /dev/null +++ b/src/main/java/com/github/nizshee/node/Node.java @@ -0,0 +1,20 @@ +package com.github.nizshee.node; + + +import com.github.nizshee.message.CommitMessage; +import com.github.nizshee.exception.StateException; +import com.github.nizshee.state.State; + +import java.io.Serializable; +import java.util.List; + +/** + * Class that represents change of workspace. + */ +public interface Node extends Serializable { + List getDependencies(); + + State change(List states) throws StateException; + + CommitMessage message(String hash); +} diff --git a/src/main/java/com/github/nizshee/state/State.java b/src/main/java/com/github/nizshee/state/State.java new file mode 100644 index 0000000..6017a4e --- /dev/null +++ b/src/main/java/com/github/nizshee/state/State.java @@ -0,0 +1,111 @@ +package com.github.nizshee.state; + + +import com.github.nizshee.workspace.Workspace; +import com.github.nizshee.exception.WorkspaceException; +import com.github.nizshee.exception.StateException; + +import java.io.Serializable; +import java.util.*; +import java.util.stream.Collectors; + +public class State implements Serializable { + + private Map> files; + + public State() { + files = new HashMap<>(); + } + + public State copy() { + State state = new State(); + state.files = new HashMap<>(files); + return state; + } + + public boolean contains(String fileName) { + return files.containsKey(fileName); + } + + public void create(String fileName, List content) throws StateException { + if (files.containsKey(fileName)) throw new StateException("File " + fileName + " exists."); + files.put(fileName, content); + } + + public void put(String fileName, List content) throws StateException { + if (!files.containsKey(fileName)) throw new StateException("File " + fileName + " not found."); + files.put(fileName, content); + } + + public List get(String fileName) throws StateException { + if (!files.containsKey(fileName)) throw new StateException("File " + fileName + " not found."); + return files.get(fileName); + } + + public void remove(String fileName) throws StateException { + files.remove(fileName); + } + + @SuppressWarnings("StatementWithEmptyBody") + public void syncFile(String fileName, State that) throws StateException { + if (this.files.containsKey(fileName) && !that.files.containsKey(fileName)) { + this.files.remove(fileName); + } else if (!this.files.containsKey(fileName) && !that.files.containsKey(fileName)) { + } else { + this.files.put(fileName, that.files.get(fileName)); + } + } + + public Set changed(Workspace workspace) throws WorkspaceException { + Set wFiles = workspace.getFiles(); + Set result = new HashSet<>(); + for (String filename : files.keySet()) { + if (wFiles.contains(filename) && !workspace.get(filename).equals(files.get(filename))) + result.add(filename); + } + return result; + } + + public Set changed(State state) { + return files.keySet().stream().filter(state.files.keySet()::contains) + .filter(fileName -> !files.get(fileName).equals(state.files.get(fileName))).collect(Collectors.toSet()); + } + + public Set created(Workspace workspace) throws WorkspaceException { + Set wFiles = workspace.getFiles(); + return wFiles.stream().filter(fileName -> !files.keySet().contains(fileName)).collect(Collectors.toSet()); + } + + public Set removed(State state) { + return state.files.keySet().stream().filter(fileName -> !files.containsKey(fileName)) + .collect(Collectors.toSet()); + } + + public Set removed(Workspace workspace) throws WorkspaceException { + Set wFiles = workspace.getFiles(); + return files.keySet().stream().filter(fileName -> !wFiles.contains(fileName)).collect(Collectors.toSet()); + } + + public Set created(State state) { + return files.keySet().stream() + .filter(fileName -> !state.files.containsKey(fileName)).collect(Collectors.toSet()); + } + + public void changeWorkspace(Workspace workspace) throws WorkspaceException { + Set toCreate = removed(workspace); + Set toChange = changed(workspace); + Set toRemove = created(workspace); + + for (String fileName : toCreate) { + workspace.create(fileName, files.get(fileName)); + } + + for (String fileName : toChange) { + workspace.change(fileName, files.get(fileName)); + } + + for (String fileName : toRemove) { + workspace.remove(fileName); + } + } +} diff --git a/src/main/java/com/github/nizshee/tags/InMemoryTags.java b/src/main/java/com/github/nizshee/tags/InMemoryTags.java new file mode 100644 index 0000000..52c93ba --- /dev/null +++ b/src/main/java/com/github/nizshee/tags/InMemoryTags.java @@ -0,0 +1,58 @@ +package com.github.nizshee.tags; + + +import com.github.nizshee.exception.TagException; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +public class InMemoryTags implements Tags, Serializable { + + private final Map tags; + private String name; + + public InMemoryTags() { + this.tags = new HashMap<>(); + } + + @Override + public void create(String name, String hash) throws TagException { + if (tags.containsKey(name)) throw new TagException("Name already exists."); + tags.put(name, hash); + } + + @Override + public String getHash(String maybeName) throws TagException { + if (tags.containsKey(maybeName)) { + return tags.get(maybeName); + } else return maybeName; + } + + @Override + public void changeCurrent(String hash) throws TagException { + if (name != null) { + tags.put(name, hash); + } + } + + @Override + public String current() throws TagException { + return name == null ? "no name" : name; + } + + @Override + public void setCurrent(String maybeName) throws TagException { + if (tags.containsKey(maybeName)) { + name = maybeName; + } else { + name = null; + } + } + + @Override + public void remove(String name) throws TagException { + if (!tags.containsKey(name)) throw new TagException("Tag not found,"); + tags.remove(name); + } +} diff --git a/src/main/java/com/github/nizshee/tags/Tags.java b/src/main/java/com/github/nizshee/tags/Tags.java new file mode 100644 index 0000000..d5d6ed5 --- /dev/null +++ b/src/main/java/com/github/nizshee/tags/Tags.java @@ -0,0 +1,22 @@ +package com.github.nizshee.tags; + + +import com.github.nizshee.exception.TagException; + +/** + * Stores names for identificators. + */ +public interface Tags { + + void create(String name, String hash) throws TagException; + + String current() throws TagException; + + void changeCurrent(String hash) throws TagException; + + void setCurrent(String maybeName) throws TagException; + + String getHash(String maybeName) throws TagException; + + void remove(String name) throws TagException; +} diff --git a/src/main/java/com/github/nizshee/workspace/FileWorkspace.java b/src/main/java/com/github/nizshee/workspace/FileWorkspace.java new file mode 100644 index 0000000..082da25 --- /dev/null +++ b/src/main/java/com/github/nizshee/workspace/FileWorkspace.java @@ -0,0 +1,69 @@ +package com.github.nizshee.workspace; + + +import com.github.nizshee.exception.WorkspaceException; + +import java.io.IOException; +import java.io.Serializable; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +public class FileWorkspace implements Workspace, Serializable { + private final String directoryPath; + + public FileWorkspace(String path) throws WorkspaceException { + this.directoryPath = path; + } + + @Override + public Set getFiles() throws WorkspaceException { + try { + Path directoryPath = Paths.get(this.directoryPath); + return Files.walk(directoryPath).filter(path -> !path.equals(directoryPath)) + .map(path -> directoryPath.relativize(path).normalize().toString()).collect(Collectors.toSet()); + } catch (IOException e) { + throw new WorkspaceException(e.getMessage()); + } + } + + @Override + public List get(String fileName) throws WorkspaceException { + try { + return Files.lines(Paths.get(directoryPath, fileName)).collect(Collectors.toList()); + } catch (IOException e) { + throw new WorkspaceException(e.getMessage()); + } + } + + @Override + public void create(String fileName, List content) throws WorkspaceException { + try { + Path path = Files.createFile(Paths.get(directoryPath, fileName)); + Files.write(path, content); + } catch (IOException e) { + throw new WorkspaceException(e.getMessage()); + } + } + + @Override + public void remove(String fileName) throws WorkspaceException { + try { + Files.delete(Paths.get(directoryPath, fileName)); + } catch (IOException e) { + throw new WorkspaceException(e.getMessage()); + } + } + + @Override + public void change(String fileName, List content) throws WorkspaceException { + try { + Files.write(Paths.get(directoryPath, fileName), content); + } catch (IOException e) { + throw new WorkspaceException(e.getMessage()); + } + } +} diff --git a/src/main/java/com/github/nizshee/workspace/InMemoryWorkspace.java b/src/main/java/com/github/nizshee/workspace/InMemoryWorkspace.java new file mode 100644 index 0000000..e40936e --- /dev/null +++ b/src/main/java/com/github/nizshee/workspace/InMemoryWorkspace.java @@ -0,0 +1,44 @@ +package com.github.nizshee.workspace; + + +import com.github.nizshee.exception.WorkspaceException; + +import java.util.*; + +public class InMemoryWorkspace implements Workspace { + + private final Map> files; + + public InMemoryWorkspace(Map> files) { + this.files = new HashMap<>(files); + } + + @Override + public Set getFiles() throws WorkspaceException { + return new HashSet<>(files.keySet()); + } + + @Override + public List get(String fileName) throws WorkspaceException { + if (!files.containsKey(fileName)) throw new WorkspaceException("File not found"); + return files.get(fileName); + } + + @Override + public void create(String fileName, List content) throws WorkspaceException { + if (files.containsKey(fileName)) throw new WorkspaceException("File " + fileName + " already exists"); + files.put(fileName, content); + } + + @Override + public void remove(String fileName) throws WorkspaceException { + if (!files.containsKey(fileName)) throw new WorkspaceException("File not found"); + files.remove(fileName); + } + + @Override + public void change(String fileName, List content) throws WorkspaceException { + if (!files.containsKey(fileName)) throw new WorkspaceException("File not found"); + files.put(fileName, content); + } +} diff --git a/src/main/java/com/github/nizshee/workspace/Workspace.java b/src/main/java/com/github/nizshee/workspace/Workspace.java new file mode 100644 index 0000000..c7f3c3a --- /dev/null +++ b/src/main/java/com/github/nizshee/workspace/Workspace.java @@ -0,0 +1,24 @@ +package com.github.nizshee.workspace; + + +import com.github.nizshee.exception.WorkspaceException; + +import java.util.List; +import java.util.Set; + + +/** + * Abstraction over directory with files. + */ +public interface Workspace { + + Set getFiles() throws WorkspaceException; + + List get(String fileName) throws WorkspaceException; + + void create(String fileName, List content) throws WorkspaceException; + + void remove(String fileName) throws WorkspaceException; + + void change(String fileName, List content) throws WorkspaceException; +} diff --git a/src/test/java/com/github/nizshee/cvs/CVSTest.java b/src/test/java/com/github/nizshee/cvs/CVSTest.java new file mode 100644 index 0000000..d3f350c --- /dev/null +++ b/src/test/java/com/github/nizshee/cvs/CVSTest.java @@ -0,0 +1,369 @@ +package com.github.nizshee.cvs; + +import com.github.nizshee.message.CommitMessage; +import com.github.nizshee.exception.CVSException; +import com.github.nizshee.index.InMemoryIndex; +import com.github.nizshee.index.Index; +import com.github.nizshee.node.Commit; +import com.github.nizshee.node.Init; +import com.github.nizshee.node.Node; +import com.github.nizshee.workspace.InMemoryWorkspace; +import com.github.nizshee.workspace.Workspace; +import org.junit.Test; +import static org.junit.Assert.*; + +import java.util.*; + + +public class CVSTest { + + @Test + public void initTest() throws Exception { + Index index = new InMemoryIndex(new HashMap<>()); + Workspace workspace = new InMemoryWorkspace(new HashMap<>()); + CVS cvs = new CVS(workspace, index, Collections.emptyList()); + cvs.init(); + + assertNotNull(cvs.current()); + } + + @Test + public void addTest() throws Exception { + Index index = new InMemoryIndex(getIndexMap()); + Workspace workspace = new InMemoryWorkspace(getWorkspaceDifferentMap()); + CVS cvs = new CVS(workspace, index, Collections.emptyList()); + cvs.change("2"); + + assertEquals(0, cvs.createdToCommit().size()); + assertEquals(0, cvs.changedToCommit().size()); + assertEquals(0, cvs.removedToCommit().size()); + assertEquals(0, cvs.created().size()); + assertEquals(1, cvs.changed().size()); + assertEquals(1, cvs.removed().size()); + assertEquals(1, cvs.notTracked().size()); + + cvs.add("file1"); + + assertEquals(0, cvs.createdToCommit().size()); + assertEquals(0, cvs.changedToCommit().size()); + assertEquals(1, cvs.removedToCommit().size()); + assertEquals(0, cvs.created().size()); + assertEquals(1, cvs.changed().size()); + assertEquals(0, cvs.removed().size()); + assertEquals(1, cvs.notTracked().size()); + + cvs.add("file2"); + + assertEquals(0, cvs.createdToCommit().size()); + assertEquals(1, cvs.changedToCommit().size()); + assertEquals(1, cvs.removedToCommit().size()); + assertEquals(0, cvs.created().size()); + assertEquals(0, cvs.changed().size()); + assertEquals(0, cvs.removed().size()); + assertEquals(1, cvs.notTracked().size()); + + cvs.add("file3"); + + assertEquals(1, cvs.createdToCommit().size()); + assertEquals(1, cvs.changedToCommit().size()); + assertEquals(1, cvs.removedToCommit().size()); + assertEquals(0, cvs.created().size()); + assertEquals(0, cvs.changed().size()); + assertEquals(0, cvs.removed().size()); + assertEquals(0, cvs.notTracked().size()); + } + + @Test + public void resetTest() throws Exception { + Index index = new InMemoryIndex(getIndexMap()); + Workspace workspace = new InMemoryWorkspace(getWorkspaceDifferentMap()); + CVS cvs = new CVS(workspace, index, Collections.emptyList()); + cvs.change("2"); + cvs.add("file1"); + cvs.add("file2"); + cvs.add("file3"); + + cvs.reset("file1"); + + assertEquals(1, cvs.createdToCommit().size()); + assertEquals(1, cvs.changedToCommit().size()); + assertEquals(0, cvs.removedToCommit().size()); + assertEquals(0, cvs.created().size()); + assertEquals(0, cvs.changed().size()); + assertEquals(1, cvs.removed().size()); + assertEquals(0, cvs.notTracked().size()); + + cvs.reset("file3"); + + assertEquals(0, cvs.createdToCommit().size()); + assertEquals(1, cvs.changedToCommit().size()); + assertEquals(0, cvs.removedToCommit().size()); + assertEquals(0, cvs.created().size()); + assertEquals(0, cvs.changed().size()); + assertEquals(1, cvs.removed().size()); + assertEquals(1, cvs.notTracked().size()); + + cvs.reset("file2"); + + assertEquals(0, cvs.createdToCommit().size()); + assertEquals(0, cvs.changedToCommit().size()); + assertEquals(0, cvs.removedToCommit().size()); + assertEquals(0, cvs.created().size()); + assertEquals(1, cvs.changed().size()); + assertEquals(1, cvs.removed().size()); + assertEquals(1, cvs.notTracked().size()); + } + + @Test + public void cleanTest() throws Exception { + Index index = new InMemoryIndex(getIndexMap()); + Workspace workspace = new InMemoryWorkspace(getWorkspaceDifferentMap()); + CVS cvs = new CVS(workspace, index, Collections.emptyList()); + cvs.change("2"); + + cvs.clean(); + + assertEquals(new HashSet<>(Collections.singletonList("file2")), workspace.getFiles()); + + cvs.clean(); + + assertEquals(new HashSet<>(Collections.singletonList("file2")), workspace.getFiles()); + } + + @Test + public void rmTest() throws Exception { + Index index = new InMemoryIndex(getIndexMap()); + Workspace workspace = new InMemoryWorkspace(getWorkspaceDifferentMap()); + CVS cvs = new CVS(workspace, index, Collections.emptyList()); + cvs.change("2"); + + cvs.rm("file2"); + + assertEquals(1, cvs.removedToCommit().size()); + + cvs.rm("file1"); + + assertEquals(2, cvs.removedToCommit().size()); + + cvs.rm("file3"); + + assertEquals(2, cvs.removedToCommit().size()); + } + + @Test + public void changedTest() throws Exception { + Set set = new HashSet<>(); + + Index index = new InMemoryIndex(getIndexMap()); + Workspace workspace = new InMemoryWorkspace(getWorkspaceDifferentMap()); + CVS cvs = new CVS(workspace, index, Collections.emptyList()); + cvs.change("2"); + + assertEquals(set, cvs.changedToCommit()); + + cvs.add("file1"); + cvs.add("file2"); + cvs.add("file3"); + set.add("file2"); + + assertEquals(set, cvs.changedToCommit()); + } + + @Test + public void removedTest() throws Exception { + Set set = new HashSet<>(); + + Index index = new InMemoryIndex(getIndexMap()); + Workspace workspace = new InMemoryWorkspace(getWorkspaceDifferentMap()); + CVS cvs = new CVS(workspace, index, Collections.emptyList()); + cvs.change("2"); + + assertEquals(set, cvs.removedToCommit()); + + cvs.add("file1"); + cvs.add("file2"); + cvs.add("file3"); + set.add("file1"); + + assertEquals(set, cvs.removedToCommit()); + System.out.println(""); + } + + @Test + public void createdTest() throws Exception { + Set set = new HashSet<>(); + + Index index = new InMemoryIndex(getIndexMap()); + Workspace workspace = new InMemoryWorkspace(getWorkspaceDifferentMap()); + CVS cvs = new CVS(workspace, index, Collections.emptyList()); + cvs.change("2"); + + assertEquals(set, cvs.createdToCommit()); + + cvs.add("file1"); + cvs.add("file2"); + cvs.add("file3"); + set.add("file3"); + + assertEquals(set, cvs.createdToCommit()); + } + + @Test + public void notIndexedFilesTest() throws Exception { + Set set = new HashSet<>(); + set.add("file3"); + + Index index = new InMemoryIndex(getIndexMap()); + Workspace workspace = new InMemoryWorkspace(getWorkspaceDifferentMap()); + CVS cvs = new CVS(workspace, index, Collections.emptyList()); + cvs.change("2"); + + assertEquals(set, cvs.notTracked()); + + cvs.add("file1"); + cvs.add("file2"); + + assertEquals(set, cvs.notTracked()); + + cvs.add("file3"); + + assertEquals(new HashSet(), cvs.notTracked()); + } + + @Test + public void savedTest() throws Exception { + Index index = new InMemoryIndex(getIndexMap()); + Workspace workspace = new InMemoryWorkspace(getWorkspaceSameMap()); + CVS cvs = new CVS(workspace, index, Collections.emptyList()); + cvs.change("2"); + + assertTrue(cvs.saved()); + + cvs.add("file1"); + cvs.add("file2"); + + assertTrue(cvs.saved()); + + workspace = new InMemoryWorkspace(getWorkspaceDifferentMap()); + cvs = new CVS(workspace, index, Collections.emptyList()); + cvs.change("2"); + + assertTrue(cvs.saved()); + + cvs.add("file1"); + cvs.add("file2"); + cvs.add("file3"); + + assertFalse(cvs.saved()); + } + + @Test + public void commitTest() throws Exception { + Index index = new InMemoryIndex(getIndexMap()); + Workspace workspace = new InMemoryWorkspace(getWorkspaceDifferentMap()); + CVS cvs = new CVS(workspace, index, Collections.emptyList()); + cvs.change("2"); + + cvs.add("file1"); + cvs.add("file2"); + cvs.add("file3"); + + String hash = cvs.commit("12345"); + + Node node = index.getNode(hash); + assertTrue(index.getNode(hash) instanceof Commit); + CommitMessage message = node.message(hash); + assertEquals(message.message, "12345"); + + assertTrue(cvs.createdToCommit().isEmpty()); + assertTrue(cvs.changedToCommit().isEmpty()); + assertTrue(cvs.removedToCommit().isEmpty()); + } + + @Test(expected = CVSException.class) + public void checkoutTest() throws Exception { + Index index = new InMemoryIndex(getIndexMap()); + Workspace workspace = new InMemoryWorkspace(getWorkspaceDifferentMap()); + CVS cvs = new CVS(workspace, index, Collections.emptyList()); + cvs.change("2"); + + cvs.checkout("1"); + assertTrue(workspace.getFiles().isEmpty()); + + cvs.checkout("2"); + assertEquals(new HashSet<>(Arrays.asList("file1", "file2")), workspace.getFiles()); + + cvs.add("file1"); + cvs.add("file2"); + cvs.add("file3"); + + cvs.checkout("1"); + } + + @Test + public void mergeTest() throws Exception { + Index index = new InMemoryIndex(getIndexMap()); + Workspace workspace = new InMemoryWorkspace(getWorkspaceDifferentMap()); + CVS cvs = new CVS(workspace, index, Collections.emptyList()); + cvs.change("1"); + + cvs.add("file2"); + cvs.add("file3"); + + cvs.commit("321"); + cvs.mergeWith("2"); + + assertEquals(new HashSet<>(Arrays.asList("file1", "file2", "file3")), new HashSet<>(workspace.getFiles())); + assertEquals(workspace.get("file1"), Arrays.asList("1", "2")); + assertEquals(workspace.get("file2"), Arrays.asList("<<<", "1", "2", "===", "1", "2", "3", ">>>")); + assertEquals(workspace.get("file3"), Arrays.asList("1", "2", "3")); + } + + @Test + public void logTest() throws Exception { + Index index = new InMemoryIndex(getIndexMap()); + Workspace workspace = new InMemoryWorkspace(getWorkspaceSameMap()); + CVS cvs = new CVS(workspace, index, Collections.emptyList()); + cvs.change("1"); + cvs.add("file1"); + cvs.add("file2"); + + assertEquals( + new HashSet<>(Arrays.asList(new CommitMessage("123", "2"), new CommitMessage("init", "1"))), + cvs.log("2") + ); + + String commit = cvs.commit("321"); + String merge = cvs.mergeWith("2"); + + assertEquals( + new HashSet<>(Arrays.asList(new CommitMessage("123", "2"), new CommitMessage("init", "1"), + new CommitMessage("321", commit), new CommitMessage(commit + " with " + "2", merge))), + cvs.log() + ); + } + + private static Map getIndexMap() { + Map map = new HashMap<>(); + map.put("1", new Init()); + Set diff = new HashSet<>(); + diff.add(new Commit.Create("file1", Arrays.asList("1", "2"))); + diff.add(new Commit.Create("file2", Arrays.asList("1", "2", "3"))); + map.put("2", new Commit("123", "1", diff)); + return map; + } + + private static Map> getWorkspaceDifferentMap() { + Map> map = new HashMap<>(); + map.put("file2", Arrays.asList("1", "2")); + map.put("file3", Arrays.asList("1", "2", "3")); + return map; + } + + private static Map> getWorkspaceSameMap() { + Map> map = new HashMap<>(); + map.put("file1", Arrays.asList("1", "2")); + map.put("file2", Arrays.asList("1", "2", "3")); + return map; + } +} diff --git a/src/test/java/com/github/nizshee/tags/InMemoryTagsTest.java b/src/test/java/com/github/nizshee/tags/InMemoryTagsTest.java new file mode 100644 index 0000000..9f01a07 --- /dev/null +++ b/src/test/java/com/github/nizshee/tags/InMemoryTagsTest.java @@ -0,0 +1,47 @@ +package com.github.nizshee.tags; + +import org.junit.Test; +import static org.junit.Assert.*; + +public class InMemoryTagsTest { + + @Test + public void createTest() throws Exception { + Tags tags = new InMemoryTags(); + tags.create("a", "1"); + + assertEquals(tags.getHash("a"), "1"); + } + + @Test + public void getHashTest() throws Exception { + Tags tags = new InMemoryTags(); + tags.create("a", "1"); + tags.create("b", "2"); + + assertEquals("1", tags.getHash("a")); + assertEquals("2", tags.getHash("b")); + assertEquals("2", tags.getHash("2")); + assertEquals("3", tags.getHash("3")); + } + + @Test + public void changeTest() throws Exception { + Tags tags = new InMemoryTags(); + tags.create("a", "1"); + tags.setCurrent("a"); + + tags.changeCurrent("2"); + + assertEquals("2", tags.getHash("a")); + } + + @Test + public void currentTest() throws Exception { + Tags tags = new InMemoryTags(); + tags.create("a", "1"); + tags.setCurrent("a"); + + assertEquals("a", tags.current()); + } +}