diff --git a/src/main/java/codechicken/diffpatch/cli/DiffOperation.java b/src/main/java/codechicken/diffpatch/cli/DiffOperation.java index 66f6449..fca1373 100644 --- a/src/main/java/codechicken/diffpatch/cli/DiffOperation.java +++ b/src/main/java/codechicken/diffpatch/cli/DiffOperation.java @@ -14,13 +14,13 @@ import org.apache.commons.lang3.StringUtils; import java.io.*; -import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.util.*; import java.util.function.Consumer; import static codechicken.diffpatch.util.LogLevel.*; +import static codechicken.diffpatch.util.Utils.filterPrefixed; import static codechicken.diffpatch.util.Utils.indexChildren; /** @@ -307,21 +307,6 @@ public List doDiff(DiffSummary summary, String aName, String bName, List return patchFile.toLines(autoHeader); } - private static Set filterPrefixed(Set toFilter, String[] filters) { - if (filters.length == 0) return toFilter; - - return FastStream.of(toFilter) - .filterNot(e -> { - for (String s : filters) { - if (e.startsWith(s)) { - return true; - } - } - return false; - }) - .toSet(); - } - public static class DiffSummary { public int unchangedFiles; diff --git a/src/main/java/codechicken/diffpatch/cli/PatchOperation.java b/src/main/java/codechicken/diffpatch/cli/PatchOperation.java index 0338f23..8627c88 100644 --- a/src/main/java/codechicken/diffpatch/cli/PatchOperation.java +++ b/src/main/java/codechicken/diffpatch/cli/PatchOperation.java @@ -22,6 +22,7 @@ import java.util.function.Function; import static codechicken.diffpatch.util.LogLevel.*; +import static codechicken.diffpatch.util.Utils.filterPrefixed; import static codechicken.diffpatch.util.Utils.indexChildren; import static net.covers1624.quack.io.IOUtils.makeParents; import static net.covers1624.quack.util.SneakyUtils.sneak; @@ -45,8 +46,9 @@ public class PatchOperation extends CliOperation private final PatchMode mode; private final String patchesPrefix; private final String lineEnding; + private final String[] ignorePrefixes; - private PatchOperation(PrintStream logger, LogLevel level, Consumer helpCallback, boolean summary, InputPath basePath, InputPath patchesPath, String aPrefix, String bPrefix, OutputPath outputPath, OutputPath rejectsPath, float minFuzz, int maxOffset, PatchMode mode, String patchesPrefix, String lineEnding) { + private PatchOperation(PrintStream logger, LogLevel level, Consumer helpCallback, boolean summary, InputPath basePath, InputPath patchesPath, String aPrefix, String bPrefix, OutputPath outputPath, OutputPath rejectsPath, float minFuzz, int maxOffset, PatchMode mode, String patchesPrefix, String lineEnding, String[] ignorePrefixes) { super(logger, level, helpCallback); this.summary = summary; this.basePath = basePath; @@ -60,6 +62,7 @@ private PatchOperation(PrintStream logger, LogLevel level, Consumer this.mode = mode; this.patchesPrefix = patchesPrefix; this.lineEnding = lineEnding; + this.ignorePrefixes = ignorePrefixes; } public static Builder builder() { @@ -172,11 +175,12 @@ public Result operate() throws IOException { try (ArchiveReader baseReader = basePath.getFormat().createReader(basePath.open())) { try (ArchiveReader patchesReader = patchesPath.getFormat().createReader(patchesPath.open(), patchesPrefix)) { + Set filteredBaseIndex = filterPrefixed(baseReader.getEntries(), ignorePrefixes); patchSuccess = doPatch( outputCollector, rejectCollector, summary, - baseReader.getEntries(), + filteredBaseIndex, patchesReader.getEntries(), sneak(baseReader::getBytes), sneak(patchesReader::getBytes), @@ -191,11 +195,12 @@ public Result operate() throws IOException { if (!basePath.isFile() && !patchesPath.isFile()) { Map baseIndex = indexChildren(basePath.toPath()); Map patchIndex = indexChildren(patchesPath.toPath(), patchesPrefix); + Set filteredBaseIndex = filterPrefixed( baseIndex.keySet(), ignorePrefixes); patchSuccess = doPatch( outputCollector, rejectCollector, summary, - baseIndex.keySet(), + filteredBaseIndex, patchIndex.keySet(), SneakyUtils.sneak(e -> Files.readAllBytes(baseIndex.get(e))), SneakyUtils.sneak(e -> Files.readAllBytes(patchIndex.get(e))), @@ -238,6 +243,7 @@ public Result operate() throws IOException { baseFunc = sneak(reader::getBytes); } } + baseIndex = filterPrefixed(baseIndex, ignorePrefixes); patchSuccess = doPatch(outputCollector, rejectCollector, summary, baseIndex, patchIndex, baseFunc, patchFunc, minFuzz, maxOffset, mode); } } @@ -249,7 +255,8 @@ public Result operate() throws IOException { } } } else { - if (Files.exists(outputPath.toPath())) { + boolean isInPlaceOperation = basePath.getType().isPath() && basePath.toPath().equals(outputPath.toPath()); + if (!isInPlaceOperation && Files.exists(outputPath.toPath())) { Utils.deleteFolder(outputPath.toPath()); } for (Map.Entry entry : outputCollector.get().entrySet()) { @@ -533,6 +540,8 @@ public static class Builder { private String bPrefix = "b/"; private String lineEnding = System.lineSeparator(); + private final List ignorePrefixes = new LinkedList<>(); + private Builder() { } @@ -665,6 +674,11 @@ public Builder lineEnding(String lineEnding) { return this; } + public Builder ignorePrefix(String prefix) { + ignorePrefixes.add(prefix); + return this; + } + public PatchOperation build() { if (basePath == null) { throw new IllegalStateException("basePath not set."); @@ -675,7 +689,7 @@ public PatchOperation build() { if (outputPath == null) { throw new IllegalStateException("output not set."); } - return new PatchOperation(logger, level, helpCallback, summary, basePath, patchesPath, aPrefix, bPrefix, outputPath, rejectsPath, minFuzz, maxOffset, mode, patchesPrefix, lineEnding); + return new PatchOperation(logger, level, helpCallback, summary, basePath, patchesPath, aPrefix, bPrefix, outputPath, rejectsPath, minFuzz, maxOffset, mode, patchesPrefix, lineEnding, ignorePrefixes.toArray(new String[0])); } } diff --git a/src/main/java/codechicken/diffpatch/util/Utils.java b/src/main/java/codechicken/diffpatch/util/Utils.java index 0c3870f..0a5eba5 100644 --- a/src/main/java/codechicken/diffpatch/util/Utils.java +++ b/src/main/java/codechicken/diffpatch/util/Utils.java @@ -1,5 +1,6 @@ package codechicken.diffpatch.util; +import net.covers1624.quack.collection.FastStream; import net.covers1624.quack.util.SneakyUtils; import java.io.IOException; @@ -7,6 +8,7 @@ import java.nio.file.Path; import java.util.Comparator; import java.util.Map; +import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -43,4 +45,19 @@ public static Map indexChildren(Path toIndex, String prefix) throw .collect(Collectors.toMap(e -> stripStart('/', finalToIndex.relativize(e).toString().replace("\\", "/")), Function.identity())); } } + + public static Set filterPrefixed(Set toFilter, String[] filters) { + if (filters.length == 0) return toFilter; + + return FastStream.of(toFilter) + .filterNot(e -> { + for (String s : filters) { + if (e.startsWith(s)) { + return true; + } + } + return false; + }) + .toSet(); + } } diff --git a/src/test/java/codechicken/diffpatch/test/PatchOperationTests.java b/src/test/java/codechicken/diffpatch/test/PatchOperationTests.java index 7af6678..33e7f15 100644 --- a/src/test/java/codechicken/diffpatch/test/PatchOperationTests.java +++ b/src/test/java/codechicken/diffpatch/test/PatchOperationTests.java @@ -32,6 +32,7 @@ public void testFolderToFolder() throws Throwable { Path src = tempDir.resolve("src"); Path patches = tempDir.resolve("patches"); copyResource("/data/orig/PatchFile.java", orig.resolve("PatchFile.java")); + copyResource("/data/orig/A.txt", orig.resolve("A.txt")); copyResource("/data/src/PatchFile.java", cmp.resolve("PatchFile.java")); copyResource("/data/patches/PatchFile.java.patch", patches.resolve("PatchFile.java.patch")); CliOperation.Result result = PatchOperation.builder() @@ -40,15 +41,45 @@ public void testFolderToFolder() throws Throwable { .basePath(orig) .outputPath(src) .patchesPath(patches) + .ignorePrefix("A") .build() .operate(); assertEquals(0, result.exit); assertTrue(Files.exists(src.resolve("PatchFile.java"))); + assertFalse(Files.exists(src.resolve("A.txt")), "A is ignored, A.txt should not have been copied"); List output = Files.readAllLines(src.resolve("PatchFile.java")); List original = Files.readAllLines(cmp.resolve("PatchFile.java")); assertEquals(output, original); } + @Test + public void testFolderInplace() throws Throwable { + Path tempDir = Files.createTempDirectory("dir_test"); + tempDir.toFile().deleteOnExit(); + Path orig = tempDir.resolve("orig"); + Path cmp = tempDir.resolve("cmp"); + Path patches = tempDir.resolve("patches"); + copyResource("/data/orig/PatchFile.java", orig.resolve("PatchFile.java")); + copyResource("/data/orig/A.txt", orig.resolve("A.txt")); + copyResource("/data/src/PatchFile.java", cmp.resolve("PatchFile.java")); + copyResource("/data/patches/PatchFile.java.patch", patches.resolve("PatchFile.java.patch")); + CliOperation.Result result = PatchOperation.builder() + .logTo(System.out) + .level(LogLevel.ALL) + .basePath(orig) + .outputPath(orig) + .patchesPath(patches) + .ignorePrefix("A") + .build() + .operate(); + assertEquals(0, result.exit); + assertTrue(Files.exists(orig.resolve("PatchFile.java"))); + assertTrue(Files.exists(orig.resolve("A.txt")), "A is ignored, A.txt should not have been deleted"); + List output = Files.readAllLines(orig.resolve("PatchFile.java")); + List original = Files.readAllLines(cmp.resolve("PatchFile.java")); + assertEquals(output, original); + } + @Test public void testFolderToZip() throws Throwable { Path tempDir = Files.createTempDirectory("dir_test");