diff --git a/java-diff-utils/src/main/java/com/github/difflib/UnifiedDiffUtils.java b/java-diff-utils/src/main/java/com/github/difflib/UnifiedDiffUtils.java index ca6a34a..8dba67e 100644 --- a/java-diff-utils/src/main/java/com/github/difflib/UnifiedDiffUtils.java +++ b/java-diff-utils/src/main/java/com/github/difflib/UnifiedDiffUtils.java @@ -69,6 +69,13 @@ public static Patch parseUnifiedDiff(List diff) { old_ln = m.group(1) == null ? 1 : Integer.parseInt(m.group(1)); new_ln = m.group(3) == null ? 1 : Integer.parseInt(m.group(3)); + // an empty range names the line before it + if ("0".equals(m.group(2))) { + old_ln++; + } + if ("0".equals(m.group(4))) { + new_ln++; + } if (old_ln == 0) { old_ln = 1; } @@ -278,6 +285,14 @@ private static List processDeltas( revTotal++; } + // an empty range names the line before it + if (origTotal == 0 && origStart > 0) { + origStart--; + } + if (revTotal == 0) { + revStart--; + } + // Create and insert the block header, conforming to the Unified Diff // standard StringBuilder header = new StringBuilder(); diff --git a/java-diff-utils/src/main/java/com/github/difflib/unifieddiff/UnifiedDiffReader.java b/java-diff-utils/src/main/java/com/github/difflib/unifieddiff/UnifiedDiffReader.java index 9be9b0f..3b7bd8b 100644 --- a/java-diff-utils/src/main/java/com/github/difflib/unifieddiff/UnifiedDiffReader.java +++ b/java-diff-utils/src/main/java/com/github/difflib/unifieddiff/UnifiedDiffReader.java @@ -366,6 +366,13 @@ private void processChunk(MatchResult match, String chunkStart) { old_size = toInteger(match, 2, 1); new_ln = toInteger(match, 3, 1); new_size = toInteger(match, 4, 1); + // an empty range names the line before it + if (old_size == 0) { + old_ln++; + } + if (new_size == 0) { + new_ln++; + } if (old_ln == 0) { old_ln = 1; } diff --git a/java-diff-utils/src/main/java/com/github/difflib/unifieddiff/UnifiedDiffWriter.java b/java-diff-utils/src/main/java/com/github/difflib/unifieddiff/UnifiedDiffWriter.java index aadea70..ea33776 100644 --- a/java-diff-utils/src/main/java/com/github/difflib/unifieddiff/UnifiedDiffWriter.java +++ b/java-diff-utils/src/main/java/com/github/difflib/unifieddiff/UnifiedDiffWriter.java @@ -196,6 +196,14 @@ private static void processDeltas( revTotal++; } + // an empty range names the line before it + if (origTotal == 0 && origStart > 0) { + origStart--; + } + if (revTotal == 0) { + revStart--; + } + // Create and insert the block header, conforming to the Unified Diff // standard writer.accept("@@ -" + origStart + "," + origTotal + " +" + revStart + "," + revTotal + " @@"); diff --git a/java-diff-utils/src/test/java/com/github/difflib/GenerateUnifiedDiffTest.java b/java-diff-utils/src/test/java/com/github/difflib/GenerateUnifiedDiffTest.java index 3e2357d..f3915f1 100644 --- a/java-diff-utils/src/test/java/com/github/difflib/GenerateUnifiedDiffTest.java +++ b/java-diff-utils/src/test/java/com/github/difflib/GenerateUnifiedDiffTest.java @@ -138,6 +138,35 @@ public void testNewFileCreation() { UnifiedDiffUtils.parseUnifiedDiff(udiff); } + @Test + public void testZeroContextEmptyRangeHeader() throws PatchFailedException { + List original = Arrays.asList("a", "b", "c"); + List revised = Arrays.asList("a", "b", "X", "c"); + + Patch patch = DiffUtils.diff(original, revised); + List udiff = UnifiedDiffUtils.generateUnifiedDiff("original", "revised", original, patch, 0); + + assertEquals("@@ -2,0 +3,1 @@", udiff.get(2)); + assertEquals(revised, UnifiedDiffUtils.parseUnifiedDiff(udiff).applyTo(original)); + } + + @Test + public void testParseZeroContextEmptyRanges() throws PatchFailedException { + // as written by diff -U0 and git diff -U0 + List insert = Arrays.asList("--- original", "+++ revised", "@@ -2,0 +3 @@", "+X"); + assertEquals( + Arrays.asList("a", "b", "X", "c"), + UnifiedDiffUtils.parseUnifiedDiff(insert).applyTo(Arrays.asList("a", "b", "c"))); + + List delete = Arrays.asList("--- original", "+++ revised", "@@ -2 +1,0 @@", "-b"); + assertEquals( + Arrays.asList("a", "c"), + UnifiedDiffUtils.parseUnifiedDiff(delete).applyTo(Arrays.asList("a", "b", "c"))); + assertEquals( + Arrays.asList("a", "b", "c"), + UnifiedDiffUtils.parseUnifiedDiff(delete).restore(Arrays.asList("a", "c"))); + } + /** * Issue 89 */ diff --git a/java-diff-utils/src/test/java/com/github/difflib/unifieddiff/UnifiedDiffReaderTest.java b/java-diff-utils/src/test/java/com/github/difflib/unifieddiff/UnifiedDiffReaderTest.java index ee7e1d9..774ac1b 100644 --- a/java-diff-utils/src/test/java/com/github/difflib/unifieddiff/UnifiedDiffReaderTest.java +++ b/java-diff-utils/src/test/java/com/github/difflib/unifieddiff/UnifiedDiffReaderTest.java @@ -21,7 +21,12 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import com.github.difflib.patch.AbstractDelta; +import com.github.difflib.patch.Patch; +import com.github.difflib.patch.PatchFailedException; +import java.io.ByteArrayInputStream; import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.junit.jupiter.api.Test; @@ -503,4 +508,16 @@ public void testParseIssue193Copy() throws IOException { assertThat(file1.getCopyTo()) .isEqualTo("modules/configuration/config/web/pcf/account/AccountContactCV.default.pcf"); } + + @Test + public void testParseZeroContextEmptyRanges() throws IOException, PatchFailedException { + // as written by diff -U0 and git diff -U0 + String text = "--- a/f\n+++ b/f\n@@ -2,0 +3 @@\n+X\n@@ -4 +4,0 @@\n-d\n"; + UnifiedDiff diff = + UnifiedDiffReader.parseUnifiedDiff(new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8))); + + Patch patch = diff.getFiles().get(0).getPatch(); + assertThat(patch.applyTo(Arrays.asList("a", "b", "c", "d"))).containsExactly("a", "b", "X", "c"); + assertThat(patch.restore(Arrays.asList("a", "b", "X", "c"))).containsExactly("a", "b", "c", "d"); + } } diff --git a/java-diff-utils/src/test/java/com/github/difflib/unifieddiff/UnifiedDiffWriterTest.java b/java-diff-utils/src/test/java/com/github/difflib/unifieddiff/UnifiedDiffWriterTest.java index a43c579..3de1d4b 100644 --- a/java-diff-utils/src/test/java/com/github/difflib/unifieddiff/UnifiedDiffWriterTest.java +++ b/java-diff-utils/src/test/java/com/github/difflib/unifieddiff/UnifiedDiffWriterTest.java @@ -31,6 +31,7 @@ import java.nio.file.Files; import java.nio.file.Paths; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; import org.junit.jupiter.api.Test; @@ -86,6 +87,23 @@ public void testWriteWithNewFile() throws URISyntaxException, IOException { assertEquals("@@ -0,0 +1,2 @@", lines[2]); } + @Test + public void testWriteZeroContextEmptyRange() throws IOException { + List original = Arrays.asList("a", "b", "c"); + List revised = Arrays.asList("a", "b", "X", "c"); + + Patch patch = DiffUtils.diff(original, revised); + UnifiedDiff diff = new UnifiedDiff(); + diff.addFile(UnifiedDiffFile.from("original", "revised", patch)); + + StringWriter writer = new StringWriter(); + UnifiedDiffWriter.write(diff, f -> original, writer, 0); + + String[] lines = writer.toString().split("\\n"); + + assertEquals("@@ -2,0 +3,1 @@", lines[2]); + } + @ParameterizedTest @ValueSource(ints = {1, 2, 9, 16}) public void testPropagateWriterFailures(int failOnWrite) {