Source code for cpp_linter.git.git_str

"""This was reintroduced to deal with any bugs in pygit2 (or the libgit2 C library it
binds to). The `parse_diff()` function here is only used when
:py:meth:`pygit2.Diff.parse_diff()` function fails in `cpp_linter.git.parse_diff()`"""

import re
from typing import Optional, List, Tuple, cast
from ..common_fs import FileObj, has_line_changes
from ..common_fs.file_filter import FileFilter
from ..loggers import logger


DIFF_FILE_DELIMITER = re.compile(r"^diff --git a/.*$", re.MULTILINE)
DIFF_FILE_NAME = re.compile(r"^\+\+\+\sb?/(.*)$", re.MULTILINE)
DIFF_RENAMED_FILE = re.compile(r"^rename to (.*)$", re.MULTILINE)
DIFF_BINARY_FILE = re.compile(r"^Binary\sfiles\s", re.MULTILINE)
HUNK_INFO = re.compile(r"^@@\s\-\d+,?\d*\s\+(\d+,?\d*)\s@@", re.MULTILINE)


def _get_filename_from_diff(front_matter: str) -> Optional[re.Match]:
    """Get the filename from content in the given diff front matter."""
    filename_match = DIFF_FILE_NAME.search(front_matter)
    if filename_match is not None:
        return filename_match

    # check for renamed file name
    rename_match = DIFF_RENAMED_FILE.search(front_matter)
    if rename_match is not None and front_matter.lstrip().startswith("similarity"):
        return rename_match
    # We may need to compensate for other instances where the filename is
    # not directly after `+++ b/`. Binary files are another example of this.
    if DIFF_BINARY_FILE.search(front_matter) is None:
        # log the case and hope it helps in the future
        logger.warning(  # pragma: no cover
            "Unrecognized diff starting with:\n%s",
            "\n".join(front_matter.splitlines()),
        )
    return None


[docs] def parse_diff( full_diff: str, file_filter: FileFilter, lines_changed_only: int, ) -> List[FileObj]: """Parse a given diff into file objects. :param full_diff: The complete diff for an event. :param file_filter: A `FileFilter` object. :param lines_changed_only: A value that dictates what file changes to focus on. :returns: A `list` of `FileObj` instances containing information about the files changed. """ file_objects: List[FileObj] = [] logger.error("Using pure python to parse diff because pygit2 failed!") file_diffs = DIFF_FILE_DELIMITER.split(full_diff.lstrip("\n")) for diff in file_diffs: if not diff or diff.lstrip().startswith("deleted file"): continue first_hunk = HUNK_INFO.search(diff) hunk_start = -1 if first_hunk is None else first_hunk.start() diff_front_matter = diff[:hunk_start] filename_match = _get_filename_from_diff(diff_front_matter) if filename_match is None: continue filename = cast(str, filename_match.groups(0)[0]) if first_hunk is None: continue if not file_filter.is_source_or_ignored(filename): continue diff_chunks, additions = _parse_patch(diff[first_hunk.start() :]) if has_line_changes(lines_changed_only, diff_chunks, additions): file_objects.append(FileObj(filename, additions, diff_chunks)) return file_objects
def _parse_patch(full_patch: str) -> Tuple[List[List[int]], List[int]]: """Parse a diff's patch accordingly. :param full_patch: The entire patch of hunks for 1 file. :returns: A `tuple` of lists where - Index 0 is the ranges of lines in the diff. Each item in this `list` is a 2 element `list` describing the starting and ending line numbers. - Index 1 is a `list` of the line numbers that contain additions. """ ranges: List[List[int]] = [] # additions is a list line numbers in the diff containing additions additions: List[int] = [] line_numb_in_diff: int = 0 chunks = HUNK_INFO.split(full_patch) for index, chunk in enumerate(chunks): if index % 2 == 1: # each odd element holds the starting line number and number of lines if "," in chunk: start_line, hunk_length = [int(x) for x in chunk.split(",")] else: start_line = int(chunk) hunk_length = 1 ranges.append([start_line, hunk_length + start_line]) line_numb_in_diff = start_line continue # each even element holds the actual line changes for i, line in enumerate(chunk.splitlines()): if line.startswith("+"): additions.append(line_numb_in_diff) if not line.startswith("-") and i: # don't increment on first line line_numb_in_diff += 1 return (ranges, additions)