diff --git a/.travis/install.sh b/.travis/install.sh index dcbaf41a..5e1b954a 100755 --- a/.travis/install.sh +++ b/.travis/install.sh @@ -15,7 +15,7 @@ spork_jar_path="$PWD/$(ls target/spork*.jar)" echo "Creating spork executable" echo "#! /bin/bash" > spork -echo "java -jar $spork_jar_path" '$@' >> spork +echo "$JAVA_HOME/bin/java -jar $spork_jar_path --exit-on-error" '$@' >> spork chmod 700 spork mkdir -p ~/.local/bin diff --git a/src/main/java/se/kth/spork/cli/Cli.java b/src/main/java/se/kth/spork/cli/Cli.java index d20b4d24..07e499a2 100644 --- a/src/main/java/se/kth/spork/cli/Cli.java +++ b/src/main/java/se/kth/spork/cli/Cli.java @@ -99,9 +99,15 @@ static class Merge implements Callable { @CommandLine.Option( names = {"-o", "--output"}, - description = "Path to the output file. Existing files are overwritten") + description = "Path to the output file. Existing files are overwritten.") File out; + @CommandLine.Option( + names = {"-e", "--exit-on-error"}, + description = "Disable line-based fallback if the structured merge encounters an error." + ) + boolean exitOnError; + @CommandLine.Option( names = {"-g", "--git-mode"}, description = "Enable Git compatibility mode. Required to use Spork as a Git merge driver." @@ -136,7 +142,7 @@ public Integer call() throws IOException { rightPath.toFile().deleteOnExit(); } - Pair merged = merge(basePath, leftPath, rightPath); + Pair merged = merge(basePath, leftPath, rightPath, !exitOnError); String pretty = merged.first; int numConflicts = merged.second; @@ -160,31 +166,49 @@ public Integer call() throws IOException { * @param base Path to base revision. * @param left Path to left revision. * @param right Path to right revision. + * @param exitOnError Disallow the use of line-based fallback if the structured merge encounters an error. * @return A pair on the form (prettyPrint, numConflicts) */ - public static Pair merge(Path base, Path left, Path right) { + public static Pair merge(Path base, Path left, Path right, boolean exitOnError) { LOGGER.info(() -> "Parsing input files"); CtModule baseModule = Parser.parse(base); CtModule leftModule = Parser.parse(left); CtModule rightModule = Parser.parse(right); - LOGGER.info(() -> "Initiating merge"); - Pair merge = Spoon3dmMerge.merge(baseModule, leftModule, rightModule); - CtModule mergeTree = (CtModule) merge.first; - int numConflicts = merge.second; - - LOGGER.info(() -> "Pretty-printing"); - if (containsTypes(mergeTree)) { - return Pair.of(prettyPrint(mergeTree), numConflicts); - } else { - LOGGER.warn(() -> "Merge contains no types (i.e. classes, interfaces, etc), reverting to line-based merge"); - String baseStr = Parser.read(base); - String leftStr = Parser.read(left); - String rightStr = Parser.read(right); - return LineBasedMerge.merge(baseStr, leftStr, rightStr); + try { + LOGGER.info(() -> "Initiating merge"); + Pair merge = Spoon3dmMerge.merge(baseModule, leftModule, rightModule); + CtModule mergeTree = (CtModule) merge.first; + int numConflicts = merge.second; + + LOGGER.info(() -> "Pretty-printing"); + if (containsTypes(mergeTree)) { + return Pair.of(prettyPrint(mergeTree), numConflicts); + } else if (!exitOnError) { + LOGGER.warn(() -> "Merge contains no types (i.e. classes, interfaces, etc), reverting to line-based merge"); + return lineBasedMerge(base, left, right); + } else { + throw new IllegalStateException("Merge contained no types and line-based fallback is disabled"); + } + } catch (Exception e) { + if (exitOnError) { + LOGGER.debug(e::getMessage); + LOGGER.info(() -> "Spork encountered an error in structured merge. Falling back to line-based merge"); + return lineBasedMerge(base, left, right); + } else { + LOGGER.error(() -> "Spork encountered a fatal error and line-based merge is disabled"); + throw e; + } } } + private static Pair lineBasedMerge(Path base, Path left, Path right) { + String baseStr = Parser.read(base); + String leftStr = Parser.read(left); + String rightStr = Parser.read(right); + return LineBasedMerge.merge(baseStr, leftStr, rightStr); + } + /** * Create a hard link from a temporary git .merge_xxx file, with the name .merge_xxx.java. This is necessary for * Spork to be compatible with Git, as Spoon will only parse Java files if they actually have the .java file