From 5c2d98b155984964742d0d3a7d42184ab6fbbbe5 Mon Sep 17 00:00:00 2001
From: Emmanuel Bourg <ebourg@apache.org>
Date: Mon, 1 Feb 2021 14:43:19 +0100
Subject: [PATCH] New upstream version 4.8

---
 .gitignore                                    |   2 +
 CHANGELOG.md                                  |  16 ++-
 README.md                                     |   4 +
 _config.yml                                   |   1 -
 java-diff-utils-jgit/pom.xml                  |   4 +-
 .../algorithm/jgit/HistogramDiffTest.java     |   2 +-
 java-diff-utils/pom.xml                       |  12 +-
 .../com/github/difflib/UnifiedDiffUtils.java  |  21 ++-
 .../java/com/github/difflib/patch/Chunk.java  |  36 ++++-
 .../com/github/difflib/patch/DeltaType.java   |  13 +-
 .../github/difflib/text/DiffRowGenerator.java |  56 +++++---
 .../difflib/unifieddiff/UnifiedDiffFile.java  |  18 +++
 .../unifieddiff/UnifiedDiffReader.java        |  68 ++++++----
 .../unifieddiff/UnifiedDiffWriter.java        |   6 +-
 .../difflib/GenerateUnifiedDiffTest.java      |  43 +++++-
 .../difflib/text/DiffRowGeneratorTest.java    | 123 ++++++++++++++++++
 .../unifieddiff/UnifiedDiffReaderTest.java    |  85 +++++++++++-
 .../unifieddiff/UnifiedDiffWriterTest.java    |   2 +-
 .../github/difflib/text/issue_86_original.txt |   7 +
 .../github/difflib/text/issue_86_revised.txt  |   8 ++
 .../unifieddiff/problem_diff_issue51.diff     |   3 +-
 .../unifieddiff/problem_diff_issue79.diff     |   3 +
 .../unifieddiff/problem_diff_issue84.diff     | 109 ++++++++++++++++
 .../unifieddiff/problem_diff_issue85.diff     |  31 +++++
 .../unifieddiff/problem_diff_issue98.diff     |  55 ++++++++
 .../src/test/resources/logging.properties     |   8 ++
 .../test/resources/mocks/issue89_patch.txt    |  29 +++++
 .../test/resources/mocks/issue89_revised.txt  |  30 +++++
 pom.xml                                       |   4 +-
 29 files changed, 737 insertions(+), 62 deletions(-)
 delete mode 100644 _config.yml
 create mode 100644 java-diff-utils/src/test/resources/com/github/difflib/text/issue_86_original.txt
 create mode 100644 java-diff-utils/src/test/resources/com/github/difflib/text/issue_86_revised.txt
 create mode 100644 java-diff-utils/src/test/resources/com/github/difflib/unifieddiff/problem_diff_issue79.diff
 create mode 100644 java-diff-utils/src/test/resources/com/github/difflib/unifieddiff/problem_diff_issue84.diff
 create mode 100644 java-diff-utils/src/test/resources/com/github/difflib/unifieddiff/problem_diff_issue85.diff
 create mode 100644 java-diff-utils/src/test/resources/com/github/difflib/unifieddiff/problem_diff_issue98.diff
 create mode 100644 java-diff-utils/src/test/resources/logging.properties
 create mode 100644 java-diff-utils/src/test/resources/mocks/issue89_patch.txt
 create mode 100644 java-diff-utils/src/test/resources/mocks/issue89_revised.txt

diff --git a/.gitignore b/.gitignore
index 54a9c8c..634cb5b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,5 @@
 build/
 nbproject/
 target/
