@Nullable
    private PatchHunk readNextHunkContext(ListIterator<String> iterator)
        throws PatchSyntaxException {
      while (iterator.hasNext()) {
        String curLine = iterator.next();
        if (curLine.startsWith(CONTEXT_FILE_PREFIX)) {
          iterator.previous();
          return null;
        }
        if (curLine.startsWith(CONTEXT_HUNK_PREFIX)) {
          break;
        }
      }
      if (!iterator.hasNext()) {
        return null;
      }
      Matcher beforeMatcher = ourContextBeforeHunkStartPattern.matcher(iterator.next());
      if (!beforeMatcher.matches()) {
        throw new PatchSyntaxException(
            iterator.previousIndex(), "Unknown before hunk start syntax");
      }
      List<String> beforeLines = readContextDiffLines(iterator);
      if (!iterator.hasNext()) {
        throw new PatchSyntaxException(iterator.previousIndex(), "Missing after hunk");
      }
      Matcher afterMatcher = ourContextAfterHunkStartPattern.matcher(iterator.next());
      if (!afterMatcher.matches()) {
        throw new PatchSyntaxException(iterator.previousIndex(), "Unknown after hunk start syntax");
      }
      // if (! iterator.hasNext()) {
      // throw new PatchSyntaxException(iterator.previousIndex(), "Unexpected patch end");
      // }
      List<String> afterLines = readContextDiffLines(iterator);
      int startLineBefore = Integer.parseInt(beforeMatcher.group(1));
      int endLineBefore = Integer.parseInt(beforeMatcher.group(2));
      int startLineAfter = Integer.parseInt(afterMatcher.group(1));
      int endLineAfter = Integer.parseInt(afterMatcher.group(2));
      PatchHunk hunk =
          new PatchHunk(
              startLineBefore - 1, endLineBefore - 1, startLineAfter - 1, endLineAfter - 1);

      int beforeLineIndex = 0;
      int afterLineIndex = 0;
      PatchLine lastBeforePatchLine = null;
      PatchLine lastAfterPatchLine = null;
      if (beforeLines.size() == 0) {
        for (String line : afterLines) {
          hunk.addLine(parsePatchLine(line, 2));
        }
      } else if (afterLines.size() == 0) {
        for (String line : beforeLines) {
          hunk.addLine(parsePatchLine(line, 2));
        }
      } else {
        while (beforeLineIndex < beforeLines.size() || afterLineIndex < afterLines.size()) {
          String beforeLine =
              beforeLineIndex >= beforeLines.size() ? null : beforeLines.get(beforeLineIndex);
          String afterLine =
              afterLineIndex >= afterLines.size() ? null : afterLines.get(afterLineIndex);
          if (startsWith(beforeLine, NO_NEWLINE_SIGNATURE) && lastBeforePatchLine != null) {
            lastBeforePatchLine.setSuppressNewLine(true);
            beforeLineIndex++;
          } else if (startsWith(afterLine, NO_NEWLINE_SIGNATURE) && lastAfterPatchLine != null) {
            lastAfterPatchLine.setSuppressNewLine(true);
            afterLineIndex++;
          } else if (startsWith(beforeLine, " ")
              && (startsWith(afterLine, " ")
                  || afterLine
                      == null /* handle some weird cases with line breaks truncated at EOF */)) {
            addContextDiffLine(hunk, beforeLine, PatchLine.Type.CONTEXT);
            beforeLineIndex++;
            afterLineIndex++;
          } else if (startsWith(beforeLine, "-")) {
            lastBeforePatchLine = addContextDiffLine(hunk, beforeLine, PatchLine.Type.REMOVE);
            beforeLineIndex++;
          } else if (startsWith(afterLine, "+")) {
            lastAfterPatchLine = addContextDiffLine(hunk, afterLine, PatchLine.Type.ADD);
            afterLineIndex++;
          } else if (startsWith(beforeLine, "!") && startsWith(afterLine, "!")) {
            while (beforeLineIndex < beforeLines.size()
                && beforeLines.get(beforeLineIndex).startsWith("! ")) {
              lastBeforePatchLine =
                  addContextDiffLine(hunk, beforeLines.get(beforeLineIndex), PatchLine.Type.REMOVE);
              beforeLineIndex++;
            }

            while (afterLineIndex < afterLines.size()
                && afterLines.get(afterLineIndex).startsWith("! ")) {
              lastAfterPatchLine =
                  addContextDiffLine(hunk, afterLines.get(afterLineIndex), PatchLine.Type.ADD);
              afterLineIndex++;
            }
          } else {
            throw new PatchSyntaxException(-1, "Unknown line prefix");
          }
        }
      }
      return hunk;
    }
    @Nullable
    private PatchHunk readNextHunkUnified(ListIterator<String> iterator)
        throws PatchSyntaxException {
      String curLine = null;
      int numIncrements = 0;
      while (iterator.hasNext()) {
        curLine = iterator.next();
        ++numIncrements;
        if (curLine.startsWith("--- ")) {
          for (int i = 0; i < numIncrements; i++) {
            iterator.previous();
          }
          return null;
        }
        if (curLine.startsWith("@@ ")) {
          break;
        }
      }
      if (!iterator.hasNext()) return null;

      Matcher m = ourUnifiedHunkStartPattern.matcher(curLine);
      if (!m.matches()) {
        throw new PatchSyntaxException(iterator.previousIndex(), "Unknown hunk start syntax");
      }
      int startLineBefore = Integer.parseInt(m.group(1));
      final String linesBeforeText = m.group(3);
      int linesBefore = linesBeforeText == null ? 1 : Integer.parseInt(linesBeforeText);
      int startLineAfter = Integer.parseInt(m.group(4));
      final String linesAfterText = m.group(6);
      int linesAfter = linesAfterText == null ? 1 : Integer.parseInt(linesAfterText);
      PatchHunk hunk =
          new PatchHunk(
              startLineBefore - 1,
              startLineBefore + linesBefore - 1,
              startLineAfter - 1,
              startLineAfter + linesAfter - 1);

      PatchLine lastLine = null;
      int before = 0;
      int after = 0;
      while (iterator.hasNext()) {
        String hunkCurLine = iterator.next();
        if (lastLine != null && hunkCurLine.startsWith(NO_NEWLINE_SIGNATURE)) {
          lastLine.setSuppressNewLine(true);
          continue;
        }
        lastLine = parsePatchLine(hunkCurLine, 1, before < linesBefore || after < linesAfter);
        if (lastLine == null) {
          iterator.previous();
          break;
        }
        switch (lastLine.getType()) {
          case CONTEXT:
            before++;
            after++;
            break;
          case ADD:
            after++;
            break;
          case REMOVE:
            before++;
            break;
        }
        hunk.addLine(lastLine);
      }
      return hunk;
    }