From 9d31fd53ea7667e8fd104e81c6471690a2ff03c4 Mon Sep 17 00:00:00 2001 From: catbref Date: Mon, 25 Jun 2018 10:47:09 +0100 Subject: [PATCH] Empty-context add-only patches were applied in the wrong place, typically one line early. --- .../src/main/java/difflib/DiffUtils.java | 53 ++++++----- .../EmptyContextUnifiedDiffTest.java | 90 +++++++++++++++++++ .../mocks/unified_empty_context_original.txt | 5 ++ .../mocks/unified_empty_context_patch.txt | 6 ++ .../mocks/unified_empty_context_revised.txt | 5 ++ 5 files changed, 135 insertions(+), 24 deletions(-) create mode 100644 java-diff-utils-lib/src/test/java/diffutils/EmptyContextUnifiedDiffTest.java create mode 100644 java-diff-utils-lib/src/test/resources/mocks/unified_empty_context_original.txt create mode 100644 java-diff-utils-lib/src/test/resources/mocks/unified_empty_context_patch.txt create mode 100644 java-diff-utils-lib/src/test/resources/mocks/unified_empty_context_revised.txt diff --git a/java-diff-utils-lib/src/main/java/difflib/DiffUtils.java b/java-diff-utils-lib/src/main/java/difflib/DiffUtils.java index 0178d56..9400cd1 100644 --- a/java-diff-utils-lib/src/main/java/difflib/DiffUtils.java +++ b/java-diff-utils-lib/src/main/java/difflib/DiffUtils.java @@ -204,25 +204,8 @@ public static Patch parseUnifiedDiff(List diff) { Matcher m = unifiedDiffChunkRe.matcher(line); if (m.find()) { // Process the lines in the previous chunk - if (rawChunk.size() != 0) { - List oldChunkLines = new ArrayList(); - List newChunkLines = new ArrayList(); - - for (String[] raw_line : rawChunk) { - tag = raw_line[0]; - rest = raw_line[1]; - if (tag.equals(" ") || tag.equals("-")) { - oldChunkLines.add(rest); - } - if (tag.equals(" ") || tag.equals("+")) { - newChunkLines.add(rest); - } - } - patch.addDelta(new ChangeDelta(new Chunk( - old_ln - 1, oldChunkLines), new Chunk( - new_ln - 1, newChunkLines))); - rawChunk.clear(); - } + processRawChunk(rawChunk, patch, old_ln, new_ln); + // Parse the @@ header old_ln = m.group(1) == null ? 1 : Integer.parseInt(m.group(1)); new_ln = m.group(3) == null ? 1 : Integer.parseInt(m.group(3)); @@ -247,6 +230,15 @@ public static Patch parseUnifiedDiff(List diff) { } // Process the lines in the last chunk + processRawChunk(rawChunk, patch, old_ln, new_ln); + + return patch; + } + + public static void processRawChunk(List rawChunk, Patch patch, int old_ln, int new_ln) { + String tag; + String rest; + if (rawChunk.size() != 0) { List oldChunkLines = new ArrayList(); List newChunkLines = new ArrayList(); @@ -262,13 +254,18 @@ public static Patch parseUnifiedDiff(List diff) { } } - patch.addDelta(new ChangeDelta(new Chunk( - old_ln - 1, oldChunkLines), new Chunk(new_ln - 1, - newChunkLines))); + if (oldChunkLines.isEmpty()) { + patch.addDelta(new InsertDelta(new Chunk(old_ln, oldChunkLines), + new Chunk(new_ln - 1, newChunkLines))); + } else if (newChunkLines.isEmpty()) { + patch.addDelta(new DeleteDelta(new Chunk(old_ln - 1, oldChunkLines), + new Chunk(new_ln, newChunkLines))); + } else { + patch.addDelta(new ChangeDelta(new Chunk(old_ln - 1, oldChunkLines), + new Chunk(new_ln - 1, newChunkLines))); + } rawChunk.clear(); } - - return patch; } /** @@ -416,6 +413,14 @@ private static List processDeltas(List origLines, revTotal++; } + // In case of empty chunk and context + if (origTotal == 0 && origStart > 1) + --origStart; + + // In case of empty chunk and context + if (revTotal == 0 && revStart > 1) + --revStart; + // Create and insert the block header, conforming to the Unified Diff // standard StringBuffer header = new StringBuffer(); diff --git a/java-diff-utils-lib/src/test/java/diffutils/EmptyContextUnifiedDiffTest.java b/java-diff-utils-lib/src/test/java/diffutils/EmptyContextUnifiedDiffTest.java new file mode 100644 index 0000000..580b4b8 --- /dev/null +++ b/java-diff-utils-lib/src/test/java/diffutils/EmptyContextUnifiedDiffTest.java @@ -0,0 +1,90 @@ +package diffutils; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.util.LinkedList; +import java.util.List; + +import difflib.DiffUtils; +import difflib.Patch; +import difflib.PatchFailedException; + +import junit.framework.TestCase; + +public class EmptyContextUnifiedDiffTest extends TestCase { + + public List fileToLines(String filename) { + List lines = new LinkedList(); + String line = ""; + BufferedReader in = null; + try { + in = new BufferedReader(new FileReader(filename)); + while ((line = in.readLine()) != null) { + lines.add(line); + } + } catch (IOException e) { + e.printStackTrace(); + fail(e.getMessage()); + } finally { + if (in != null) { + try { + in.close(); + } catch (IOException e) { + // ignore ... any errors should already have been + // reported via an IOException from the final flush. + } + } + } + return lines; + } + + public void testEmptyUnifiedContextPatch() { + List origLines = fileToLines(TestConstants.MOCK_FOLDER + "unified_empty_context_original.txt"); + List revLines = fileToLines(TestConstants.MOCK_FOLDER + "unified_empty_context_revised.txt"); + List unifiedDiff = fileToLines(TestConstants.MOCK_FOLDER + "unified_empty_context_patch.txt"); + + List patchedLines = null; + Patch patch = DiffUtils.parseUnifiedDiff(unifiedDiff); + + try { + patchedLines = (List) patch.applyTo(origLines); + } catch (PatchFailedException e) { + fail(e.getMessage()); + } + + verifyLinesEqual(patchedLines, revLines); + } + + public void testEmptyUnifiedContextDiff() { + List origLines = fileToLines(TestConstants.MOCK_FOLDER + "unified_empty_context_original.txt"); + List revLines = fileToLines(TestConstants.MOCK_FOLDER + "unified_empty_context_revised.txt"); + + List patchedLines = null; + + // Generate a 0-context diff then reapply + Patch generatedPatch = DiffUtils.diff(origLines, revLines); + List generatedDiff = DiffUtils.generateUnifiedDiff("original", "revised", origLines, generatedPatch, 0); + Patch newPatch = DiffUtils.parseUnifiedDiff(generatedDiff); + + try { + patchedLines = (List) newPatch.applyTo(origLines); + } catch (PatchFailedException e) { + fail(e.getMessage()); + } + + verifyLinesEqual(patchedLines, revLines); + } + + public void verifyLinesEqual(List patchedLines, List revLines) { + assertTrue(revLines.size() == patchedLines.size()); + for (int i = 0; i < revLines.size(); i++) { + String l1 = revLines.get(i); + String l2 = patchedLines.get(i); + if (!l1.equals(l2)) { + fail("Line " + (i + 1) + " of the patched file did not match the revised original"); + } + } + } + +} diff --git a/java-diff-utils-lib/src/test/resources/mocks/unified_empty_context_original.txt b/java-diff-utils-lib/src/test/resources/mocks/unified_empty_context_original.txt new file mode 100644 index 0000000..24346cc --- /dev/null +++ b/java-diff-utils-lib/src/test/resources/mocks/unified_empty_context_original.txt @@ -0,0 +1,5 @@ +This +is +a +test +file diff --git a/java-diff-utils-lib/src/test/resources/mocks/unified_empty_context_patch.txt b/java-diff-utils-lib/src/test/resources/mocks/unified_empty_context_patch.txt new file mode 100644 index 0000000..1249035 --- /dev/null +++ b/java-diff-utils-lib/src/test/resources/mocks/unified_empty_context_patch.txt @@ -0,0 +1,6 @@ +--- uc_original.txt 2011-06-14 16:21:56.578627000 +0300 ++++ uc_insert_revised.txt 2011-06-14 16:20:37.654820000 +0300 +@@ -2,0 +3 @@ ++not +@@ -5 +5,0 @@ +-file diff --git a/java-diff-utils-lib/src/test/resources/mocks/unified_empty_context_revised.txt b/java-diff-utils-lib/src/test/resources/mocks/unified_empty_context_revised.txt new file mode 100644 index 0000000..ce3ca7b --- /dev/null +++ b/java-diff-utils-lib/src/test/resources/mocks/unified_empty_context_revised.txt @@ -0,0 +1,5 @@ +This +is +not +a +test