+
+*.iml
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8bf3fcb..48b3622 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,7 +5,21 @@ All notable changes to this project will be documented in this file.
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 This project uses a custom versioning scheme (and not [Semantic Versioning](https://semver.org/spec/v2.0.0.html)).
 
-## [Unreleased]
+## [unreleased]
+
+### Changed
+
+## [4.8]
+
+### Changed
+
+* some bugfixes regarding unified diff writer
+* UnifiedDiffReader improved for **deleted file mode** and better timestamp recognition
+* UnifiedDiffReader improved for **new file mode** and better timestamp recognition
+
+## [4.7]
+
+### Changed 
 
 * minor bug fixes
 * optional include equal parts of original and revised data
diff --git a/README.md b/README.md
index 9e5221e..34c09e6 100644
--- a/README.md
+++ b/README.md
@@ -14,6 +14,10 @@ Main reason to build this library was the lack of easy-to-use libraries with all
 
 **This is originally a fork of java-diff-utils from Google Code Archive.**
 
+## API
+
+Javadocs of the actual release version: [JavaDocs java-diff-utils](https://java-diff-utils.github.io/java-diff-utils/4.7/docs/api/)
+
 ## Examples
 
 Look [here](https://github.com/wumpz/java-diff-utils/wiki) to find more helpful informations and examples.
diff --git a/_config.yml b/_config.yml
deleted file mode 100644
index 2f7efbe..0000000
--- a/_config.yml
+++ /dev/null
@@ -1 +0,0 @@
-theme: jekyll-theme-minimal
\ No newline at end of file
diff --git a/java-diff-utils-jgit/pom.xml b/java-diff-utils-jgit/pom.xml
index deeef10..4f312c1 100644
--- a/java-diff-utils-jgit/pom.xml
+++ b/java-diff-utils-jgit/pom.xml
@@ -4,7 +4,7 @@
     <parent>
         <groupId>io.github.java-diff-utils</groupId>
         <artifactId>java-diff-utils-parent</artifactId>
-        <version>4.7</version>
+        <version>4.8</version>
     </parent>
     <artifactId>java-diff-utils-jgit</artifactId>
     <name>java-diff-utils-jgit</name>
@@ -20,7 +20,7 @@
         <dependency>
             <groupId>org.eclipse.jgit</groupId>
             <artifactId>org.eclipse.jgit</artifactId>
-            <version>4.4.1.201607150455-r</version>
+            <version>5.8.1.202007141445-r</version>
             <exclusions>
                 <exclusion>
                     <groupId>com.googlecode.javaewah</groupId>
diff --git a/java-diff-utils-jgit/src/test/java/com/github/difflib/algorithm/jgit/HistogramDiffTest.java b/java-diff-utils-jgit/src/test/java/com/github/difflib/algorithm/jgit/HistogramDiffTest.java
index d4487c2..4a06ef9 100644
--- a/java-diff-utils-jgit/src/test/java/com/github/difflib/algorithm/jgit/HistogramDiffTest.java
+++ b/java-diff-utils-jgit/src/test/java/com/github/difflib/algorithm/jgit/HistogramDiffTest.java
@@ -82,6 +82,6 @@ public class HistogramDiffTest {
         assertEquals(revList, patched);
         
         System.out.println(logdata);
-        assertEquals(17, logdata.size());
+        assertEquals(19, logdata.size());
     }
 }
diff --git a/java-diff-utils/pom.xml b/java-diff-utils/pom.xml
index 5cbff43..4343765 100644
--- a/java-diff-utils/pom.xml
+++ b/java-diff-utils/pom.xml
@@ -7,7 +7,7 @@
     <parent>
         <groupId>io.github.java-diff-utils</groupId>
         <artifactId>java-diff-utils-parent</artifactId>
-        <version>4.7</version>
+        <version>4.8</version>
     </parent>
     <properties>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
@@ -51,6 +51,16 @@
                     </archive>
                 </configuration>
             </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <trimStackTrace>false</trimStackTrace>
+                    <systemPropertyVariables>
+                        <java.util.logging.config.file>target/test-classes/logging.properties</java.util.logging.config.file>
+                    </systemPropertyVariables>
+                </configuration>
+            </plugin>
         </plugins>
     </build>
 </project>
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 9d7908b..2098aac 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
@@ -33,6 +33,7 @@ public final class UnifiedDiffUtils {
 
     private static final Pattern UNIFIED_DIFF_CHUNK_REGEXP = Pattern
             .compile("^@@\\s+-(?:(\\d+)(?:,(\\d+))?)\\s+\\+(?:(\\d+)(?:,(\\d+))?)\\s+@@$");
+    private static final String NULL_FILE_INDICATOR = "/dev/null";
 
     /**
      * Parse the given text in unified format and creates the list of deltas for it.
@@ -97,19 +98,31 @@ public final class UnifiedDiffUtils {
             List<String> oldChunkLines = new ArrayList<>();
             List<String> newChunkLines = new ArrayList<>();
 
+            List<Integer> removePosition = new ArrayList<>();
+            List<Integer> addPosition = new ArrayList<>();
+            int removeNum = 0;
+            int addNum = 0;
             for (String[] raw_line : rawChunk) {
                 tag = raw_line[0];
                 rest = raw_line[1];
                 if (" ".equals(tag) || "-".equals(tag)) {
+                    removeNum++;
                     oldChunkLines.add(rest);
+                    if ("-".equals(tag)) {
+                        removePosition.add(old_ln - 1 + removeNum);
+                    }
                 }
                 if (" ".equals(tag) || "+".equals(tag)) {
+                    addNum++;
                     newChunkLines.add(rest);
+                    if ("+".equals(tag)) {
+                        addPosition.add(new_ln - 1 + addNum);
+                    }
                 }
             }
             patch.addDelta(new ChangeDelta<>(new Chunk<>(
-                    old_ln - 1, oldChunkLines), new Chunk<>(
-                    new_ln - 1, newChunkLines)));
+                    old_ln - 1, oldChunkLines, removePosition), new Chunk<>(
+                    new_ln - 1, newChunkLines, addPosition)));
             rawChunk.clear();
         }
     }
@@ -131,8 +144,8 @@ public final class UnifiedDiffUtils {
             int contextSize) {
         if (!patch.getDeltas().isEmpty()) {
             List<String> ret = new ArrayList<>();
-            ret.add("--- " + Optional.ofNullable(originalFileName).orElse(""));
-            ret.add("+++ " + Optional.ofNullable(revisedFileName).orElse(""));
+            ret.add("--- " + Optional.ofNullable(originalFileName).orElse(NULL_FILE_INDICATOR));
+            ret.add("+++ " + Optional.ofNullable(revisedFileName).orElse(NULL_FILE_INDICATOR));
 
             List<AbstractDelta<String>> patchDeltas = new ArrayList<>(
                     patch.getDeltas());
diff --git a/java-diff-utils/src/main/java/com/github/difflib/patch/Chunk.java b/java-diff-utils/src/main/java/com/github/difflib/patch/Chunk.java
index 70b5722..87af9a2 100644
--- a/java-diff-utils/src/main/java/com/github/difflib/patch/Chunk.java
+++ b/java-diff-utils/src/main/java/com/github/difflib/patch/Chunk.java
@@ -37,16 +37,19 @@ public final class Chunk<T> {
 
     private final int position;
     private List<T> lines;
+    private final List<Integer> changePosition;
 
     /**
      * Creates a chunk and saves a copy of affected lines
      *
      * @param position the start position
      * @param lines the affected lines
+     * @param changePosition the positions of changed lines
      */
-    public Chunk(int position, List<T> lines) {
+    public Chunk(int position, List<T> lines, List<Integer> changePosition) {
         this.position = position;
         this.lines = new ArrayList<>(lines);
+        this.changePosition = changePosition;
     }
 
     /**
@@ -55,9 +58,31 @@ public final class Chunk<T> {
      * @param position the start position
      * @param lines the affected lines
      */
-    public Chunk(int position, T[] lines) {
+    public Chunk(int position, List<T> lines) {
+        this(position, lines, null);
+    }
+
+    /**
+     * Creates a chunk and saves a copy of affected lines
+     *
+     * @param position the start position
+     * @param lines the affected lines
+     * @param changePosition the positions of changed lines
+     */
+    public Chunk(int position, T[] lines, List<Integer> changePosition) {
         this.position = position;
         this.lines = Arrays.asList(lines);
+        this.changePosition = changePosition;
+    }
+
+    /**
+     * Creates a chunk and saves a copy of affected lines
+     *
+     * @param position the start position
+     * @param lines the affected lines
+     */
+    public Chunk(int position, T[] lines) {
+        this(position, lines, null);
     }
 
     /**
@@ -96,6 +121,13 @@ public final class Chunk<T> {
         return lines;
     }
 
+    /**
+     * @return the positions of changed lines of chunk in the text
+     */
+    public List<Integer> getChangePosition() {
+        return changePosition;
+    }
+
     public int size() {
         return lines.size();
     }
diff --git a/java-diff-utils/src/main/java/com/github/difflib/patch/DeltaType.java b/java-diff-utils/src/main/java/com/github/difflib/patch/DeltaType.java
index 3c12196..666e803 100644
--- a/java-diff-utils/src/main/java/com/github/difflib/patch/DeltaType.java
+++ b/java-diff-utils/src/main/java/com/github/difflib/patch/DeltaType.java
@@ -16,7 +16,18 @@
 package com.github.difflib.patch;
 
 /**
- * Specifies the type of the delta.
+ * Specifies the type of the delta. There are three types of modifications from  
+ * the original to get the revised text. 
+ * 
+ * CHANGE: a block of data of the original is replaced by another block of data.
+ * DELETE: a block of data of the original is removed
+ * INSERT: at a position of the original a block of data is inserted
+ * 
+ * to be complete there is also 
+ * 
+ * EQUAL: a block of data of original and the revised text is equal
+ * 
+ * which is no change at all.
  *
  */
 public enum DeltaType {
diff --git a/java-diff-utils/src/main/java/com/github/difflib/text/DiffRowGenerator.java b/java-diff-utils/src/main/java/com/github/difflib/text/DiffRowGenerator.java
index 350bef5..c315c45 100644
--- a/java-diff-utils/src/main/java/com/github/difflib/text/DiffRowGenerator.java
+++ b/java-diff-utils/src/main/java/com/github/difflib/text/DiffRowGenerator.java
@@ -61,6 +61,7 @@ public final class DiffRowGenerator {
         }
         return list;
     };
+
     public static final Pattern SPLIT_BY_WORD_PATTERN = Pattern.compile("\\s+|[,.\\[\\](){}/\\\\*+\\-#]");
 
     /**
@@ -106,7 +107,7 @@ public final class DiffRowGenerator {
      */
     static void wrapInTag(List<String> sequence, int startPosition,
             int endPosition, Tag tag, BiFunction<Tag, Boolean, String> tagGenerator,
-            Function<String, String> processDiffs) {
+            Function<String, String> processDiffs, boolean replaceLinefeedWithSpace) {
         int endPos = endPosition;
 
         while (endPos >= startPosition) {
@@ -115,6 +116,9 @@ public final class DiffRowGenerator {
             while (endPos > startPosition) {
                 if (!"\n".equals(sequence.get(endPos - 1))) {
                     break;
+                } else if (replaceLinefeedWithSpace) {
+                    sequence.set(endPos - 1, " ");
+                    break;
                 }
                 endPos--;
             }
@@ -133,7 +137,11 @@ public final class DiffRowGenerator {
             //search position for end tag
             while (endPos > startPosition) {
                 if ("\n".equals(sequence.get(endPos - 1))) {
-                    break;
+                    if (replaceLinefeedWithSpace) {
+                        sequence.set(endPos - 1, " ");
+                    } else {
+                        break;
+                    }
                 }
                 if (processDiffs != null) {
                     sequence.set(endPos - 1,
@@ -159,6 +167,7 @@ public final class DiffRowGenerator {
     private final Function<String, String> processDiffs;
 
     private final boolean showInlineDiffs;
+    private final boolean replaceOriginalLinefeedInChangesWithSpaces;
 
     private DiffRowGenerator(Builder builder) {
         showInlineDiffs = builder.showInlineDiffs;
@@ -178,6 +187,8 @@ public final class DiffRowGenerator {
         reportLinesUnchanged = builder.reportLinesUnchanged;
         lineNormalizer = builder.lineNormalizer;
         processDiffs = builder.processDiffs;
+        
+        replaceOriginalLinefeedInChangesWithSpaces = builder.replaceOriginalLinefeedInChangesWithSpaces;
 
         Objects.requireNonNull(inlineDiffSplitter);
         Objects.requireNonNull(lineNormalizer);
@@ -313,7 +324,7 @@ public final class DiffRowGenerator {
             if (inlineDelta.getType() == DeltaType.DELETE) {
                 wrapInTag(origList, inlineOrig.getPosition(), inlineOrig
                         .getPosition()
-                        + inlineOrig.size(), Tag.DELETE, oldTag, processDiffs);
+                        + inlineOrig.size(), Tag.DELETE, oldTag, processDiffs, replaceOriginalLinefeedInChangesWithSpaces && mergeOriginalRevised);
             } else if (inlineDelta.getType() == DeltaType.INSERT) {
                 if (mergeOriginalRevised) {
                     origList.addAll(inlineOrig.getPosition(),
@@ -321,11 +332,11 @@ public final class DiffRowGenerator {
                                     inlineRev.getPosition() + inlineRev.size()));
                     wrapInTag(origList, inlineOrig.getPosition(),
                             inlineOrig.getPosition() + inlineRev.size(),
-                            Tag.INSERT, newTag, processDiffs);
+                            Tag.INSERT, newTag, processDiffs, false);
                 } else {
                     wrapInTag(revList, inlineRev.getPosition(),
                             inlineRev.getPosition() + inlineRev.size(),
-                            Tag.INSERT, newTag, processDiffs);
+                            Tag.INSERT, newTag, processDiffs, false);
                 }
             } else if (inlineDelta.getType() == DeltaType.CHANGE) {
                 if (mergeOriginalRevised) {
@@ -334,15 +345,15 @@ public final class DiffRowGenerator {
                                     inlineRev.getPosition() + inlineRev.size()));
                     wrapInTag(origList, inlineOrig.getPosition() + inlineOrig.size(),
                             inlineOrig.getPosition() + inlineOrig.size() + inlineRev.size(),
-                            Tag.CHANGE, newTag, processDiffs);
+                            Tag.CHANGE, newTag, processDiffs, false);
                 } else {
                     wrapInTag(revList, inlineRev.getPosition(),
                             inlineRev.getPosition() + inlineRev.size(),
-                            Tag.CHANGE, newTag, processDiffs);
+                            Tag.CHANGE, newTag, processDiffs, false);
                 }
                 wrapInTag(origList, inlineOrig.getPosition(),
                         inlineOrig.getPosition() + inlineOrig.size(),
-                        Tag.CHANGE, oldTag, processDiffs);
+                        Tag.CHANGE, oldTag, processDiffs, replaceOriginalLinefeedInChangesWithSpaces && mergeOriginalRevised);
             }
         }
         StringBuilder origResult = new StringBuilder();
@@ -385,10 +396,10 @@ public final class DiffRowGenerator {
         private boolean showInlineDiffs = false;
         private boolean ignoreWhiteSpaces = false;
 
-        private BiFunction<Tag, Boolean, String> oldTag = 
-                (tag, f) -> f ? "<span class=\"editOldInline\">" : "</span>";
-        private BiFunction<Tag, Boolean, String> newTag = 
-                (tag, f) -> f ? "<span class=\"editNewInline\">" : "</span>";
+        private BiFunction<Tag, Boolean, String> oldTag
+                = (tag, f) -> f ? "<span class=\"editOldInline\">" : "</span>";
+        private BiFunction<Tag, Boolean, String> newTag
+                = (tag, f) -> f ? "<span class=\"editNewInline\">" : "</span>";
 
         private int columnWidth = 0;
         private boolean mergeOriginalRevised = false;
@@ -397,6 +408,7 @@ public final class DiffRowGenerator {
         private Function<String, String> lineNormalizer = LINE_NORMALIZER_FOR_HTML;
         private Function<String, String> processDiffs = null;
         private BiPredicate<String, String> equalizer = null;
+        private boolean replaceOriginalLinefeedInChangesWithSpaces = false;
 
         private Builder() {
         }
@@ -445,7 +457,7 @@ public final class DiffRowGenerator {
             this.oldTag = generator;
             return this;
         }
-        
+
         /**
          * Generator for Old-Text-Tags.
          *
@@ -467,7 +479,7 @@ public final class DiffRowGenerator {
             this.newTag = generator;
             return this;
         }
-        
+
         /**
          * Generator for New-Text-Tags.
          *
@@ -494,8 +506,8 @@ 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 have any sense. Default 80. @return
-         * builder with config ured ignoreBlankLines parameter
+         * @param width the width to set. Making it < 0 doesn't make any sense. Default 80. 
+         * @return builder with config of column width
          */
         public Builder columnWidth(int width) {
             if (width >= 0) {
@@ -575,5 +587,17 @@ public final class DiffRowGenerator {
             this.equalizer = equalizer;
             return this;
         }
+        
+        /**
+         * Sometimes it happens that a change contains multiple lines. If there is no correspondence
+         * in old and new. To keep the merged line more readable the linefeeds could be replaced
+         * by spaces.
+         * @param replace
+         * @return 
+         */
+        public Builder replaceOriginalLinefeedInChangesWithSpaces(boolean replace) {
+            this.replaceOriginalLinefeedInChangesWithSpaces = replace;
+            return this;
+        }
     }
 }
diff --git a/java-diff-utils/src/main/java/com/github/difflib/unifieddiff/UnifiedDiffFile.java b/java-diff-utils/src/main/java/com/github/difflib/unifieddiff/UnifiedDiffFile.java
index c244184..bc1ef2f 100644
--- a/java-diff-utils/src/main/java/com/github/difflib/unifieddiff/UnifiedDiffFile.java
+++ b/java-diff-utils/src/main/java/com/github/difflib/unifieddiff/UnifiedDiffFile.java
@@ -29,6 +29,8 @@ public final class UnifiedDiffFile {
     private String toFile;
     private String toTimestamp;
     private String index;
+    private String newFileMode;
+    private String deletedFileMode;
     private Patch<String> patch = new Patch<>();
 
     public String getDiffCommand() {
@@ -92,4 +94,20 @@ public final class UnifiedDiffFile {
         file.patch = patch;
         return file;
     }
+
+    public void setNewFileMode(String newFileMode) {
+        this.newFileMode = newFileMode;
+    }
+    
+    public String getNewFileMode() {
+        return newFileMode;
+    }
+
+    public String getDeletedFileMode() {
+        return deletedFileMode;
+    }
+
+    public void setDeletedFileMode(String deletedFileMode) {
+        this.deletedFileMode = deletedFileMode;
+    }
 }
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 75dff38..e959693 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
@@ -39,7 +39,7 @@ import java.util.regex.Pattern;
 public final class UnifiedDiffReader {
 
     static final Pattern UNIFIED_DIFF_CHUNK_REGEXP = Pattern.compile("^@@\\s+-(?:(\\d+)(?:,(\\d+))?)\\s+\\+(?:(\\d+)(?:,(\\d+))?)\\s+@@");
-    static final Pattern TIMESTAMP_REGEXP = Pattern.compile("(\\d{4}-\\d{2}-\\d{2}[T ]\\d{2}:\\d{2}:\\d{2}\\.\\d{3,})");
+    static final Pattern TIMESTAMP_REGEXP = Pattern.compile("(\\d{4}-\\d{2}-\\d{2}[T ]\\d{2}:\\d{2}:\\d{2}\\.\\d{3,})(?: [+-]\\d+)?");
 
     private final InternalUnifiedDiffReader READER;
     private final UnifiedDiff data = new UnifiedDiff();
@@ -49,6 +49,10 @@ public final class UnifiedDiffReader {
     private final UnifiedDiffLine FROM_FILE = new UnifiedDiffLine(true, "^---\\s", this::processFromFile);
     private final UnifiedDiffLine TO_FILE = new UnifiedDiffLine(true, "^\\+\\+\\+\\s", this::processToFile);
 
+    private final UnifiedDiffLine NEW_FILE_MODE = new UnifiedDiffLine(true, "^new\\sfile\\smode\\s(\\d+)", this::processNewFileMode);
+    
+    private final UnifiedDiffLine DELETED_FILE_MODE = new UnifiedDiffLine(true, "^deleted\\sfile\\smode\\s(\\d+)", this::processDeletedFileMode);
+
     private final UnifiedDiffLine CHUNK = new UnifiedDiffLine(false, UNIFIED_DIFF_CHUNK_REGEXP, this::processChunk);
     private final UnifiedDiffLine LINE_NORMAL = new UnifiedDiffLine("^\\s", this::processNormalLine);
     private final UnifiedDiffLine LINE_DEL = new UnifiedDiffLine("^-", this::processDelLine);
@@ -66,13 +70,14 @@ public final class UnifiedDiffReader {
     // [/^-/, del], [/^\+/, add], [/^\\ No newline at end of file$/, eof]];
     private UnifiedDiff parse() throws IOException, UnifiedDiffParserException {
         String headerTxt = "";
-        LOG.log(Level.INFO, "header parsing");
+        LOG.log(Level.FINE, "header parsing");
         String line = null;
         while (READER.ready()) {
             line = READER.readLine();
-            LOG.log(Level.INFO, "parsing line {0}", line);
+            LOG.log(Level.FINE, "parsing line {0}", line);
             if (DIFF_COMMAND.validLine(line) || INDEX.validLine(line)
-                    || FROM_FILE.validLine(line) || TO_FILE.validLine(line)) {
+                    || FROM_FILE.validLine(line) || TO_FILE.validLine(line)
+                    || NEW_FILE_MODE.validLine(line)) {
                 break;
             } else {
                 headerTxt += line + "\n";
@@ -85,26 +90,28 @@ public final class UnifiedDiffReader {
         while (line != null) {
             if (!CHUNK.validLine(line)) {
                 initFileIfNecessary();
-                while (!CHUNK.validLine(line)) {
-                    if (processLine(line, DIFF_COMMAND, INDEX, FROM_FILE, TO_FILE) == false) {
+                while (line != null && !CHUNK.validLine(line)) {
+                    if (processLine(line, DIFF_COMMAND, INDEX, FROM_FILE, TO_FILE, NEW_FILE_MODE, DELETED_FILE_MODE) == false) {
                         throw new UnifiedDiffParserException("expected file start line not found");
                     }
                     line = READER.readLine();
                 }
             }
-            processLine(line, CHUNK);
-            while ((line = READER.readLine()) != null) {
-                if (processLine(line, LINE_NORMAL, LINE_ADD, LINE_DEL) == false) {
-                    throw new UnifiedDiffParserException("expected data line not found");
-                }
-                if ((originalTxt.size() == old_size && revisedTxt.size() == new_size) 
-                        || (old_size==0 && new_size==0 && originalTxt.size() == this.old_ln 
+            if (line != null) {
+                processLine(line, CHUNK);
+                while ((line = READER.readLine()) != null) {
+                    if (processLine(line, LINE_NORMAL, LINE_ADD, LINE_DEL) == false) {
+                        throw new UnifiedDiffParserException("expected data line not found");
+                    }
+                    if ((originalTxt.size() == old_size && revisedTxt.size() == new_size)
+                            || (old_size == 0 && new_size == 0 && originalTxt.size() == this.old_ln
                             && revisedTxt.size() == this.new_ln)) {
-                    finalizeChunk();
-                    break;
+                        finalizeChunk();
+                        break;
+                    }
                 }
+                line = READER.readLine();
             }
-            line = READER.readLine();
             if (line == null || line.startsWith("--")) {
                 break;
             }
@@ -113,7 +120,10 @@ public final class UnifiedDiffReader {
         if (READER.ready()) {
             String tailTxt = "";
             while (READER.ready()) {
-                tailTxt += READER.readLine() + "\n";
+                if (tailTxt.length() > 0) {
+                    tailTxt += "\n";
+                }
+                tailTxt += READER.readLine();
             }
             data.setTailTxt(tailTxt);
         }
@@ -137,13 +147,16 @@ public final class UnifiedDiffReader {
     }
 
     private boolean processLine(String line, UnifiedDiffLine... rules) throws UnifiedDiffParserException {
+        if (line == null) {
+            return false;
+        }
         for (UnifiedDiffLine rule : rules) {
             if (rule.processLine(line)) {
-                LOG.info("  >>> processed rule " + rule.toString());
+                LOG.fine("  >>> processed rule " + rule.toString());
                 return true;
             }
         }
-        LOG.info("  >>> no rule matched " + line);
+        LOG.warning("  >>> no rule matched " + line);
         return false;
         //throw new UnifiedDiffParserException("parsing error at line " + line);
     }
@@ -161,7 +174,7 @@ public final class UnifiedDiffReader {
 
     private void processDiff(MatchResult match, String line) {
         //initFileIfNecessary();
-        LOG.log(Level.INFO, "start {0}", line);
+        LOG.log(Level.FINE, "start {0}", line);
         String[] fromTo = parseFileNames(READER.lastLine());
         actualFile.setFromFile(fromTo[0]);
         actualFile.setToFile(fromTo[1]);
@@ -223,7 +236,7 @@ public final class UnifiedDiffReader {
 
     private void processIndex(MatchResult match, String line) {
         //initFileIfNecessary();
-        LOG.log(Level.INFO, "index {0}", line);
+        LOG.log(Level.FINE, "index {0}", line);
         actualFile.setIndex(line.substring(6));
     }
 
@@ -239,14 +252,25 @@ public final class UnifiedDiffReader {
         actualFile.setToTimestamp(extractTimestamp(line));
     }
 
+    private void processNewFileMode(MatchResult match, String line) {
+        //initFileIfNecessary();
+        actualFile.setNewFileMode(match.group(1));
+    }
+    
+    private void processDeletedFileMode(MatchResult match, String line) {
+        //initFileIfNecessary();
+        actualFile.setDeletedFileMode(match.group(1));
+    }
+
     private String extractFileName(String _line) {
         Matcher matcher = TIMESTAMP_REGEXP.matcher(_line);
         String line = _line;
         if (matcher.find()) {
             line = line.substring(0, matcher.start());
         }
+        line = line.split("\t")[0];
         return line.substring(4).replaceFirst("^(a|b|old|new)(\\/)?", "")
-                .replace(TIMESTAMP_REGEXP.toString(), "").trim();
+                .trim();
     }
 
     private String extractTimestamp(String line) {
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 54259f7..320e685 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
@@ -59,7 +59,7 @@ public class UnifiedDiffWriter {
                     writer.accept("index " + file.getIndex());
                 }
 
-                writer.accept("--- " + file.getFromFile());
+                writer.accept("--- " + (file.getFromFile() == null ? "/dev/null" : file.getFromFile()));
 
                 if (file.getToFile() != null) {
                     writer.accept("+++ " + file.getToFile());
@@ -96,7 +96,7 @@ public class UnifiedDiffWriter {
 
                 }
                 // don't forget to process the last set of Deltas
-                processDeltas(writer, originalLines, deltas, contextSize, 
+                processDeltas(writer, originalLines, deltas, contextSize,
                         patchDeltas.size() == 1 && file.getFromFile() == null);
             }
 
@@ -156,7 +156,7 @@ public class UnifiedDiffWriter {
             AbstractDelta<String> nextDelta = deltas.get(deltaIndex);
             int intermediateStart = curDelta.getSource().getPosition()
                     + curDelta.getSource().getLines().size();
-            for (line = intermediateStart; line < nextDelta.getSource().getPosition() 
+            for (line = intermediateStart; line < nextDelta.getSource().getPosition()
                     && line < origLines.size(); line++) {
                 // output the code between the last Delta and this one
                 buffer.add(" " + origLines.get(line));
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 8c023c9..8febc30 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
@@ -1,5 +1,6 @@
 package com.github.difflib;
 
+import com.github.difflib.patch.Chunk;
 import com.github.difflib.patch.Patch;
 import com.github.difflib.patch.PatchFailedException;
 import java.io.BufferedReader;
@@ -8,9 +9,11 @@ import java.io.FileReader;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 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.assertTrue;
 import static org.junit.jupiter.api.Assertions.fail;
 import org.junit.jupiter.api.Test;
 
@@ -119,13 +122,51 @@ public class GenerateUnifiedDiffTest {
         List<String> udiff = UnifiedDiffUtils.generateUnifiedDiff(null, "revised",
                 original, patch, 10);
         
-        assertEquals("--- ", udiff.get(0));
+        assertEquals("--- /dev/null", udiff.get(0));
         assertEquals("+++ revised", udiff.get(1));
         assertEquals("@@ -0,0 +1,2 @@", udiff.get(2));
         
         UnifiedDiffUtils.parseUnifiedDiff(udiff);
     }
 
+    /**
+     * Issue 89
+     */
+    @Test
+    public void testChagngePosition() throws IOException {
+        final List<String> patchLines = fileToLines(TestConstants.MOCK_FOLDER + "issue89_patch.txt");
+        final Patch<String> patch = UnifiedDiffUtils.parseUnifiedDiff(patchLines);
+        List<Integer> realRemoveListOne = Collections.singletonList(3);
+        List<Integer> realAddListOne = Arrays.asList(3, 7, 8, 9, 10, 11, 12, 13, 14);
+        validateChangePosition(patch, 0, realRemoveListOne, realAddListOne);
+        List<Integer> realRemoveListTwo = new ArrayList<>();
+        List<Integer> realAddListTwo = Arrays.asList(27, 28);
+        validateChangePosition(patch, 1, realRemoveListTwo, realAddListTwo);
+
+    }
+
+    private void validateChangePosition(Patch<String> patch, int index, List<Integer> realRemoveList,
+                                        List<Integer> realAddList ) {
+        final Chunk originChunk = patch.getDeltas().get(index).getSource();
+        List<Integer> removeList = originChunk.getChangePosition();
+        assertEquals(realRemoveList.size(), removeList.size());
+        for (Integer ele: realRemoveList) {
+            assertTrue(realRemoveList.contains(ele));
+        }
+        for (Integer ele: removeList) {
+            assertTrue(realAddList.contains(ele));
+        }
+        final Chunk targetChunk = patch.getDeltas().get(index).getTarget();
+        List<Integer> addList = targetChunk.getChangePosition();
+        assertEquals(realAddList.size(), addList.size());
+        for (Integer ele: realAddList) {
+            assertTrue(addList.contains(ele));
+        }
+        for (Integer ele: addList) {
+            assertTrue(realAddList.contains(ele));
+        }
+    }
+
     private void verify(List<String> origLines, List<String> revLines,
             String originalFile, String revisedFile) {
         Patch<String> patch = DiffUtils.diff(origLines, revLines);
diff --git a/java-diff-utils/src/test/java/com/github/difflib/text/DiffRowGeneratorTest.java b/java-diff-utils/src/test/java/com/github/difflib/text/DiffRowGeneratorTest.java
index a1450f3..63c8e45 100644
--- a/java-diff-utils/src/test/java/com/github/difflib/text/DiffRowGeneratorTest.java
+++ b/java-diff-utils/src/test/java/com/github/difflib/text/DiffRowGeneratorTest.java
@@ -3,10 +3,12 @@ package com.github.difflib.text;
 import java.io.File;
 import java.io.IOException;
 import java.nio.file.Files;
+import java.nio.file.Paths;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 import java.util.regex.Pattern;
+import static java.util.stream.Collectors.joining;
 import static java.util.stream.Collectors.toList;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -498,4 +500,125 @@ public class DiffRowGeneratorTest {
 
         assertEquals("This~//~**/**is~//~**/**a~//~**/**test~.~", rows.get(0).getOldLine());
     }
+
+    @Test
+    public void testProblemTooManyDiffRowsIssue65() {
+        DiffRowGenerator generator = DiffRowGenerator.create()
+                .showInlineDiffs(true)
+                .reportLinesUnchanged(true)
+                .oldTag(f -> "~")
+                .newTag(f -> "**")
+                .mergeOriginalRevised(true)
+                .inlineDiffByWord(false)
+                .replaceOriginalLinefeedInChangesWithSpaces(true)
+                .build();
+
+        List<DiffRow> diffRows = generator.generateDiffRows(
+                Arrays.asList("Ich möchte nicht mit einem Bot sprechen.", "Ich soll das schon wieder wiederholen?"),
+                Arrays.asList("Ich möchte nicht mehr mit dir sprechen. Leite mich weiter.", "Kannst du mich zum Kundendienst weiterleiten?"));
+
+        print(diffRows);
+
+        assertThat(diffRows).hasSize(2);
+    }
+
+    @Test
+    public void testProblemTooManyDiffRowsIssue65_NoMerge() {
+        DiffRowGenerator generator = DiffRowGenerator.create()
+                .showInlineDiffs(true)
+                .reportLinesUnchanged(true)
+                .oldTag(f -> "~")
+                .newTag(f -> "**")
+                .mergeOriginalRevised(false)
+                .inlineDiffByWord(false)
+                .build();
+
+        List<DiffRow> diffRows = generator.generateDiffRows(
+                Arrays.asList("Ich möchte nicht mit einem Bot sprechen.", "Ich soll das schon wieder wiederholen?"),
+                Arrays.asList("Ich möchte nicht mehr mit dir sprechen. Leite mich weiter.", "Kannst du mich zum Kundendienst weiterleiten?"));
+
+        System.out.println(diffRows);
+
+        assertThat(diffRows).hasSize(2);
+    }
+
+    @Test
+    public void testProblemTooManyDiffRowsIssue65_DiffByWord() {
+        DiffRowGenerator generator = DiffRowGenerator.create()
+                .showInlineDiffs(true)
+                .reportLinesUnchanged(true)
+                .oldTag(f -> "~")
+                .newTag(f -> "**")
+                .mergeOriginalRevised(true)
+                .inlineDiffByWord(true)
+                .build();
+
+        List<DiffRow> diffRows = generator.generateDiffRows(
+                Arrays.asList("Ich möchte nicht mit einem Bot sprechen.", "Ich soll das schon wieder wiederholen?"),
+                Arrays.asList("Ich möchte nicht mehr mit dir sprechen. Leite mich weiter.", "Kannst du mich zum Kundendienst weiterleiten?"));
+
+        System.out.println(diffRows);
+
+        assertThat(diffRows).hasSize(2);
+    }
+
+    @Test
+    public void testProblemTooManyDiffRowsIssue65_NoInlineDiff() {
+        DiffRowGenerator generator = DiffRowGenerator.create()
+                .showInlineDiffs(false)
+                .reportLinesUnchanged(true)
+                .oldTag(f -> "~")
+                .newTag(f -> "**")
+                .mergeOriginalRevised(true)
+                .inlineDiffByWord(false)
+                .build();
+
+        List<DiffRow> diffRows = generator.generateDiffRows(
+                Arrays.asList("Ich möchte nicht mit einem Bot sprechen.", "Ich soll das schon wieder wiederholen?"),
+                Arrays.asList("Ich möchte nicht mehr mit dir sprechen. Leite mich weiter.", "Kannst du mich zum Kundendienst weiterleiten?"));
+
+        System.out.println(diffRows);
+
+        assertThat(diffRows).hasSize(2);
+    }
+
+    @Test
+    public void testLinefeedInStandardTagsWithLineWidthIssue81() {
+        List<String> original = Arrays.asList(("American bobtail jaguar. American bobtail bombay but turkish angora and tomcat.\n"
+                + "Russian blue leopard. Lion. Tabby scottish fold for russian blue, so savannah yet lynx. Tomcat singapura, cheetah.\n"
+                + "Bengal tiger panther but singapura but bombay munchkin for cougar.").split("\n"));
+        List<String> revised = Arrays.asList(("bobtail jaguar. American bobtail turkish angora and tomcat.\n"
+                + "Russian blue leopard. Lion. Tabby scottish folded for russian blue, so savannah yettie? lynx. Tomcat singapura, cheetah.\n"
+                + "Bengal tiger panther but singapura but bombay munchkin for cougar. And more.").split("\n"));
+
+        DiffRowGenerator generator = DiffRowGenerator.create()
+                .showInlineDiffs(true)
+                .ignoreWhiteSpaces(true)
+                .columnWidth(100)
+                .build();
+        List<DiffRow> deltas = generator.generateDiffRows(original, revised);
+
+        System.out.println(deltas);
+    }
+    
+    @Test
+    public void testIssue86WrongInlineDiff() throws IOException {      
+        String original = Files.lines(Paths.get("target/test-classes/com/github/difflib/text/issue_86_original.txt")).collect(joining("\n"));
+        String revised = Files.lines(Paths.get("target/test-classes/com/github/difflib/text/issue_86_revised.txt")).collect(joining("\n"));
+        
+        DiffRowGenerator generator = DiffRowGenerator.create()
+            .showInlineDiffs(true)
+            .mergeOriginalRevised(false)
+            .inlineDiffByWord(true)
+            .oldTag( f -> "~" )
+            .newTag( f -> "**" )
+            .build();
+        List<DiffRow> rows = generator.generateDiffRows(
+                Arrays.asList(original.split("\n")),
+                Arrays.asList(revised.split("\n")));
+        
+        for (DiffRow diff : rows) {
+            System.out.println(diff);
+        }
+    }
 }
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 a7c6753..ed792c8 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,6 +21,7 @@ import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 import org.junit.jupiter.api.Test;
 
@@ -42,7 +43,7 @@ public class UnifiedDiffReaderTest {
         assertThat(file1.getFromFile()).isEqualTo("src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt");
         assertThat(file1.getPatch().getDeltas().size()).isEqualTo(3);
 
-        assertThat(diff.getTail()).isEqualTo("2.17.1.windows.2\n\n");
+        assertThat(diff.getTail()).isEqualTo("2.17.1.windows.2\n");
     }
 
     @Test
@@ -100,7 +101,7 @@ public class UnifiedDiffReaderTest {
         assertThat(first.getSource().size()).isGreaterThan(0);
         assertThat(first.getTarget().size()).isGreaterThan(0);
 
-        assertThat(diff.getTail()).isEqualTo("2.17.1.windows.2\n\n");
+        assertThat(diff.getTail()).isEqualTo("2.17.1.windows.2\n");
     }
 
     @Test
@@ -141,7 +142,7 @@ public class UnifiedDiffReaderTest {
         assertThat(diff.getTail()).isNull();
         assertThat(diff.getHeader()).isNull();
     }
-    
+
     @Test
     public void testParseIssue51() throws IOException {
         UnifiedDiff diff = UnifiedDiffReader.parseUnifiedDiff(
@@ -154,7 +155,85 @@ public class UnifiedDiffReaderTest {
         UnifiedDiffFile file1 = diff.getFiles().get(0);
         assertThat(file1.getFromFile()).isEqualTo("f1");
         assertThat(file1.getPatch().getDeltas().size()).isEqualTo(1);
+        
+        UnifiedDiffFile file2 = diff.getFiles().get(1);
+        assertThat(file2.getFromFile()).isEqualTo("f2");
+        assertThat(file2.getPatch().getDeltas().size()).isEqualTo(1);
+
+        assertThat(diff.getTail()).isNull();
+    }
+
+    @Test
+    public void testParseIssue79() throws IOException {
+        UnifiedDiff diff = UnifiedDiffReader.parseUnifiedDiff(
+                UnifiedDiffReaderTest.class.getResourceAsStream("problem_diff_issue79.diff"));
+
+        assertThat(diff.getFiles().size()).isEqualTo(1);
+
+        UnifiedDiffFile file1 = diff.getFiles().get(0);
+        assertThat(file1.getFromFile()).isEqualTo("test/Issue.java");
+        assertThat(file1.getPatch().getDeltas().size()).isEqualTo(0);
 
         assertThat(diff.getTail()).isNull();
+        assertThat(diff.getHeader()).isNull();
+    }
+
+    @Test
+    public void testParseIssue84() throws IOException {
+        UnifiedDiff diff = UnifiedDiffReader.parseUnifiedDiff(
+                UnifiedDiffReaderTest.class.getResourceAsStream("problem_diff_issue84.diff"));
+
+        assertThat(diff.getFiles().size()).isEqualTo(2);
+
+        UnifiedDiffFile file1 = diff.getFiles().get(0);
+        assertThat(file1.getFromFile()).isEqualTo("config/ant-phase-verify.xml");
+        assertThat(file1.getPatch().getDeltas().size()).isEqualTo(1);
+
+        UnifiedDiffFile file2 = diff.getFiles().get(1);
+        assertThat(file2.getFromFile()).isEqualTo("/dev/null");
+        assertThat(file2.getPatch().getDeltas().size()).isEqualTo(1);
+
+        assertThat(diff.getTail()).isEqualTo("2.7.4");
+        assertThat(diff.getHeader()).startsWith("From b53e612a2ab5ff15d14860e252f84c0f343fe93a Mon Sep 17 00:00:00 2001");
+    }
+
+    @Test
+    public void testParseIssue85() throws IOException {
+        UnifiedDiff diff = UnifiedDiffReader.parseUnifiedDiff(
+                UnifiedDiffReaderTest.class.getResourceAsStream("problem_diff_issue85.diff"));
+
+        assertThat(diff.getFiles().size()).isEqualTo(1);
+
+        assertEquals(1, diff.getFiles().size());
+
+        final UnifiedDiffFile file1 = diff.getFiles().get(0);
+        assertEquals("diff -r 83e41b73d115 -r a4438263b228 tests/test-check-pyflakes.t",
+                file1.getDiffCommand());
+        assertEquals("tests/test-check-pyflakes.t", file1.getFromFile());
+        assertEquals("tests/test-check-pyflakes.t", file1.getToFile());
+        assertEquals(1, file1.getPatch().getDeltas().size());
+
+        assertNull(diff.getTail());
+    }
+
+    @Test
+    public void testTimeStampRegexp() {
+        assertThat("2019-04-18 13:49:39.516149751 +0200").matches(UnifiedDiffReader.TIMESTAMP_REGEXP);
+    }
+    
+    @Test
+    public void testParseIssue98() throws IOException {
+        UnifiedDiff diff = UnifiedDiffReader.parseUnifiedDiff(
+                UnifiedDiffReaderTest.class.getResourceAsStream("problem_diff_issue98.diff"));
+
+        assertThat(diff.getFiles().size()).isEqualTo(1);
+
+        assertEquals(1, diff.getFiles().size());
+
+        final UnifiedDiffFile file1 = diff.getFiles().get(0);
+        assertEquals("100644",
+                file1.getDeletedFileMode());
+        assertEquals("src/test/java/se/bjurr/violations/lib/model/ViolationTest.java", file1.getFromFile());
+        assertThat(diff.getTail()).isEqualTo("2.25.1");
     }
 }
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 a89d027..5527fe1 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
@@ -72,7 +72,7 @@ public class UnifiedDiffWriterTest {
         
         String[] lines = writer.toString().split("\\n");
         
-        assertEquals("--- null", lines[0]);
+        assertEquals("--- /dev/null", lines[0]);
         assertEquals("+++ revised", lines[1]);
         assertEquals("@@ -0,0 +1,2 @@", lines[2]);
     }
diff --git a/java-diff-utils/src/test/resources/com/github/difflib/text/issue_86_original.txt b/java-diff-utils/src/test/resources/com/github/difflib/text/issue_86_original.txt
new file mode 100644
index 0000000..02c2492
--- /dev/null
+++ b/java-diff-utils/src/test/resources/com/github/difflib/text/issue_86_original.txt
@@ -0,0 +1,7 @@
+MessageTime,MessageType,Instrument,InstrumentState,TradePrice,TradeVolume,TradeCond,TradeId,AskPrice1,AskVol1,BidPrice1,BidVol1,AskPrice2,AskVol2,BidPrice2,BidVol2,AskPrice3,AskVol3,BidPrice3,BidVol3,AskPrice4,AskVol4,BidPrice4,BidVol4,AskPrice5,AskVol5,BidPrice5,BidVol5
+2020-04-04T08:00:00.000Z,S,HHD_MAY20,Open,,,,,,,,,,,,,,,,,,,,,,,,
+2020-04-04T08:00:00.000Z,S,FHK_C23.5_MAY20,Open,,,,,,,,,,,,,,,,,,,,,,,,
+2020-04-04T13:49:11.522Z,Q,HHD_MAY20,,,,,,2.6,10,2.6,10,,,,,,,,,,,,,,,,
+2020-04-04T13:49:18.210Z,T,HHD_MAY20,,2.6,1,Screen,0,,,,,,,,,,,,,,,,,,,,
+2020-04-04T17:00:00.000Z,S,HHD_MAY20,Close,,,,,,,,,,,,,,,,,,,,,,,,
+2020-04-04T17:00:00.000Z,S,FHK_C23.5_MAY20,Close,,,,,,,,,,,,,,,,,,,,,,,,
diff --git a/java-diff-utils/src/test/resources/com/github/difflib/text/issue_86_revised.txt b/java-diff-utils/src/test/resources/com/github/difflib/text/issue_86_revised.txt
new file mode 100644
index 0000000..e081675
--- /dev/null
+++ b/java-diff-utils/src/test/resources/com/github/difflib/text/issue_86_revised.txt
@@ -0,0 +1,8 @@
+MessageTime,MessageType,Instrument,InstrumentState,TradePrice,TradeVolume,TradeCond,TradeId,AskPrice1,AskVol1,BidPrice1,BidVol1,AskPrice2,AskVol2,BidPrice2,BidVol2,AskPrice3,AskVol3,BidPrice3,BidVol3,AskPrice4,AskVol4,BidPrice4,BidVol4,AskPrice5,AskVol5,BidPrice5,BidVol5
+2020-04-02T08:00:00.000Z,S,HHD_MAY20,Open,,,,,,,,,,,,,,,,,,,,,,,,
+2020-04-02T08:00:00.000Z,S,FHK_C23.5_MAY20,Open,,,,,,,,,,,,,,,,,,,,,,,,
+2020-04-04T13:49:11.522Z,Q,HHD_MAY20,,,,,,2.6,10,2.6,10,,,,,,,,,,,,,,,,
+2020-04xs-04T17dw:00:00.000Z,Sdwdw,HHD_MAY20dwdw,Closdwde,,,,,,,,,,,,,,,,,,,,,,,,
+2020-04-04T13:49:18.210Z,T,HHD_MAY20,,2.6,2,Screen,0,,,,,,,,,,,,,,,,,,,,
+2020-04-04T17:00:00.000Z,S,HHD_MAY20,Close,,,,,,,,,,,,,,,,,,,,,,,,
+2020-04-04T17:00:00.000Z,S,FHK_C23.5_MAY20,Close,,,,,,,,,,,,,,,,,,,,,,,,
\ No newline at end of file
diff --git a/java-diff-utils/src/test/resources/com/github/difflib/unifieddiff/problem_diff_issue51.diff b/java-diff-utils/src/test/resources/com/github/difflib/unifieddiff/problem_diff_issue51.diff
index 0c8b483..a9c4dae 100644
--- a/java-diff-utils/src/test/resources/com/github/difflib/unifieddiff/problem_diff_issue51.diff
+++ b/java-diff-utils/src/test/resources/com/github/difflib/unifieddiff/problem_diff_issue51.diff
@@ -8,4 +8,5 @@ diff -U0 old/f2 new/f2
 --- old/f2	2019-09-25 14:38:14.000000000 +0200
 +++ new/f2	2019-09-25 14:38:32.000000000 +0200
 @@ -1 +1 @@
--a\nc
\ No newline at end of file
+-a\nc
++a\nb\nd
diff --git a/java-diff-utils/src/test/resources/com/github/difflib/unifieddiff/problem_diff_issue79.diff b/java-diff-utils/src/test/resources/com/github/difflib/unifieddiff/problem_diff_issue79.diff
new file mode 100644
index 0000000..4fd3403
--- /dev/null
+++ b/java-diff-utils/src/test/resources/com/github/difflib/unifieddiff/problem_diff_issue79.diff
@@ -0,0 +1,3 @@
+diff --git a/test/Issue.java b/test/Issue.java
+new file mode 100644
+index 00000000..9702606e
\ No newline at end of file
diff --git a/java-diff-utils/src/test/resources/com/github/difflib/unifieddiff/problem_diff_issue84.diff b/java-diff-utils/src/test/resources/com/github/difflib/unifieddiff/problem_diff_issue84.diff
new file mode 100644
index 0000000..e558fc0
--- /dev/null
+++ b/java-diff-utils/src/test/resources/com/github/difflib/unifieddiff/problem_diff_issue84.diff
@@ -0,0 +1,109 @@
+From b53e612a2ab5ff15d14860e252f84c0f343fe93a Mon Sep 17 00:00:00 2001
+From: nmancus1 <nmancus1
+Date: Thu, 4 Jun 2020 11:46:34 -0400
+Subject: [PATCH] minor: Add input file for Java14 instanceof with pattern
+ matching (#7290)
+
+---
+ config/ant-phase-verify.xml                        |  2 +
+ .../InputJava14InstanceofWithPatternMatching.java  | 76 ++++++++++++++++++++++
+ 2 files changed, 78 insertions(+)
+ create mode 100644 src/test/resources-noncompilable/com/puppycrawl/tools/checkstyle/grammar/java14/InputJava14InstanceofWithPatternMatching.java
+
+diff --git a/config/ant-phase-verify.xml b/config/ant-phase-verify.xml
+index d27ffef..e0285ce 100644
+--- a/config/ant-phase-verify.xml
++++ b/config/ant-phase-verify.xml
+@@ -148,6 +148,8 @@
+          <exclude name="**/InputMainFrameModelIncorrectClass.java"/>
+          <exclude name="**/InputBeforeExecutionExclusionFileFilterIncorrectClass.java"/>
+          <exclude name="**/InputJavaParser.java"/>
++         <!-- until https://github.com/checkstyle/checkstyle/issues/7290 -->
++         <exclude name="**/InputJava14InstanceofWithPatternMatching.java"/>
+          <!-- Cannot parse until Java 14 support -->
+          <exclude name="**/InputJava14Records.java"/>
+         </fileset>
+diff --git a/src/test/resources-noncompilable/com/puppycrawl/tools/checkstyle/grammar/java14/InputJava14InstanceofWithPatternMatching.java b/src/test/resources-noncompilable/com/puppycrawl/tools/checkstyle/grammar/java14/InputJava14InstanceofWithPatternMatching.java
+new file mode 100644
+index 0000000..8fa3eba
+--- /dev/null
++++ b/src/test/resources-noncompilable/com/puppycrawl/tools/checkstyle/grammar/java14/InputJava14InstanceofWithPatternMatching.java
+@@ -0,0 +1,76 @@
++//non-compiled with javac: Compilable with Java14
++package com.puppycrawl.tools.checkstyle.grammar.java14;
++
++import java.util.Arrays;
++import java.util.Locale;
++ ...... I removed it
++ ...... I removed it
++ ...... I removed it
++ ...... I removed it
++ ...... I removed it
++ ...... I removed it
++ ...... I removed it
++ ...... I removed it
++ ...... I removed it
++ ...... I removed it
++ ...... I removed it
++ ...... I removed it
++ ...... I removed it
++ ...... I removed it
++ ...... I removed it
++ ...... I removed it
++ ...... I removed it
++ ...... I removed it
++ ...... I removed it
++ ...... I removed it
++ ...... I removed it
++ ...... I removed it
++ ...... I removed it
++ ...... I removed it
++ ...... I removed it
++ ...... I removed it
++ ...... I removed it
++ ...... I removed it
++ ...... I removed it
++ ...... I removed it
++ ...... I removed it
++ ...... I removed it
++ ...... I removed it
++ ...... I removed it
++ ...... I removed it
++ ...... I removed it
++ ...... I removed it
++ ...... I removed it
++ ...... I removed it
++ ...... I removed it
++ ...... I removed it
++ ...... I removed it
++ ...... I removed it
++ ...... I removed it
++ ...... I removed it
++ ...... I removed it
++ ...... I removed it
++ ...... I removed it
++ ...... I removed it
++ ...... I removed it
++ ...... I removed it
++ ...... I removed it
++ ...... I removed it
++ ...... I removed it
++ ...... I removed it
++ ...... I removed it
++ ...... I removed it
++ ...... I removed it
++ ...... I removed it
++ ...... I removed it
++ ...... I removed it
++ ...... I removed it
++ ...... I removed it
++ ...... I removed it
++ ...... I removed it
++ ...... I removed it
++ ...... I removed it
++ ...... I removed it
++        }
++    }
++}
+--
+2.7.4
\ No newline at end of file
diff --git a/java-diff-utils/src/test/resources/com/github/difflib/unifieddiff/problem_diff_issue85.diff b/java-diff-utils/src/test/resources/com/github/difflib/unifieddiff/problem_diff_issue85.diff
new file mode 100644
index 0000000..09d12f3
--- /dev/null
+++ b/java-diff-utils/src/test/resources/com/github/difflib/unifieddiff/problem_diff_issue85.diff
@@ -0,0 +1,31 @@
+# HG changeset patch
+# User Anton Shestakov <av6@dwimlabs.net>
+# Date 1591442367 -28800
+# Node ID a4438263b228dd3e2983d59095c6180b1411f0e8
+# Parent  83e41b73d115e3717943c2e5a83d36d05670384c
+tests: skip pyflakes for mercurial/thirdparty/
+
+The current version of pyflakes (2.2.0) correctly detects one issue:
+
+  mercurial/thirdparty/selectors2.py:335:40 '...'.format(...) has unused arguments at position(s): 1
+
+But we're not interested in fixing lint errors in third-party code, so we need
+to exclude at least selectors2.py. And in the discussion for this patch it was
+decided to just skip the entire thirdparty directory.
+
+Differential Revision: https://phab.mercurial-scm.org/D8619
+
+diff -r 83e41b73d115 -r a4438263b228 tests/test-check-pyflakes.t
+--- a/tests/test-check-pyflakes.t	Tue Jun 09 17:13:26 2020 -0400
++++ b/tests/test-check-pyflakes.t	Sat Jun 06 19:19:27 2020 +0800
+@@ -16,9 +16,7 @@
+   $ testrepohg locate 'set:**.py or grep("^#!.*python")' \
+   > -X hgext/fsmonitor/pywatchman \
+   > -X mercurial/pycompat.py -X contrib/python-zstandard \
+-  > -X mercurial/thirdparty/cbor \
+-  > -X mercurial/thirdparty/concurrent \
+-  > -X mercurial/thirdparty/zope \
++  > -X mercurial/thirdparty \
+   > 2>/dev/null \
+   > | xargs $PYTHON -m pyflakes 2>/dev/null | "$TESTDIR/filterpyflakes.py"
+   contrib/perf.py:*:* undefined name 'xrange' (glob) (?)
\ No newline at end of file
diff --git a/java-diff-utils/src/test/resources/com/github/difflib/unifieddiff/problem_diff_issue98.diff b/java-diff-utils/src/test/resources/com/github/difflib/unifieddiff/problem_diff_issue98.diff
new file mode 100644
index 0000000..e5f7f8d
--- /dev/null
+++ b/java-diff-utils/src/test/resources/com/github/difflib/unifieddiff/problem_diff_issue98.diff
@@ -0,0 +1,55 @@
+From fd940c6f66126734e82c00889e7c987e11deea91 Mon Sep 17 00:00:00 2001
+From: Tomas Bjerre <tomas.bjerre85@gmail.com>
+Date: Sun, 6 Sep 2020 11:20:34 +0200
+Subject: [PATCH] removing file
+
+---
+ .../violations/lib/model/ViolationTest.java   | 37 -------------------
+ 1 file changed, 37 deletions(-)
+ delete mode 100644 src/test/java/se/bjurr/violations/lib/model/ViolationTest.java
+
+diff --git a/src/test/java/se/bjurr/violations/lib/model/ViolationTest.java b/src/test/java/se/bjurr/violations/lib/model/ViolationTest.java
+deleted file mode 100644
+index 3e40b52..0000000
+--- a/src/test/java/se/bjurr/violations/lib/model/ViolationTest.java
++++ /dev/null
+@@ -1,37 +0,0 @@
+-package se.bjurr.violations.lib.model;
+-
+-import static org.assertj.core.api.Assertions.assertThat;
+-import static se.bjurr.violations.lib.model.SEVERITY.ERROR;
+-import static se.bjurr.violations.lib.model.Violation.violationBuilder;
+-import static se.bjurr.violations.lib.reports.Parser.CHECKSTYLE;
+-
+-import org.junit.Test;
+-import se.bjurr.violations.lib.model.Violation.ViolationBuilder;
+-import uk.co.jemos.podam.api.PodamFactoryImpl;
+-
+-public class ViolationTest {
+-
+-  @Test
+-  public void testThatFilePathsAreAlwaysFronSlashes() {
+-    final Violation violation =
+-        violationBuilder() //
+-            .setParser(CHECKSTYLE) //
+-            .setFile("c:\\path\\to\\file.xml") //
+-            .setMessage("message") //
+-            .setSeverity(ERROR) //
+-            .setStartLine(1) //
+-            .build();
+-    assertThat(violation.getFile()) //
+-        .isEqualTo("c:/path/to/file.xml");
+-  }
+-
+-  @Test
+-  public void testThatCopyConstructorWorks() {
+-    final ViolationBuilder originalBuilder =
+-        new PodamFactoryImpl().manufacturePojo(ViolationBuilder.class);
+-    final Violation original = originalBuilder.build();
+-    final Violation copied = new Violation(original);
+-    assertThat(copied) //
+-        .isEqualTo(original);
+-  }
+-}
+-- 
+2.25.1
diff --git a/java-diff-utils/src/test/resources/logging.properties b/java-diff-utils/src/test/resources/logging.properties
new file mode 100644
index 0000000..4c1655b
--- /dev/null
+++ b/java-diff-utils/src/test/resources/logging.properties
@@ -0,0 +1,8 @@
+handlers=java.util.logging.ConsoleHandler
+
+.level=INFO
+com.github.difflib.unifieddiff.level=FINE
+
+java.util.logging.ConsoleHandler.level=INFO
+#java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter
+
diff --git a/java-diff-utils/src/test/resources/mocks/issue89_patch.txt b/java-diff-utils/src/test/resources/mocks/issue89_patch.txt
new file mode 100644
index 0000000..7d410ed
--- /dev/null
+++ b/java-diff-utils/src/test/resources/mocks/issue89_patch.txt
@@ -0,0 +1,29 @@
+--- Origin.java	2020-06-11 11:06:21.000000000 +0800
++++ Update.java	2020-06-11 10:59:48.000000000 +0800
+@@ -1,9 +1,17 @@
+ package checkstyle_demo.PatchSuppression.MultiChangeInOneFile;
+ 
+-public class Origin {
++public class Update {
+     public void test1() {
+ 
+     }
++
++    public void test2() {
++
++    }
++
++    public void test3() {
++
++    }
+ }
+ 
+ class BasicTest {
+@@ -16,5 +24,7 @@
+ class Test2 {
+     public void test1() {
+         System.out.println();
++        System.out.println();
++        System.out.println();
+     }
+ }
diff --git a/java-diff-utils/src/test/resources/mocks/issue89_revised.txt b/java-diff-utils/src/test/resources/mocks/issue89_revised.txt
new file mode 100644
index 0000000..559e6a4
--- /dev/null
+++ b/java-diff-utils/src/test/resources/mocks/issue89_revised.txt
@@ -0,0 +1,30 @@
+package checkstyle_demo.PatchSuppression.MultiChangeInOneFile;
+
+public class issue89_revised {
+    public void test1() {
+
+    }
+
+    public void test2() {
+
+    }
+
+    public void test3() {
+
+    }
+}
+
+class Test {
+    private int i;
+    void foo() {
+        i++;
+    }
+}
+
+class Test2 {
+    public void test1() {
+        System.out.println();
+        System.out.println();
+        System.out.println();
+    }
+}
diff --git a/pom.xml b/pom.xml
index b6b3d5f..14aa2c9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -3,7 +3,7 @@
     <modelVersion>4.0.0</modelVersion>
     <groupId>io.github.java-diff-utils</groupId>
     <artifactId>java-diff-utils-parent</artifactId>
-    <version>4.7</version>
+    <version>4.8</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>java-diff-utils-parent-4.7</tag>
+        <tag>java-diff-utils-parent-4.8</tag>
     </scm>
     <issueManagement>
         <system>GitHub Issues</system>
-- 
GitLab