18 Commits

Author SHA1 Message Date
Tobias Warneke
0fd3bd8e06 [maven-release-plugin] prepare release java-diff-utils-parent-4.12 2022-07-23 01:33:40 +02:00
Tobias Warneke
eeb819cad4 Merge origin/master 2022-07-23 01:29:58 +02:00
Tobias Warneke
e73742c4fc 2022-07-23 01:29:37 +02:00
Tobias Warneke
0545519fc3 included some more tests 2022-05-15 00:17:49 +02:00
Okue
bc65f9703c Update version in gradle example of README (#145) 2022-04-10 00:25:45 +02:00
Hussein Kasem
ea9942da12 Fix for #141 (#144)
* Add test to reproduce issue #141

* Fix assumption on test to match expected behaviour expressed on #141

* Make "/" after "virtual" directories mandatory

Now the regex only matches with "a/", "b/", "new/" and "old/", previously the slash was taken as optional.
fixes #141
2022-04-10 00:20:37 +02:00
Sebastian
8fdd239961 Fix a typo in ComputeDifference.java (#140) 2022-02-21 09:00:27 +01:00
anatawa12
37f9eafaa9 Fizzy apply (#125)
* add applyFuzzyTo and restoreFuzzy

* implement applyFuzzyTo and restoreFuzzy for EqualDelta

* add verifyChunk with fuzzy and delta

* implement fuzzy apply and add tests

* fix style

* extract method

* fix: assign lastPatchDelta and lastPatchEnd

* fix use explict import

* document EqualDelta#applyFuzzyToAt

* set access modifiers

* change test method name

* document empty method

* apply from first

* add more tests

* add more documentation about fuzz parameter

* fix documentation about fizzy patch

Co-authored-by: cowwoc <cowwoc2020@gmail.com>

Co-authored-by: cowwoc <cowwoc2020@gmail.com>
2021-11-28 23:10:52 +01:00
Tobias Warneke
0fd38db8ae fixes #129 - added the possibility to skip delta decompression 2021-10-21 23:17:04 +02:00
Tobias Warneke
3663cb5d87 changed gpg plugin version 2021-09-08 12:37:20 +02:00
Tobias Warneke
8ae1f6cbbf [maven-release-plugin] prepare for next development iteration 2021-09-08 12:28:14 +02:00
Tobias Warneke
ab1e38c57b [maven-release-plugin] prepare release java-diff-utils-parent-4.11 2021-09-08 12:28:13 +02:00
Tobias Warneke
286e807232 Merge origin/master 2021-09-08 12:25:34 +02:00
Jerry James
c85296b63d Javadoc warning fixes (#109)
Co-authored-by: Tobias <t.warneke@gmx.net>
2021-09-08 12:21:50 +02:00
Tobias Warneke
4c457b39dc 2021-09-08 12:19:53 +02:00
Tobias Warneke
7dea4e2298 2021-09-08 12:16:50 +02:00
Tobias Warneke
2b02951e89 Merge introduce-optimized-meyers-algorithm 2021-09-08 12:11:12 +02:00
Ilari Suhonen
89ce301123 Fix typo in UnifiedDiff#spplyPatchTo(Predicate<String>, List<String>) (#127)
* Fix typo in UnifiedDiff.java

Changes UnifiedDiff#spplyPatchTo to UnifiedDiff#applyPatchTo

* Fix typo in test

Fixes the method invocation in the UnifiedDiff roundtrip test to account for the typo fix in UnifiedDiff
2021-07-07 07:15:55 +02:00
27 changed files with 774 additions and 32 deletions

8
.github/release.yml vendored Normal file
View File

@@ -0,0 +1,8 @@
changelog:
categories:
- title: Bugs solved
labels:
- "bug"
- title: Changes and new Features
labels:
- "*"

View File

@@ -13,6 +13,12 @@ This project uses a custom versioning scheme (and not [Semantic Versioning](http
### Changed
* bugfixing new UnifiedDiff reader
* header for each file
* skip empty lines
* introduction of Meyers Diff Algorithm with Linear Space improvment (until matured this will not be the default diff algorithm)
* introduction of DiffAlgorithmFactory to set the default diff algorithm DiffUtils use (`DiffUtils.withDefaultDiffAlgorithmFactory(MeyersDiffWithLinearSpace.factory());`)
## [4.10]
### Changed

View File

@@ -49,12 +49,13 @@ This is a test ~senctence~**for diffutils**.
* producing human-readable differences
* inline difference construction
* Algorithms:
* Myer
* Meyers Standard Algorithm
* Meyers with linear space improvement
* HistogramDiff using JGit Library
### Algorithms
* Myer's diff
* Meyer's diff
* HistogramDiff
But it can easily replaced by any other which is better for handing your texts. I have plan to add implementation of some in future.
@@ -88,7 +89,7 @@ Just add the code below to your maven dependencies:
<dependency>
<groupId>io.github.java-diff-utils</groupId>
<artifactId>java-diff-utils</artifactId>
<version>4.9</version>
<version>4.11</version>
</dependency>
```
@@ -96,5 +97,5 @@ or using gradle:
```groovy
// https://mvnrepository.com/artifact/io.github.java-diff-utils/java-diff-utils
implementation "io.github.java-diff-utils:java-diff-utils:4.5"
implementation "io.github.java-diff-utils:java-diff-utils:4.11"
```

View File

@@ -4,7 +4,7 @@
<parent>
<groupId>io.github.java-diff-utils</groupId>
<artifactId>java-diff-utils-parent</artifactId>
<version>4.11-SNAPSHOT</version>
<version>4.12</version>
</parent>
<artifactId>java-diff-utils-jgit</artifactId>
<name>java-diff-utils-jgit</name>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>io.github.java-diff-utils</groupId>
<artifactId>java-diff-utils-parent</artifactId>
<version>4.11-SNAPSHOT</version>
<version>4.12</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

View File

@@ -129,7 +129,7 @@ public final class UnifiedDiffUtils {
/**
* generateUnifiedDiff takes a Patch and some other arguments, returning the Unified Diff format
* text representing the Patch.
* text representing the Patch. Author: Bill James (tankerbay@gmail.com).
*
* @param originalFileName - Filename of the original (unrevised file)
* @param revisedFileName - Filename of the revised file
@@ -137,7 +137,6 @@ public final class UnifiedDiffUtils {
* @param patch - Patch created by the diff() function
* @param contextSize - number of lines of context output around each difference in the file.
* @return List of strings representing the Unified Diff representation of the Patch argument.
* @author Bill James (tankerbay@gmail.com)
*/
public static List<String> generateUnifiedDiff(String originalFileName,
String revisedFileName, List<String> originalLines, Patch<String> patch,
@@ -200,13 +199,12 @@ public final class UnifiedDiffUtils {
/**
* processDeltas takes a list of Deltas and outputs them together in a single block of
* Unified-Diff-format text.
* Unified-Diff-format text. Author: Bill James (tankerbay@gmail.com).
*
* @param origLines - the lines of the original file
* @param deltas - the Deltas to be output as a single block
* @param contextSize - the number of lines of context to place around block
* @return
* @author Bill James (tankerbay@gmail.com)
*/
private static List<String> processDeltas(List<String> origLines,
List<AbstractDelta<String>> deltas, int contextSize, boolean newFile) {
@@ -297,11 +295,10 @@ public final class UnifiedDiffUtils {
}
/**
* getDeltaText returns the lines to be added to the Unified Diff text from the Delta parameter
* getDeltaText returns the lines to be added to the Unified Diff text from the Delta parameter. Author: Bill James (tankerbay@gmail.com).
*
* @param delta - the Delta to output
* @return list of String lines of code.
* @author Bill James (tankerbay@gmail.com)
*/
private static List<String> getDeltaText(AbstractDelta<String> delta) {
List<String> buffer = new ArrayList<>();

View File

@@ -70,6 +70,19 @@ public abstract class AbstractDelta<T> implements Serializable {
protected abstract void restore(List<T> target);
/**
* Apply patch fuzzy.
*
* @param target the list this patch will be applied to
* @param fuzz the number of elements to ignore before/after the patched elements
* @param position the position this patch will be applied to. ignores {@code source.getPosition()}
* @see <a href="https://www.gnu.org/software/diffutils/manual/html_node/Inexact.html">Description of Fuzzy Patch</a> for more information.
*/
@SuppressWarnings("RedundantThrows")
protected void applyFuzzyToAt(List<T> target, int fuzz, int position) throws PatchFailedException {
throw new UnsupportedOperationException(this.getClass().getSimpleName() + " does not supports applying patch fuzzy");
}
/**
* Create a new delta of the actual instance with customized chunk data.
*/

View File

@@ -66,6 +66,19 @@ public final class ChangeDelta<T> extends AbstractDelta<T> {
}
}
protected void applyFuzzyToAt(List<T> target, int fuzz, int position) throws PatchFailedException {
int size = getSource().size();
for (int i = fuzz; i < size - fuzz; i++) {
target.remove(position + fuzz);
}
int i = fuzz;
for (T line : getTarget().getLines().subList(fuzz, getTarget().size() - fuzz)) {
target.add(position + i, line);
i++;
}
}
@Override
public String toString() {
return "[ChangeDelta, position: " + getSource().getPosition() + ", lines: "

View File

@@ -95,10 +95,28 @@ public final class Chunk<T> implements Serializable {
* @throws com.github.difflib.patch.PatchFailedException
*/
public VerifyChunk verifyChunk(List<T> target) throws PatchFailedException {
if (position > target.size() || last() > target.size()) {
return verifyChunk(target, 0, getPosition());
}
/**
* Verifies that this chunk's saved text matches the corresponding text in
* the given sequence.
*
* @param target the sequence to verify against.
* @param fuzz the count of ignored prefix/suffix
* @param position the position of target
* @throws com.github.difflib.patch.PatchFailedException
*/
public VerifyChunk verifyChunk(List<T> target, int fuzz, int position) throws PatchFailedException {
//noinspection UnnecessaryLocalVariable
int startIndex = fuzz;
int lastIndex = size() - fuzz;
int last = position + size() - 1;
if (position + fuzz > target.size() || last - fuzz > target.size()) {
return VerifyChunk.POSITION_OUT_OF_TARGET;
}
for (int i = 0; i < size(); i++) {
for (int i = startIndex; i < lastIndex; i++) {
if (!target.get(position + i).equals(lines.get(i))) {
return VerifyChunk.CONTENT_DOES_NOT_MATCH_TARGET;
}

View File

@@ -35,6 +35,14 @@ public class EqualDelta<T> extends AbstractDelta<T> {
protected void restore(List<T> target) {
}
/**
* {@inheritDoc}
*/
@Override
protected void applyFuzzyToAt(List<T> target, int fuzz, int delta) {
// equals so no operations
}
@Override
public String toString() {
return "[EqualDelta, position: " + getSource().getPosition() + ", lines: "

View File

@@ -66,6 +66,117 @@ public final class Patch<T> implements Serializable {
return result;
}
private static class PatchApplyingContext<T> {
public final List<T> result;
public final int maxFuzz;
// the position last patch applied to.
public int lastPatchEnd = -1;
///// passing values from find to apply
public int currentFuzz = 0;
public int defaultPosition;
public boolean beforeOutRange = false;
public boolean afterOutRange = false;
private PatchApplyingContext(List<T> result, int maxFuzz) {
this.result = result;
this.maxFuzz = maxFuzz;
}
}
public List<T> applyFuzzy(List<T> target, int maxFuzz) throws PatchFailedException {
PatchApplyingContext<T> ctx = new PatchApplyingContext<>(new ArrayList<>(target), maxFuzz);
// the difference between patch's position and actually applied position
int lastPatchDelta = 0;
for (AbstractDelta<T> delta : getDeltas()) {
ctx.defaultPosition = delta.getSource().getPosition() + lastPatchDelta;
int patchPosition = findPositionFuzzy(ctx, delta);
if (0 <= patchPosition) {
delta.applyFuzzyToAt(ctx.result, ctx.currentFuzz, patchPosition);
lastPatchDelta = patchPosition - delta.getSource().getPosition();
ctx.lastPatchEnd = delta.getSource().last() + lastPatchDelta;
} else {
conflictOutput.processConflict(VerifyChunk.CONTENT_DOES_NOT_MATCH_TARGET, delta, ctx.result);
}
}
return ctx.result;
}
// negative for not found
private int findPositionFuzzy(PatchApplyingContext<T> ctx, AbstractDelta<T> delta) throws PatchFailedException {
for (int fuzz = 0; fuzz <= ctx.maxFuzz; fuzz++) {
ctx.currentFuzz = fuzz;
int foundPosition = findPositionWithFuzz(ctx, delta, fuzz);
if (foundPosition >= 0) {
return foundPosition;
}
}
return -1;
}
// negative for not found
private int findPositionWithFuzz(PatchApplyingContext<T> ctx, AbstractDelta<T> delta, int fuzz) throws PatchFailedException {
if (delta.getSource().verifyChunk(ctx.result, fuzz, ctx.defaultPosition) == VerifyChunk.OK) {
return ctx.defaultPosition;
}
ctx.beforeOutRange = false;
ctx.afterOutRange = false;
// moreDelta >= 0: just for overflow guard, not a normal condition
//noinspection OverflowingLoopIndex
for (int moreDelta = 0; moreDelta >= 0; moreDelta++) {
int pos = findPositionWithFuzzAndMoreDelta(ctx, delta, fuzz, moreDelta);
if (pos >= 0) {
return pos;
}
if (ctx.beforeOutRange && ctx.afterOutRange) {
break;
}
}
return -1;
}
// negative for not found
private int findPositionWithFuzzAndMoreDelta(PatchApplyingContext<T> ctx, AbstractDelta<T> delta, int fuzz, int moreDelta) throws PatchFailedException {
// range check: can't apply before end of last patch
if (!ctx.beforeOutRange) {
int beginAt = ctx.defaultPosition - moreDelta + fuzz;
// We can't apply patch before end of last patch.
if (beginAt <= ctx.lastPatchEnd) {
ctx.beforeOutRange = true;
}
}
// range check: can't apply after end of result
if (!ctx.afterOutRange) {
int beginAt = ctx.defaultPosition + moreDelta + delta.getSource().size() - fuzz;
// We can't apply patch before end of last patch.
if (ctx.result.size() < beginAt) {
ctx.afterOutRange = true;
}
}
if (!ctx.beforeOutRange) {
VerifyChunk before = delta.getSource().verifyChunk(ctx.result, fuzz, ctx.defaultPosition - moreDelta);
if (before == VerifyChunk.OK) {
return ctx.defaultPosition - moreDelta;
}
}
if (!ctx.afterOutRange) {
VerifyChunk after = delta.getSource().verifyChunk(ctx.result, fuzz, ctx.defaultPosition + moreDelta);
if (after == VerifyChunk.OK) {
return ctx.defaultPosition + moreDelta;
}
}
return -1;
}
/**
* Standard Patch behaviour to throw an exception for pathching conflicts.
*/

View File

@@ -173,6 +173,7 @@ public final class DiffRowGenerator {
private final boolean showInlineDiffs;
private final boolean replaceOriginalLinefeedInChangesWithSpaces;
private final boolean decompressDeltas;
private DiffRowGenerator(Builder builder) {
showInlineDiffs = builder.showInlineDiffs;
@@ -182,6 +183,7 @@ public final class DiffRowGenerator {
columnWidth = builder.columnWidth;
mergeOriginalRevised = builder.mergeOriginalRevised;
inlineDiffSplitter = builder.inlineDiffSplitter;
decompressDeltas = builder.decompressDeltas;
if (builder.equalizer != null) {
equalizer = builder.equalizer;
@@ -225,8 +227,14 @@ public final class DiffRowGenerator {
int endPos = 0;
final List<AbstractDelta<String>> deltaList = patch.getDeltas();
for (AbstractDelta<String> originalDelta : deltaList) {
for (AbstractDelta<String> delta : decompressDeltas(originalDelta)) {
if (decompressDeltas) {
for (AbstractDelta<String> originalDelta : deltaList) {
for (AbstractDelta<String> delta : decompressDeltas(originalDelta)) {
endPos = transformDeltaIntoDiffRow(original, endPos, diffRows, delta);
}
}
} else {
for (AbstractDelta<String> delta : deltaList) {
endPos = transformDeltaIntoDiffRow(original, endPos, diffRows, delta);
}
}
@@ -442,6 +450,7 @@ public final class DiffRowGenerator {
private boolean showInlineDiffs = false;
private boolean ignoreWhiteSpaces = false;
private boolean decompressDeltas = true;
private BiFunction<Tag, Boolean, String> oldTag
= (tag, f) -> f ? "<span class=\"editOldInline\">" : "</span>";
@@ -554,8 +563,9 @@ public final class DiffRowGenerator {
* Set the column width of generated lines of original and revised
* texts.
*
* @param width the width to set. Making it < 0 doesn't make any sense.
* Default 80. @return builder with config of column width
* @param width the width to set. Making it &lt; 0 doesn't make any
* sense. Default 80.
* @return builder with config of column width
*/
public Builder columnWidth(int width) {
if (width >= 0) {
@@ -586,6 +596,19 @@ public final class DiffRowGenerator {
return this;
}
/**
* Deltas could be in a state, that would produce some unreasonable
* results within an inline diff. So the deltas are decompressed into
* smaller parts and rebuild. But this could result in more differences.
*
* @param decompressDeltas
* @return
*/
public Builder decompressDeltas(boolean decompressDeltas) {
this.decompressDeltas = decompressDeltas;
return this;
}
/**
* Per default each character is separatly processed. This variant
* introduces processing by word, which does not deliver in word

View File

@@ -55,7 +55,7 @@ public final class UnifiedDiff {
return tail;
}
public List<String> spplyPatchTo(Predicate<String> findFile, List<String> originalLines) throws PatchFailedException {
public List<String> applyPatchTo(Predicate<String> findFile, List<String> originalLines) throws PatchFailedException {
UnifiedDiffFile file = files.stream()
.filter(diff -> findFile.test(diff.getFromFile()))
.findFirst().orElse(null);

View File

@@ -353,7 +353,7 @@ public final class UnifiedDiffReader {
line = line.substring(0, matcher.start());
}
line = line.split("\t")[0];
return line.substring(4).replaceFirst("^(a|b|old|new)(\\/)?", "")
return line.substring(4).replaceFirst("^(a|b|old|new)/", "")
.trim();
}

View File

@@ -189,10 +189,10 @@ public class UnifiedDiffWriter {
}
/**
* getDeltaText returns the lines to be added to the Unified Diff text from the Delta parameter
* getDeltaText returns the lines to be added to the Unified Diff text from the Delta parameter.
*
* @param delta - the Delta to output
* @return list of String lines of code.
* @param writer consumer for the list of String lines of code
* @param delta the Delta to output
*/
private static void getDeltaText(Consumer<String> writer, AbstractDelta<String> delta) {
for (String line : delta.getSource().getLines()) {

View File

@@ -2,6 +2,7 @@ package com.github.difflib.algorithm.myers;
import com.github.difflib.patch.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.fail;
import java.io.ByteArrayInputStream;
@@ -9,8 +10,13 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import org.junit.jupiter.api.Test;
@@ -57,6 +63,104 @@ public class WithMeyersDiffWithLinearSpacePatchTest {
}
}
// region testPatch_fuzzyApply utils
private List<String> intRange(int count) {
return IntStream.range(0, count)
.mapToObj(Integer::toString)
.collect(Collectors.toList());
}
@SafeVarargs
private final List<String> join(List<String>... lists) {
return Arrays.stream(lists).flatMap(Collection::stream).collect(Collectors.toList());
}
private static class FuzzyApplyTestPair {
public final List<String> from;
public final List<String> to;
public final int requiredFuzz;
private FuzzyApplyTestPair(List<String> from, List<String> to, int requiredFuzz) {
this.from = from;
this.to = to;
this.requiredFuzz = requiredFuzz;
}
}
// endregion
@Test
public void fuzzyApply() throws PatchFailedException {
Patch<String> patch = new Patch<>();
List<String> deltaFrom = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee", "fff");
List<String> deltaTo = Arrays.asList("aaa", "bbb", "cxc", "dxd", "eee", "fff");
patch.addDelta(new ChangeDelta<>(
new Chunk<>(6, deltaFrom),
new Chunk<>(6, deltaTo)));
//noinspection unchecked
List<String>[] moves = new List[] {
intRange(6), // no patch move
intRange(3), // forward patch move
intRange(9), // backward patch move
intRange(0), // apply to the first
};
for (FuzzyApplyTestPair pair : FUZZY_APPLY_TEST_PAIRS) {
for (List<String> move : moves) {
List<String> from = join(move, pair.from);
List<String> to = join(move, pair.to);
for (int i = 0; i < pair.requiredFuzz; i++) {
int maxFuzz = i;
assertThrows(PatchFailedException.class, () ->
patch.applyFuzzy(from, maxFuzz),
() -> "fail for " + from + " -> " + to + " for fuzz " + maxFuzz + " required " + pair.requiredFuzz);
}
for (int i = pair.requiredFuzz; i < 4; i++) {
int maxFuzz = i;
assertEquals(to, patch.applyFuzzy(from, maxFuzz),
() -> "with " + maxFuzz);
}
}
}
}
@Test
public void fuzzyApplyTwoSideBySidePatches() throws PatchFailedException {
Patch<String> patch = new Patch<>();
List<String> deltaFrom = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee", "fff");
List<String> deltaTo = Arrays.asList("aaa", "bbb", "cxc", "dxd", "eee", "fff");
patch.addDelta(new ChangeDelta<>(
new Chunk<>(0, deltaFrom),
new Chunk<>(0, deltaTo)));
patch.addDelta(new ChangeDelta<>(
new Chunk<>(6, deltaFrom),
new Chunk<>(6, deltaTo)));
assertEquals(join(deltaTo, deltaTo), patch.applyFuzzy(join(deltaFrom, deltaFrom), 0));
}
@Test
public void fuzzyApplyToNearest() throws PatchFailedException {
Patch<String> patch = new Patch<>();
List<String> deltaFrom = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee", "fff");
List<String> deltaTo = Arrays.asList("aaa", "bbb", "cxc", "dxd", "eee", "fff");
patch.addDelta(new ChangeDelta<>(
new Chunk<>(0, deltaFrom),
new Chunk<>(0, deltaTo)));
patch.addDelta(new ChangeDelta<>(
new Chunk<>(10, deltaFrom),
new Chunk<>(10, deltaTo)));
assertEquals(join(deltaTo, deltaFrom, deltaTo),
patch.applyFuzzy(join(deltaFrom, deltaFrom, deltaFrom), 0));
assertEquals(join(intRange(1), deltaTo, deltaFrom, deltaTo),
patch.applyFuzzy(join(intRange(1), deltaFrom, deltaFrom, deltaFrom), 0));
}
@Test
public void testPatch_Serializable() throws IOException, ClassNotFoundException {
final List<String> changeTest_from = Arrays.asList("aaa", "bbb", "ccc", "ddd");
@@ -101,4 +205,190 @@ public class WithMeyersDiffWithLinearSpacePatchTest {
fail(e.getMessage());
}
}
static class FuzzyApplyTestDataGenerator {
private static String createList(List<String> values) {
return values.stream()
.map(x -> '"' + x + '"')
.collect(Collectors.joining(", ", "Arrays.asList(", ")"));
}
public static void main(String[] args) {
String[] deltaFrom = new String[] { "aaa", "bbb", "ccc", "ddd", "eee", "fff" };
String[] deltaTo = new String[] { "aaa", "bbb", "cxc", "dxd", "eee", "fff" };
List<FuzzyApplyTestPair> pairs = new ArrayList<>();
// create test data.
// Brute-force search
String[] changedValue = new String[]{"axa", "bxb", "czc", "dzd", "exe", "fxf"};
for (int i = 0; i < 1 << 6; i++) {
if ((i & 0b001100) != 0 && (i & 0b001100) != 0b001100) {
continue;
}
String[] from = deltaFrom.clone();
String[] to = deltaTo.clone();
for (int j = 0; j < 6; j++) {
if ((i & (1 << j)) != 0) {
from[j] = changedValue[j];
to[j] = changedValue[j];
}
}
int requiredFuzz;
if ((i & 0b001100) != 0) {
requiredFuzz = 3;
} else if ((i & 0b010010) != 0) {
requiredFuzz = 2;
} else if ((i & 0b100001) != 0) {
requiredFuzz = 1;
} else {
requiredFuzz = 0;
}
pairs.add(new FuzzyApplyTestPair(Arrays.asList(from), Arrays.asList(to), requiredFuzz));
}
pairs.sort(Comparator.comparingInt(a -> a.requiredFuzz));
System.out.println("FuzzyApplyTestPair[] pairs = new FuzzyApplyTestPair[] {");
for (FuzzyApplyTestPair pair : pairs) {
System.out.println(" new FuzzyApplyTestPair(");
System.out.println(" " + createList(pair.from) + ",");
System.out.println(" " + createList(pair.to) + ",");
System.out.println(" " + pair.requiredFuzz + "),");
}
System.out.println("};");
}
}
private static final FuzzyApplyTestPair[] FUZZY_APPLY_TEST_PAIRS = new FuzzyApplyTestPair[] {
new FuzzyApplyTestPair(
Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee", "fff"),
Arrays.asList("aaa", "bbb", "cxc", "dxd", "eee", "fff"),
0),
new FuzzyApplyTestPair(
Arrays.asList("axa", "bbb", "ccc", "ddd", "eee", "fff"),
Arrays.asList("axa", "bbb", "cxc", "dxd", "eee", "fff"),
1),
new FuzzyApplyTestPair(
Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee", "fxf"),
Arrays.asList("aaa", "bbb", "cxc", "dxd", "eee", "fxf"),
1),
new FuzzyApplyTestPair(
Arrays.asList("axa", "bbb", "ccc", "ddd", "eee", "fxf"),
Arrays.asList("axa", "bbb", "cxc", "dxd", "eee", "fxf"),
1),
new FuzzyApplyTestPair(
Arrays.asList("aaa", "bxb", "ccc", "ddd", "eee", "fff"),
Arrays.asList("aaa", "bxb", "cxc", "dxd", "eee", "fff"),
2),
new FuzzyApplyTestPair(
Arrays.asList("axa", "bxb", "ccc", "ddd", "eee", "fff"),
Arrays.asList("axa", "bxb", "cxc", "dxd", "eee", "fff"),
2),
new FuzzyApplyTestPair(
Arrays.asList("aaa", "bbb", "ccc", "ddd", "exe", "fff"),
Arrays.asList("aaa", "bbb", "cxc", "dxd", "exe", "fff"),
2),
new FuzzyApplyTestPair(
Arrays.asList("axa", "bbb", "ccc", "ddd", "exe", "fff"),
Arrays.asList("axa", "bbb", "cxc", "dxd", "exe", "fff"),
2),
new FuzzyApplyTestPair(
Arrays.asList("aaa", "bxb", "ccc", "ddd", "exe", "fff"),
Arrays.asList("aaa", "bxb", "cxc", "dxd", "exe", "fff"),
2),
new FuzzyApplyTestPair(
Arrays.asList("axa", "bxb", "ccc", "ddd", "exe", "fff"),
Arrays.asList("axa", "bxb", "cxc", "dxd", "exe", "fff"),
2),
new FuzzyApplyTestPair(
Arrays.asList("aaa", "bxb", "ccc", "ddd", "eee", "fxf"),
Arrays.asList("aaa", "bxb", "cxc", "dxd", "eee", "fxf"),
2),
new FuzzyApplyTestPair(
Arrays.asList("axa", "bxb", "ccc", "ddd", "eee", "fxf"),
Arrays.asList("axa", "bxb", "cxc", "dxd", "eee", "fxf"),
2),
new FuzzyApplyTestPair(
Arrays.asList("aaa", "bbb", "ccc", "ddd", "exe", "fxf"),
Arrays.asList("aaa", "bbb", "cxc", "dxd", "exe", "fxf"),
2),
new FuzzyApplyTestPair(
Arrays.asList("axa", "bbb", "ccc", "ddd", "exe", "fxf"),
Arrays.asList("axa", "bbb", "cxc", "dxd", "exe", "fxf"),
2),
new FuzzyApplyTestPair(
Arrays.asList("aaa", "bxb", "ccc", "ddd", "exe", "fxf"),
Arrays.asList("aaa", "bxb", "cxc", "dxd", "exe", "fxf"),
2),
new FuzzyApplyTestPair(
Arrays.asList("axa", "bxb", "ccc", "ddd", "exe", "fxf"),
Arrays.asList("axa", "bxb", "cxc", "dxd", "exe", "fxf"),
2),
new FuzzyApplyTestPair(
Arrays.asList("aaa", "bbb", "czc", "dzd", "eee", "fff"),
Arrays.asList("aaa", "bbb", "czc", "dzd", "eee", "fff"),
3),
new FuzzyApplyTestPair(
Arrays.asList("axa", "bbb", "czc", "dzd", "eee", "fff"),
Arrays.asList("axa", "bbb", "czc", "dzd", "eee", "fff"),
3),
new FuzzyApplyTestPair(
Arrays.asList("aaa", "bxb", "czc", "dzd", "eee", "fff"),
Arrays.asList("aaa", "bxb", "czc", "dzd", "eee", "fff"),
3),
new FuzzyApplyTestPair(
Arrays.asList("axa", "bxb", "czc", "dzd", "eee", "fff"),
Arrays.asList("axa", "bxb", "czc", "dzd", "eee", "fff"),
3),
new FuzzyApplyTestPair(
Arrays.asList("aaa", "bbb", "czc", "dzd", "exe", "fff"),
Arrays.asList("aaa", "bbb", "czc", "dzd", "exe", "fff"),
3),
new FuzzyApplyTestPair(
Arrays.asList("axa", "bbb", "czc", "dzd", "exe", "fff"),
Arrays.asList("axa", "bbb", "czc", "dzd", "exe", "fff"),
3),
new FuzzyApplyTestPair(
Arrays.asList("aaa", "bxb", "czc", "dzd", "exe", "fff"),
Arrays.asList("aaa", "bxb", "czc", "dzd", "exe", "fff"),
3),
new FuzzyApplyTestPair(
Arrays.asList("axa", "bxb", "czc", "dzd", "exe", "fff"),
Arrays.asList("axa", "bxb", "czc", "dzd", "exe", "fff"),
3),
new FuzzyApplyTestPair(
Arrays.asList("aaa", "bbb", "czc", "dzd", "eee", "fxf"),
Arrays.asList("aaa", "bbb", "czc", "dzd", "eee", "fxf"),
3),
new FuzzyApplyTestPair(
Arrays.asList("axa", "bbb", "czc", "dzd", "eee", "fxf"),
Arrays.asList("axa", "bbb", "czc", "dzd", "eee", "fxf"),
3),
new FuzzyApplyTestPair(
Arrays.asList("aaa", "bxb", "czc", "dzd", "eee", "fxf"),
Arrays.asList("aaa", "bxb", "czc", "dzd", "eee", "fxf"),
3),
new FuzzyApplyTestPair(
Arrays.asList("axa", "bxb", "czc", "dzd", "eee", "fxf"),
Arrays.asList("axa", "bxb", "czc", "dzd", "eee", "fxf"),
3),
new FuzzyApplyTestPair(
Arrays.asList("aaa", "bbb", "czc", "dzd", "exe", "fxf"),
Arrays.asList("aaa", "bbb", "czc", "dzd", "exe", "fxf"),
3),
new FuzzyApplyTestPair(
Arrays.asList("axa", "bbb", "czc", "dzd", "exe", "fxf"),
Arrays.asList("axa", "bbb", "czc", "dzd", "exe", "fxf"),
3),
new FuzzyApplyTestPair(
Arrays.asList("aaa", "bxb", "czc", "dzd", "exe", "fxf"),
Arrays.asList("aaa", "bxb", "czc", "dzd", "exe", "fxf"),
3),
new FuzzyApplyTestPair(
Arrays.asList("axa", "bxb", "czc", "dzd", "exe", "fxf"),
Arrays.asList("axa", "bxb", "czc", "dzd", "exe", "fxf"),
3),
};
}

View File

@@ -12,11 +12,11 @@ import java.util.List;
public class ComputeDifference {
private static final String ORIGINAL = TestConstants.MOCK_FOLDER + "original.txt";
private static final String RIVISED = TestConstants.MOCK_FOLDER + "revised.txt";
private static final String REVISED = TestConstants.MOCK_FOLDER + "revised.txt";
public static void main(String[] args) throws IOException {
List<String> original = Files.readAllLines(new File(ORIGINAL).toPath());
List<String> revised = Files.readAllLines(new File(RIVISED).toPath());
List<String> revised = Files.readAllLines(new File(REVISED).toPath());
// Compute diff. Get the Patch object. Patch is the container for computed deltas.
Patch<String> patch = DiffUtils.diff(original, revised);

View File

@@ -0,0 +1,43 @@
package com.github.difflib.patch;
import org.junit.jupiter.api.Test;
import java.util.List;
import java.util.stream.Collectors;
import static org.junit.jupiter.api.Assertions.assertEquals;
class ChunkTest {
@Test
void verifyChunk() throws PatchFailedException {
Chunk<Character> chunk = new Chunk<>(7, toCharList("test"));
// normal check
assertEquals(VerifyChunk.OK,
chunk.verifyChunk(toCharList("prefix test suffix")));
assertEquals(VerifyChunk.CONTENT_DOES_NOT_MATCH_TARGET,
chunk.verifyChunk(toCharList("prefix es suffix"), 0, 7));
// position
assertEquals(VerifyChunk.OK,
chunk.verifyChunk(toCharList("short test suffix"), 0, 6));
assertEquals(VerifyChunk.OK,
chunk.verifyChunk(toCharList("loonger test suffix"), 0, 8));
assertEquals(VerifyChunk.CONTENT_DOES_NOT_MATCH_TARGET,
chunk.verifyChunk(toCharList("prefix test suffix"), 0, 6));
assertEquals(VerifyChunk.CONTENT_DOES_NOT_MATCH_TARGET,
chunk.verifyChunk(toCharList("prefix test suffix"), 0, 8));
// fuzz
assertEquals(VerifyChunk.OK,
chunk.verifyChunk(toCharList("prefix test suffix"), 1, 7));
assertEquals(VerifyChunk.OK,
chunk.verifyChunk(toCharList("prefix es suffix"), 1, 7));
assertEquals(VerifyChunk.CONTENT_DOES_NOT_MATCH_TARGET,
chunk.verifyChunk(toCharList("prefix suffix"), 1, 7));
}
private List<Character> toCharList(String str) {
return str.chars().mapToObj(x -> (char) x).collect(Collectors.toList());
}
}

View File

@@ -16,8 +16,10 @@
package com.github.difflib.patch;
import com.github.difflib.DiffUtils;
import static com.github.difflib.patch.Patch.CONFLICT_PRODUCES_MERGE_CONFLICT;
import java.util.Arrays;
import java.util.List;
import static java.util.stream.Collectors.joining;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;
import org.junit.jupiter.api.Test;
@@ -27,6 +29,7 @@ import org.junit.jupiter.api.Test;
* @author tw
*/
public class PatchWithMeyerDiffTest {
@Test
public void testPatch_Change_withExceptionProcessor() {
final List<String> changeTest_from = Arrays.asList("aaa", "bbb", "ccc", "ddd");
@@ -48,4 +51,18 @@ public class PatchWithMeyerDiffTest {
fail(e.getMessage());
}
}
@Test
public void testPatchThreeWayIssue138() throws PatchFailedException {
List<String> base = Arrays.asList("Imagine there's no heaven".split("\\s+"));
List<String> left = Arrays.asList("Imagine there's no HEAVEN".split("\\s+"));
List<String> right = Arrays.asList("IMAGINE there's no heaven".split("\\s+"));
Patch<String> rightPatch = DiffUtils.diff(base, right)
.withConflictOutput(CONFLICT_PRODUCES_MERGE_CONFLICT);
List<String> applied = rightPatch.applyTo(left);
assertEquals("IMAGINE there's no HEAVEN", applied.stream().collect(joining(" ")));
}
}

View File

@@ -641,7 +641,7 @@ public class DiffRowGeneratorTest {
assertThat(rows).extracting(item -> item.getTag().name()).containsExactly("CHANGE", "DELETE", "EQUAL", "CHANGE", "EQUAL");
}
@Test
public void testCorrectChangeIssue114_2() throws IOException {
List<String> original = Arrays.asList("A", "B", "C", "D", "E");
@@ -662,7 +662,7 @@ public class DiffRowGeneratorTest {
assertThat(rows).extracting(item -> item.getTag().name()).containsExactly("CHANGE", "DELETE", "EQUAL", "CHANGE", "EQUAL");
assertThat(rows.get(1).toString()).isEqualTo("[DELETE,~B~,]");
}
@Test
public void testIssue119WrongContextLength() throws IOException {
String original = Files.lines(Paths.get("target/test-classes/com/github/difflib/text/issue_119_original.txt")).collect(joining("\n"));
@@ -683,4 +683,106 @@ public class DiffRowGeneratorTest {
.filter(item -> item.getTag() != DiffRow.Tag.EQUAL)
.forEach(System.out::println);
}
@Test
public void testIssue129WithDeltaDecompression() {
List<String> lines1 = Arrays.asList(
"apple1",
"apple2",
"apple3",
"A man named Frankenstein abc to Switzerland for cookies!",
"banana1",
"banana2",
"banana3");
List<String> lines2 = Arrays.asList(
"apple1",
"apple2",
"apple3",
"A man named Frankenstein",
"xyz",
"to Switzerland for cookies!",
"banana1",
"banana2",
"banana3");
int[] entry = {1};
String txt = DiffRowGenerator.create()
.showInlineDiffs(true)
.oldTag((tag, isOpening) -> isOpening ? "==old" + tag + "==>" : "<==old==")
.newTag((tag, isOpening) -> isOpening ? "==new" + tag + "==>" : "<==new==")
.build()
.generateDiffRows(lines1, lines2)
.stream()
.map(row -> row.getTag().toString())
.collect(joining(" "));
// .forEachOrdered(row -> {
// System.out.printf("%4d %-8s %-80s %-80s\n", entry[0]++,
// row.getTag(), row.getOldLine(), row.getNewLine());
// });
assertThat(txt).isEqualTo("EQUAL EQUAL EQUAL CHANGE INSERT INSERT EQUAL EQUAL EQUAL");
}
@Test
public void testIssue129SkipDeltaDecompression() {
List<String> lines1 = Arrays.asList(
"apple1",
"apple2",
"apple3",
"A man named Frankenstein abc to Switzerland for cookies!",
"banana1",
"banana2",
"banana3");
List<String> lines2 = Arrays.asList(
"apple1",
"apple2",
"apple3",
"A man named Frankenstein",
"xyz",
"to Switzerland for cookies!",
"banana1",
"banana2",
"banana3");
int[] entry = {1};
String txt =
DiffRowGenerator.create()
.showInlineDiffs(true)
.decompressDeltas(false)
.oldTag((tag, isOpening) -> isOpening ? "==old" + tag + "==>" : "<==old==")
.newTag((tag, isOpening) -> isOpening ? "==new" + tag + "==>" : "<==new==")
.build()
.generateDiffRows(lines1, lines2)
.stream()
.map(row -> row.getTag().toString())
.collect(joining(" "));
// .forEachOrdered(row -> {
// System.out.printf("%4d %-8s %-80s %-80s\n", entry[0]++,
// row.getTag(), row.getOldLine(), row.getNewLine());
// });
assertThat(txt).isEqualTo("EQUAL EQUAL EQUAL CHANGE CHANGE CHANGE EQUAL EQUAL EQUAL");
}
@Test
public void testIssue129SkipWhitespaceChanges() throws IOException {
String original = Files.lines(Paths.get("target/test-classes/com/github/difflib/text/issue129_1.txt")).collect(joining("\n"));
String revised = Files.lines(Paths.get("target/test-classes/com/github/difflib/text/issue129_2.txt")).collect(joining("\n"));
DiffRowGenerator generator = DiffRowGenerator.create()
.showInlineDiffs(true)
.mergeOriginalRevised(true)
.inlineDiffByWord(true)
.ignoreWhiteSpaces(true)
.oldTag((tag, isOpening) -> isOpening ? "==old" + tag + "==>" : "<==old==")
.newTag((tag, isOpening) -> isOpening ? "==new" + tag + "==>" : "<==new==")
.build();
List<DiffRow> rows = generator.generateDiffRows(
Arrays.asList(original.split("\n")),
Arrays.asList(revised.split("\n")));
assertThat(rows).hasSize(13);
rows.stream()
.filter(item -> item.getTag() != DiffRow.Tag.EQUAL)
.forEach(System.out::println);
}
}

View File

@@ -122,7 +122,7 @@ public class UnifiedDiffReaderTest {
assertThat(diff.getFiles().size()).isEqualTo(1);
UnifiedDiffFile file1 = diff.getFiles().get(0);
assertThat(file1.getFromFile()).isEqualTo(".vhd");
assertThat(file1.getFromFile()).isEqualTo("a.vhd");
assertThat(file1.getPatch().getDeltas().size()).isEqualTo(1);
assertThat(diff.getTail()).isNull();
@@ -384,4 +384,14 @@ public class UnifiedDiffReaderTest {
assertThat(diff.getFiles()).extracting(f -> f.getFromFile()).contains("src/java/main/org/apache/zookeeper/server/FinalRequestProcessor.java");
}
@Test
public void testParseIssue141() throws IOException {
UnifiedDiff diff = UnifiedDiffReader.parseUnifiedDiff(
UnifiedDiffReaderTest.class.getResourceAsStream("problem_diff_issue141.diff"));
UnifiedDiffFile file1 = diff.getFiles().get(0);
assertThat(file1.getFromFile()).isEqualTo("a.txt");
assertThat(file1.getToFile()).isEqualTo("a1.txt");
}
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright 2022 java-diff-utils.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.github.difflib.unifieddiff;
import com.github.difflib.patch.PatchFailedException;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.Arrays;
import static java.util.stream.Collectors.joining;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
@Disabled("for next release")
public class UnifiedDiffRoundTripNewLineTest {
@Test
public void testIssue135MissingNoNewLineInPatched() throws IOException, PatchFailedException {
String beforeContent = "rootProject.name = \"sample-repo\"";
String afterContent = "rootProject.name = \"sample-repo\"\n";
String patch = "diff --git a/settings.gradle b/settings.gradle\n" +
"index ef3b8e2..ab30124 100644\n" +
"--- a/settings.gradle\n" +
"+++ b/settings.gradle\n" +
"@@ -1 +1 @@\n" +
"-rootProject.name = \"sample-repo\"\n" +
"\\ No newline at end of file\n" +
"+rootProject.name = \"sample-repo\"\n";
UnifiedDiff unifiedDiff = UnifiedDiffReader.parseUnifiedDiff(new ByteArrayInputStream(patch.getBytes()));
String unifiedAfterContent = unifiedDiff.getFiles().get(0).getPatch()
.applyTo(Arrays.asList(beforeContent.split("\n"))).stream().collect(joining("\n"));
assertEquals(afterContent, unifiedAfterContent);
}
}

View File

@@ -150,7 +150,7 @@ public class UnifiedDiffRoundTripTest {
// Patch<String> fromUnifiedPatch = unifiedDiff.getFiles().get(0).getPatch();
// patchedLines = fromUnifiedPatch.applyTo(origLines);
// }
patchedLines = unifiedDiff.spplyPatchTo(file -> originalFile.equals(file), origLines);
patchedLines = unifiedDiff.applyPatchTo(file -> originalFile.equals(file), origLines);
assertEquals(revLines.size(), patchedLines.size());
for (int i = 0; i < revLines.size(); i++) {
String l1 = revLines.get(i);

View File

@@ -0,0 +1,12 @@
Four score and seven years ago our fathers brought forth on this continent, a new nation, conceived in Liberty, and dedicated
to the proposition that all men are created equal. Now we are engaged in a great civil war, testing whether that nation, or
any nation so conceived and so dedicated, can long endure. We are met on a great battle-field of that war. We have come to
dedicate a portion of that field, as a final resting place for those who here gave their lives that that nation might live.
It is altogether fitting and proper that we should do this. But, in a larger sense, we can not dedicate -- we can not
consecrate -- we can not hallow -- this ground. The brave men, living and dead, who struggled here, have consecrated it,
far above our poor power to add or detract. The world will little note, nor long remember what we say here, but it can never
forget what they did here. It is for us the living, rather, to be dedicated here to the unfinished work which they who fought
here have thus far so nobly advanced. It is rather for us to be here dedicated to the great task remaining before us -- that
from these honored dead we take increased devotion to that cause for which they gave the last full measure of devotion -- that
we here highly resolve that these dead shall not have died in vain -- that this nation, under God, shall have a new birth of
freedom -- and that government of the people, by the people, for the people, shall not perish from the earth.

View File

@@ -0,0 +1,13 @@
Four score and seven years ago our fathers brought forth on this continent, a new nation, conceived in Liberty, and
dedicated to the proposition that all men are created equal. Now we are engaged in a great civil war, testing whether
that nation, or any nation so conceived and so dedicated, can long endure. We are met on a great battle-field of that
war. We have come to dedicate a portion of that field, as a final resting place for those who here gave their lives
that that nation might live. It is altogether fitting and proper that we should do this. But, in a larger sense, we
can not dedicate -- we can not consecrate -- we can not hallow -- this ground. The brave men, living and dead, who
struggled here, have consecrated it, far above our poor power to add or detract. The world will little note, nor long
remember what we say here, but it can never forget what they did here. It is for us the living, rather, to be dedicated
here to the unfinished work which they who fought here have thus far so nobly advanced. It is rather for us to be here
dedicated to the great task remaining before us -- that from these honored dead we take increased devotion to that cause
for which they gave the last full measure of devotion -- that we here highly resolve that these dead shall not have died
in vain -- that this nation, under God, shall have a new birth of freedom -- and that government of the people, by the
people, for the people, shall not perish from the earth.

View File

@@ -0,0 +1,11 @@
--- a.txt
+++ a1.txt
@@ -8,7 +8,7 @@
<Setting>
<Setting a>
<setting b>
- <value>23</value>
+ <value>24</value>
</setting b>
<setting c>
<value>1</value>

View File

@@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>io.github.java-diff-utils</groupId>
<artifactId>java-diff-utils-parent</artifactId>
<version>4.11-SNAPSHOT</version>
<version>4.12</version>
<name>java-diff-utils-parent</name>
<packaging>pom</packaging>
<modules>
@@ -29,7 +29,7 @@
<connection>scm:git:https://github.com/java-diff-utils/java-diff-utils.git</connection>
<developerConnection>scm:git:ssh://git@github.com:java-diff-utils/java-diff-utils.git</developerConnection>
<url>https://github.com/java-diff-utils/java-diff-utils.git</url>
<tag>HEAD</tag>
<tag>java-diff-utils-parent-4.12</tag>
</scm>
<issueManagement>
<system>GitHub Issues</system>
@@ -199,7 +199,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>1.4</version>
<version>1.6</version>
<executions>
<execution>
<id>sign-artifacts</id>