Example #1
0
    def parse_patchfile(patchfile, block_dict):
        """Parse the patchfile and update corresponding block ranges."""
        # https://pypi.python.org/pypi/whatthepatch/0.0.2
        diffs = None
        with open(patchfile) as stream:
            diffs = whatthepatch.parse_patch(stream.read())
        for diff in diffs:
            curr_file = diff.header.old_path
            # In some cases, whatthepatch will set the path to "a/path/to/file"
            # instead of "path/to/file", so we strip "a/" away to match with
            # the dictionary key in block_dict
            if curr_file.startswith("a/"):
                curr_file = curr_file[2:]
            # change format: [line before patch, line after patch, text]
            for change in diff.changes:
                # line removed
                if change[0] and not change[1]:
                    curr_line = int(change[0])
                    blocks = block_dict.get(curr_file, [])
                    blocks = Block.update_block_ranges(blocks, curr_line, -1)
                    block_dict[curr_file] = blocks
                # line added
                elif not change[0] and change[1]:
                    curr_line = int(change[1])
                    blocks = block_dict.get(curr_file, [])
                    blocks = Block.update_block_ranges(blocks, curr_line, 1)
                    block_dict[curr_file] = blocks

        # Set ranges to updated ranges
        for curr_file in block_dict:
            for block in block_dict[curr_file]:
                block.range = block.new_range
        return block_dict
Example #2
0
def get_diff_patches(commit: Commit,
                     split_per_method=True,
                     only_unstaged=False) -> List[str]:
    '''
    Creates a patch file containing all the diffs in the repository and then
    returns all those patches as a list of patches
    '''
    # this command will output the diff information into stdout
    command = 'git --no-pager diff'
    if commit and not only_unstaged:
        command += ' {hash}~ {hash}'.format(hash=commit.commit_id)
    diff_data, _ = run_cmd(commit.repository, command)

    # TODO: MY GOODNESS this is such a hack
    if not diff_data:
        command = 'git --no-pager diff'
        diff_data, _ = run_cmd(commit.repository, command)

    # TODO: GETTING EVEN WORSE.  This is when there is a commit already created
    if not diff_data and not only_unstaged:
        command = 'git diff origin/bug_buddy..HEAD'
        diff_data, _ = run_cmd(commit.repository, command)

    raw_patches = diff_data.split('diff --git ')[1:]

    # covert the list of patches into whatthepatch patch objects
    patches = [
        list(whatthepatch.parse_patch(patch))[0] for patch in raw_patches
    ]

    # if split_per_method and commit.function_histories:
    #     patches = _split_patches_by_method(commit, patches)

    return patches
Example #3
0
def analyze_patch(patch, check_annotations):
    files = get_files(patch)
    changed = set(files['touched']) | set(files['moved'].keys())

    if check_annotations:
        newed = set(files['added']) | set(files['deleted'])

        info = defaultdict(lambda: [])
        for diff in whatthepatch.parse_patch(patch):
            h = diff.header
            if not h:
                continue

            old_p = h.old_path
            old_p = old_p[2:] if old_p.startswith('a/') else old_p
            if old_p in newed:
                # the file has just been added or deleted,
                # so nothing to compute
                continue

            for old, new, _ in diff.changes:
                if old is not None and new is None:
                    # removed line
                    info[old_p].append(old)

        files = list(info.keys())
        if files:
            annotations = Annotate.get(files, node='tip')
            stats = analyze_annotations(info, annotations)
            return stats, changed

    return {'deleted': {}, 'all': {}}, changed
Example #4
0
    def analyze_diff(self, diff):
        def status(change):
            if change[0] is None:
                return 'insert'
            elif change[1] is None:
                return 'delete'
            else:
                return 'equal'

        out = []
        for hunk in whatthepatch.parse_patch(diff.decode('utf-8')):
            file_changes = []
            in_changeset = False
            for mode in [('insert', 1), ('delete', 0)]:
                for change in hunk.changes:
                    if status(change) == mode[0] and not in_changeset:
                        in_changeset = True
                        file_changes.append({
                            'start': change[mode[1]],
                            'type': mode[0]
                        })
                    elif status(change) == 'equal' and in_changeset:
                        in_changeset = False
                        file_changes[-1]['end'] = change[mode[1]] - 1
            out.append({'header': hunk.header, 'changes': file_changes})
        return out
Example #5
0
def parse_changes(repo, changes):
    for change in changes:
        for diff in wtp.parse_patch(get_diff(repo, change)):
            removed = list()
            added = list()
            if not (diff.header.old_path.endswith(".java")
                    or diff.header.new_path.endswith(".java")):
                continue

            for r, a, text in diff.changes:
                if r:
                    removed.append(r)
                if a:
                    added.append(a)

            mremoved_blocks = set()
            cremoved_blocks = set()
            if diff.header.old_path != "/dev/null":
                logger.info("Generating XML for file %s @ %s", change.old.path,
                            change.old.sha)
                ftext = repo[change.old.sha].as_raw_string()
                mremoved_blocks, cremoved_blocks = get_blocks(ftext, removed)

            madded_blocks = set()
            cadded_blocks = set()
            if diff.header.new_path != "/dev/null":
                logger.info("Generating XML for file %s @ %s", change.new.path,
                            change.new.sha)
                ftext = repo[change.new.sha].as_raw_string()
                madded_blocks, cadded_blocks = get_blocks(ftext, added)

            yield mremoved_blocks, madded_blocks, cremoved_blocks, cadded_blocks
Example #6
0
def generate(patch):
    '''
    This function generates ...
    '''
    mapping = {}

    download_mapping()

    paths = []
    for diff in whatthepatch.parse_patch(patch.decode('utf-8')):
        # Get old and new path, for files that have been renamed.
        path = diff.header.new_path[2:] if diff.header.new_path.startswith(
            'b/') else diff.header.new_path

        # If the diff doesn't contain any changes, we skip it.
        if diff.changes is None:
            continue

        # If the file is not a source file, we skip it (as we already know
        # we have no coverage information for it).
        if not coverage_supported(path):
            continue

        paths.append(path)

    with sqlite3.connect('chunk_mapping.db') as conn:
        c = conn.cursor()
        for path in paths:
            c.execute('SELECT chunk FROM files WHERE path=?',
                      (path.encode('utf-8'), ))
            mapping[path] = c.fetchall()

    return mapping
Example #7
0
def diff(repo):
    repopath = posixpath.join(workspace, repo)
    branch = request.args.get('branch', '')
    checkoutBranch(branch, repopath)
    if request.method == 'GET':
        graph1 = request.args.get('a', '')
        graph2 = request.args.get('b', '')
        fileGraph1 = FileGraph(graph1, domain, repopath, repo)
        fileGraph2 = FileGraph(graph2, domain, repopath, repo)
        log.info(fileGraph1.filename)
        log.info(fileGraph2.filename)
        patchtext = diffnoindex(fileGraph1.filename, fileGraph2.filename,
                                repopath)
        # WTF tuple-structure in wthatthepatch
        # https://pypi.org/project/whatthepatch/
        patch = list(whatthepatch.parse_patch(patchtext))[0][1]
        log.info(patchtext)
        for diff in patch:
            a, b, line = diff
            if (b == None):
                print("In a, not in b: " + line)
            if (a == None):
                print("In b, not in a: " + line)
        #TODO: Do something with this ... huh?
        return patchtext
Example #8
0
    def test_git_oneline_change(self):
        with open("tests/casefiles/git-oneline-change.diff") as f:
            text = f.read()

        lines = text.splitlines()

        expected = [
            diffobj(
                header=headerobj(
                    index_path=None,
                    old_path="oneline.txt",
                    old_version="f56f98d",
                    new_path="oneline.txt",
                    new_version="169ceeb",
                ),
                changes=[
                    (1, None, "Adding a one-line file."),
                    (None, 1, "Changed a one-line file."),
                ],
                text="\n".join(lines[:34]) + "\n",
            )
        ]

        results = list(wtp.parse_patch(text))
        self.assert_diffs_equal(results, expected)
Example #9
0
def build_changelog(change: File) -> List[str]:
    lines = ['# Changelogs']
    patch = whatthepatch.parse_patch(change.patch).__next__()
    oldversions: Dict[str, str] = {}
    newversions: Dict[str, str] = {}
    for p in patch.changes:
        if len(p) > 1:
            if p[1] is None:
                req = Requirement.parse(p[2])
                oldversions[req.name] = req.specs[0][1]
            elif p[0] is None:
                req = Requirement.parse(p[2])
                newversions[req.name] = req.specs[0][1]
        else:
            lines.append(f'> Warning: {p[0]} is pinned to a specific version.')
    for package in newversions:
        old = packaging.version.parse(oldversions.get(package))
        new = packaging.version.parse(newversions.get(package))
        changes = changelogs.get(package)
        logged = False
        for version_string in changes.keys():
            v = packaging.version.parse(version_string)
            if old < v <= new:
                lines.append(f'## {package} {version_string}')
                lines.append(changes[version_string])
                logged = True
        if not logged:
            lines.append(f'## {package} {new}')
            lines.append('No release notes found.')
    return lines
def parse_diff(diff, reduce=False):
    if not diff or not diff.startswith("@@"):
        return []

    # split hunks
    parts = []
    for l in diff.split("\n"):
        if l.startswith("@@"):
            parts.append([])
        parts[-1].append(l)

    results = []
    position = 0

    if reduce:
        parts = parts[-1:]

    # parse each hunk
    for part in parts:
        diff = whatthepatch.parse_patch(part).next()  # only one file = only one diff
        result = [["comment", u"…", u"…", part[0], position]]
        for old, new, text in diff.changes:
            position += 1
            mode = " " if old and new else "-" if old else "+"
            result.append([DIFF_LINE_TYPES[mode], old or "", new or "", mode + text, position])
        position += 1
        if reduce:
            result = result[0:1] + result[1:][-12:]

        results.append(result)

    return chain.from_iterable(results)
Example #11
0
    def test_git_oneline_change(self):
        with open('tests/casefiles/git-oneline-change.diff') as f:
            text = f.read()

        lines = text.splitlines()

        expected = [
            diffobj(
                header=headerobj(
                    index_path=None,
                    old_path='oneline.txt',
                    old_version='f56f98d',
                    new_path='oneline.txt',
                    new_version='169ceeb'
                ),
                changes=[
                    (1, None, 'Adding a one-line file.'),
                    (None, 1, 'Changed a one-line file.')
                ],
                text='\n'.join(lines[:34]) + '\n'
            )
        ]

        results = list(wtp.parse_patch(text))
        self.assert_diffs_equal(results, expected)
Example #12
0
    def test_diff_rcs(self):
        with open('tests/casefiles/diff-rcs.diff') as f:
            diff_text = f.read()

        diff = next(wtp.parse_patch(diff_text))

        new_text = wtp.apply.apply_diff(diff, self.lao)
        self.assertEqual(new_text, self.tzu)
def handle_diffs(diff_src, single_desc, multi_desc):
    with open(diff_src) as f:
        text = f.read()
    diff = [x for x in whatthepatch.parse_patch(text)]
    for i in diff:
        rr = extract_lines(i.changes)
        for j in rr:
            dump_data(j, single_desc, multi_desc)
Example #14
0
    def _find_changed_files(self, current_id, new_id):
        """
        Calculates the set of _changed_ files!
        """

        # Grab the raw git diff
        diff_text = self.repo.git.diff(current_id, new_id)

        # parse the diff
        parsed_diff = whatthepatch.parse_patch(diff_text)

        # Our changed files
        changed_files = set()

        # Iterate over the diff -- one 'diff' per file
        for diff in parsed_diff:

            # Detect if the file is new or moved
            if diff.header.old_path != diff.header.new_path:

                if not self.allow_moves:
                    raise RuntimeError(
                        "Your commit range contains file moves. Cowardly aborting."
                    )

                git_old_path = diff.header.old_path

                # If the old path is not /dev/null, then ...
                if git_old_path != "/dev/null":
                    # ... file moves start with 'a' (for old) and 'b' for new
                    old_leading_dir = git_old_path.split(os.path.sep, 1)[0]
                    assert old_leading_dir == "a"

                    git_new_path = diff.header.new_path
                    new_leading_dir, new_path = git_new_path.split(
                        os.path.sep, 1)
                    assert new_leading_dir == "b"
                else:
                    # Otherwise, must be a wholly new file
                    new_path = diff.header.new_path

                #
                # TODO: we need to flag to the user that the Manage project
                # needs updating and things _will not work_ because the units
                # have moved
                #

                # Make it clear that this is further refined by interrogating Manage

            else:
                new_path = diff.header.new_path

            # Currently, environments depend on the new files and the old files
            changed_files.add(new_path)

        return changed_files
Example #15
0
    def test_diff_unified_patchutil(self):
        with open('tests/casefiles/diff-unified.diff') as f:
            diff_text = f.read()

        diff = next(wtp.parse_patch(diff_text))

        new_text = wtp.apply.apply_diff(diff, self.lao, use_patch=True)
        self.assertEqual(new_text, (self.tzu, None))

        self.assertRaises(AssertionError, wtp.apply.apply_diff, diff, [''] + self.lao, use_patch=True)
Example #16
0
def expectedRange(start_line, end_line, diff_str, file_path):
    in_diff = False
    for diff in whatthepatch.parse_patch(diff_str):
        diff_path = diff[0].new_path
        if diff_path == file_path:
            in_diff = True
            line_mapping = diff[1]
            churn_cnt, new_start, new_end = mapLineNumber(
                line_mapping, start_line, end_line)
            return churn_cnt, new_start, new_end
    return 0, start_line, end_line
Example #17
0
def line_match(commit, traceback_line):
    """Return true if line_number was added to filename in commit"""

    cmd = 'git', 'log', '-1', '--format=', '-p', str(commit)
    diff = run_command(*cmd)
    for diff in whatthepatch.parse_patch(diff):
        if diff.header.new_path == traceback_line.git_filename:
            for line in diff.changes:
                if line[0] is None:
                    if line[1] == traceback_line.line_number:
                        return True
    return False
Example #18
0
def line_match(commit, traceback_line):
    """Return true if line_number was added to filename in commit"""

    cmd = 'git', 'log', '-1', '--format=', '-p', str(commit)
    diff = run_command(*cmd)
    for diff in whatthepatch.parse_patch(diff):
        if diff.header.new_path == traceback_line.git_filename:
            for line in diff.changes:
                if line[0] is None:
                    if line[1] == traceback_line.line_number:
                        return True
    return False
Example #19
0
    def get_touched(self, patch):
        res = {}
        for diff in wtp.parse_patch(patch):
            h = diff.header
            new_p = h.new_path[2:] if h.new_path.startswith('b/') else h.new_p
            res[new_p] = added = []
            if diff.changes:
                for old_line, new_line, _ in diff.changes:
                    if not old_line and new_line:
                        added.append(new_line)

        return res
Example #20
0
    def __init__(self, patch, strip_dir=0, chdir=None):
        self.root_dir = None
        self.file_lookup = None
        self.strip_dir = strip_dir
        self.chdir = chdir
        self.diffs = []
        self.files = set()

        for diff in whatthepatch.parse_patch(patch):
            fn = self._diff_path(diff)
            self.files.add(fn)
            self.diffs.append(diff)
Example #21
0
def createBugMap(projectName):

    file = open('../projectData/filteredGitLogFor' + projectName + '.json',
                'r')
    filteredGitLog = json.load(file)
    bugMap = {}

    GitCommands().checkout('master')

    count = 0
    for k, v in list(filteredGitLog.items())[:]:
        currentCommit, parentCommit = v[0]
        patch = GitCommands().getDiff(parentCommit, currentCommit)

        GitCommands().checkout(parentCommit)

        # print(getGitLog())
        # # print(checkoutMaster())
        # print(getHeadHash())
        for diff in whatthepatch.parse_patch(patch):
            fileName = diff.header.old_path.split('/')[-1]

            if fileName.split(".")[-1] == 'java':
                if diff.changes == None:
                    continue
                for prevLine, currLine, text in diff.changes:
                    if currLine == None:
                        gitBlameText = GitCommands().gitBlame(
                            diff.header.old_path, prevLine, prevLine)
                        if gitBlameText != None:
                            parsedData = GitCommands().parseGitBlame(
                                gitBlameText)

                            if parentCommit not in bugMap:
                                bugMap[parentCommit] = {}

                            if diff.header.old_path not in bugMap[
                                    parentCommit]:
                                bugMap[parentCommit][diff.header.old_path] = [
                                    (prevLine, parsedData[0], parsedData[1])
                                ]
                            else:
                                bugMap[parentCommit][
                                    diff.header.old_path].append(
                                        (prevLine, parsedData[0],
                                         parsedData[1]))

        GitCommands().checkout('master')

        count += 1
        print("current count is at {}".format(count))

    return bugMap
Example #22
0
    def test_diff_ed(self):
        self.maxDiff = None
        with open('tests/casefiles/diff-ed.diff') as f:
            diff_text = f.read()

        diff = next(wtp.parse_patch(diff_text))

        new_text = wtp.apply.apply_diff(diff, self.lao)
        self.assertEqual(self.tzu,new_text)

        new_text = wtp.apply.apply_diff(diff, self.lao, use_patch=True)
        self.assertEqual(new_text, (self.tzu, None))
Example #23
0
def read_patch_data(patch_file_path):
    try:
        # noinspection PyPackageRequirements
        import whatthepatch
    except ImportError as e:
        raise ImportError('The --use-patch feature requires the whatthepatch library. Run "pip install --force-reinstall mutmut[patch]"') from e
    with open(patch_file_path) as f:
        diffs = whatthepatch.parse_patch(f.read())

    return {
        diff.header.new_path: {change.new for change in diff.changes if change.old is None}
        for diff in diffs
    }
Example #24
0
    def test_svn_git_patch(self):
        with open('tests/casefiles/svn-git.patch') as f:
            text = f.read()

        lines = text.splitlines()

        csc_diff = diffobj(
            header=headerobj(
                index_path='bugtrace/trunk/src/bugtrace/csc.py',
                old_path='projects/bugs/bugtrace/trunk/src/bugtrace/csc.py',
                old_version=12783,
                new_path='projects/bugs/bugtrace/trunk/src/bugtrace/csc.py',
                new_version=12784,
            ),
            changes=CSC_CHANGES,
            text='\n'.join(lines[:23]) + '\n'
        )

        diffxplore_path = 'bugtrace/trunk/src/bugtrace/Diffxplore.py'
        diffxplore_diff = diffobj(
            header=headerobj(
                index_path=diffxplore_path,
                old_path='projects/bugs/' + diffxplore_path,
                old_version=12783,
                new_path='projects/bugs/' + diffxplore_path,
                new_version=12784,
            ),
            changes=DIFFXPLORE_CHANGES,
            text='\n'.join(lines[23:42]) + '\n'
        )

        bugexplore_path = 'bugtrace/trunk/src/bugtrace/Bugxplore.py'
        bugxplore_diff = diffobj(
            header=headerobj(
                index_path=bugexplore_path,
                old_path='projects/bugs/' + bugexplore_path,
                old_version=12783,
                new_path='projects/bugs/' + bugexplore_path,
                new_version=12784,
            ),
            changes=BUGXPLORE_CHANGES,
            text='\n'.join(lines[42:]) + '\n'
        )

        expected = [csc_diff, diffxplore_diff, bugxplore_diff]

        results = list(wtp.parse_patch(text))

        self.assert_diffs_equal(results, expected)
Example #25
0
    def test_svn_git_patch(self):
        with open("tests/casefiles/svn-git.patch") as f:
            text = f.read()

        lines = text.splitlines()

        csc_diff = diffobj(
            header=headerobj(
                index_path="bugtrace/trunk/src/bugtrace/csc.py",
                old_path="projects/bugs/bugtrace/trunk/src/bugtrace/csc.py",
                old_version=12783,
                new_path="projects/bugs/bugtrace/trunk/src/bugtrace/csc.py",
                new_version=12784,
            ),
            changes=CSC_CHANGES,
            text="\n".join(lines[:23]) + "\n",
        )

        diffxplore_path = "bugtrace/trunk/src/bugtrace/Diffxplore.py"
        diffxplore_diff = diffobj(
            header=headerobj(
                index_path=diffxplore_path,
                old_path="projects/bugs/" + diffxplore_path,
                old_version=12783,
                new_path="projects/bugs/" + diffxplore_path,
                new_version=12784,
            ),
            changes=DIFFXPLORE_CHANGES,
            text="\n".join(lines[23:42]) + "\n",
        )

        bugexplore_path = "bugtrace/trunk/src/bugtrace/Bugxplore.py"
        bugxplore_diff = diffobj(
            header=headerobj(
                index_path=bugexplore_path,
                old_path="projects/bugs/" + bugexplore_path,
                old_version=12783,
                new_path="projects/bugs/" + bugexplore_path,
                new_version=12784,
            ),
            changes=BUGXPLORE_CHANGES,
            text="\n".join(lines[42:]) + "\n",
        )

        expected = [csc_diff, diffxplore_diff, bugxplore_diff]

        results = list(wtp.parse_patch(text))

        self.assert_diffs_equal(results, expected)
Example #26
0
def commit_autosuggestions(diffs, endpoint):
    commit_message = {}
    for idx, example in enumerate(whatthepatch.parse_patch(diffs)):
        if not example.changes:
            continue

        isadded, isdeleted = False, False
        added, deleted = [], []
        for change in example.changes:
            if change.old == None and change.new != None:
                added.append(change.line)
                isadded = True
            elif change.old != None and change.new == None:
                deleted.append(change.line)
                isdeleted = True

        # To speed up tokenizing request.
        added = tokenizing(" ".join(added), endpoint=endpoint)
        deleted = tokenizing(" ".join(deleted), endpoint=endpoint)

        _path = example.header.new_path \
            if example.header.new_path \
            else example.header.old_path
        if _path:
            if isadded and not isdeleted:
                data = {"idx": idx, "added": added, "deleted": deleted}
                res = requests.post(f'{endpoint}/added',
                                    data=json.dumps(data),
                                    headers={
                                        'Content-Type':
                                        'application/json; charset=utf-8'
                                    })
                commit = json.loads(res.text)
                commit_message[_path] = commit
            else:
                data = {"idx": idx, "added": added, "deleted": deleted}
                res = requests.post(f'{endpoint}/diff',
                                    data=json.dumps(data),
                                    headers={
                                        'Content-Type':
                                        'application/json; charset=utf-8'
                                    })
                commit = json.loads(res.text)
                commit_message[_path] = commit
    return commit_message
Example #27
0
    def test_svn_context_patch(self):
        with open("tests/casefiles/svn-context.patch") as f:
            text = f.read()

        lines = text.splitlines()

        expected = [
            diffobj(
                header=headerobj(
                    index_path="bugtrace/trunk/src/bugtrace/csc.py",
                    old_path="bugtrace/trunk/src/bugtrace/csc.py",
                    old_version=12783,
                    new_path="bugtrace/trunk/src/bugtrace/csc.py",
                    new_version=12784,
                ),
                changes=CSC_CHANGES,
                text="\n".join(lines[:32]) + "\n",
            ),
            diffobj(
                header=headerobj(
                    index_path="bugtrace/trunk/src/bugtrace/Diffxplore.py",
                    old_path="bugtrace/trunk/src/bugtrace/Diffxplore.py",
                    old_version=12783,
                    new_path="bugtrace/trunk/src/bugtrace/Diffxplore.py",
                    new_version=12784,
                ),
                changes=DIFFXPLORE_CHANGES,
                text="\n".join(lines[32:61]) + "\n",
            ),
            diffobj(
                header=headerobj(
                    index_path="bugtrace/trunk/src/bugtrace/Bugxplore.py",
                    old_path="bugtrace/trunk/src/bugtrace/Bugxplore.py",
                    old_version=12783,
                    new_path="bugtrace/trunk/src/bugtrace/Bugxplore.py",
                    new_version=12784,
                ),
                changes=BUGXPLORE_CHANGES,
                text="\n".join(lines[61:]) + "\n",
            ),
        ]

        results = list(wtp.parse_patch(text))

        self.assert_diffs_equal(results, expected)
Example #28
0
    def test_svn_context_patch(self):
        with open('tests/casefiles/svn-context.patch') as f:
            text = f.read()

        lines = text.splitlines()

        expected = [
            diffobj(
                header=headerobj(
                    index_path='bugtrace/trunk/src/bugtrace/csc.py',
                    old_path='bugtrace/trunk/src/bugtrace/csc.py',
                    old_version=12783,
                    new_path='bugtrace/trunk/src/bugtrace/csc.py',
                    new_version=12784,
                ),
                changes=CSC_CHANGES,
                text='\n'.join(lines[:32]) + '\n'
            ),
            diffobj(
                header=headerobj(
                    index_path='bugtrace/trunk/src/bugtrace/Diffxplore.py',
                    old_path='bugtrace/trunk/src/bugtrace/Diffxplore.py',
                    old_version=12783,
                    new_path='bugtrace/trunk/src/bugtrace/Diffxplore.py',
                    new_version=12784,
                ),
                changes=DIFFXPLORE_CHANGES,
                text='\n'.join(lines[32:61]) + '\n'
            ),
            diffobj(
                header=headerobj(
                    index_path='bugtrace/trunk/src/bugtrace/Bugxplore.py',
                    old_path='bugtrace/trunk/src/bugtrace/Bugxplore.py',
                    old_version=12783,
                    new_path='bugtrace/trunk/src/bugtrace/Bugxplore.py',
                    new_version=12784,
                ),
                changes=BUGXPLORE_CHANGES,
                text='\n'.join(lines[61:]) + '\n'
            ),
        ]

        results = list(wtp.parse_patch(text))

        self.assert_diffs_equal(results, expected)
    def run(patch):
        # Initialize changes
        changes = CommitDiff()
        
        # Parse each file
        for diff in whatthepatch.parse_patch(patch):
            # Check file extenstion for .c or .h
            filename = diff[0][3]
            if not Parser.checkFileExtension(filename):
                continue

            # Create new file diff
            file = Parser.parseFileChanges(diff)

            # Add file changes to overall diff
            changes.addFile(file)

        return changes
Example #30
0
    def test_git_oneline_rm(self):
        with open('tests/casefiles/git-oneline-rm.diff') as f:
            text = f.read()

        lines = text.splitlines()

        expected = [
            diffobj(header=headerobj(index_path=None,
                                     old_path='oneline.txt',
                                     old_version='169ceeb',
                                     new_path='/dev/null',
                                     new_version='0000000'),
                    changes=[(1, None, 'Changed a one-line file.')],
                    text='\n'.join(lines[:34]) + '\n')
        ]

        results = list(wtp.parse_patch(text))
        self.assert_diffs_equal(results, expected)
Example #31
0
def load_patch(patch_text):
    patch = [diff for diff in whatthepatch.parse_patch(patch_text)]

    status_header = collections.namedtuple(
        'header', whatthepatch.patch.header._fields + ('path', 'status'))

    patch = [
        diff._replace(header=status_header(index_path=diff.header.index_path,
                                           old_path=diff.header.old_path,
                                           old_version=diff.header.old_version,
                                           new_path=diff.header.new_path,
                                           new_version=diff.header.new_version,
                                           path=diff.header.new_path,
                                           status='unchanged'))
        for diff in patch
    ]

    return patch
Example #32
0
def get_potential_vccs(pygithub, apitoken, reponame, vfc_id):

    # Get repo
    repo = pygithub.get_repo(reponame)

    # Get changed files in vulerability fixing commit
    vfc = repo.get_commit(vfc_id)
    vfc_raw = vfc.raw_data
    vfc_files = vfc_raw['files']
    vfc_oid = vfc_raw['sha']

    # Get additions, deletions and replacements patch
    candidates = []
    for f in vfc_files:

        # Parse patches and only keep added/removed/modified lines
        wtp = [i for i in parse_patch(f['patch'])][0].changes
        diffs = pd.DataFrame(wtp)
        rmdiffs = diffs[(diffs.new.isna()) | (diffs.old.isna())]

        # Skip if there are no removed or modified lines in file
        if (len(rmdiffs.old.dropna()) < 1): continue

        # Get blame of file on left side
        blame = get_blame(reponame, vfc_oid, f['filename'], apitoken)
        blamedf = pd.DataFrame(pd.json_normalize(blame))

        # Get commit associated with remove/modified line
        blame_commits = []
        for rline in rmdiffs.old.dropna().tolist():
            bcommit = blamedf[(blamedf.startingLine <= rline)
                              & (blamedf.endingLine >= rline)]
            bcommit['old'] = rline
            blame_commits.append(bcommit)

        blamecommitsdf = pd.concat(blame_commits).set_index('old')
        final = rmdiffs.set_index('old').join(blamecommitsdf).reset_index()

        # Set meta info and add to candidate info
        final['Filename'] = f['filename']
        final['Status'] = f['status']
        candidates.append(final)

    return pd.concat(candidates)
Example #33
0
def line_removed(target_line, commit):
    """Given a commit tell if target_line was added or removed.

    True if line was removed
    False if added
    None if target_line wasn't found at all (because not a full line etc.)
    """
    cmd = 'git', 'log', '-1', '--format=', '-p', str(commit)
    diff = run_command(*cmd)
    for diff in whatthepatch.parse_patch(diff):
        for line in diff.changes:
            if target_line in line[2]:
                if line[0] is None:
                    # Line added
                    return False
                elif line[1] is None:
                    # Line removed
                    return True
    # target_line matched part of a line instead of a full line
    return None
Example #34
0
def line_removed(target_line, commit):
    """Given a commit tell if target_line was added or removed.

    True if line was removed
    False if added
    None if target_line wasn't found at all (because not a full line etc.)
    """
    cmd = 'git', 'log', '-1', '--format=', '-p', str(commit)
    diff = run_command(*cmd)
    for diff in whatthepatch.parse_patch(diff):
        for line in diff.changes:
            if target_line in line[2]:
                if line[0] is None:
                    # Line added
                    return False
                elif line[1] is None:
                    # Line removed
                    return True
    # target_line matched part of a line instead of a full line
    return None
Example #35
0
    def test_git_new_empty_file(self):
        with open('tests/casefiles/git-new-empty-file.diff') as f:
            text = f.read()

        lines = text.splitlines()

        expected = [
            diffobj(header=headerobj(
                index_path=None,
                old_path='/dev/null',
                old_version='0000000',
                new_path='somefile.txt',
                new_version='e69de29',
            ),
                    changes=[],
                    text='\n'.join(lines[:34]) + '\n')
        ]

        results = list(wtp.parse_patch(text))
        self.assert_diffs_equal(results, expected)
 def __change_color(self, orig_src, new_src, diff):
     # Step 1. Parsing patch
     diff_list = whatthepatch.parse_patch(diff)
     # Step 2. Iterate each of patch file (it may be more than one)
     for diff in diff_list:
         for s, d, v in diff.changes:
             # Step 1. Orig have no line (Insert new line)
             if s is None:
                 new_src.set_line_class(d, "bg-success")
             # Step 2. New habe no line (Remove exist line)
             elif d is None:
                 orig_src.set_line_class(s, "bg-danger")
             # Step 3. Other change
             elif s != d:
                 orig_src.set_line_class(s, "bg-info")
                 new_src.set_line_class(d, "bg-info")
             elif s == d:
                 pass
             else:
                 pass
def _get_modified_lines(_file):
    # Gets the diff from GIT
    diff_patch = subprocess.check_output(
        'git diff --cached %s' % _file,
        shell=True
    )

    # Parse the patch-format and get only added or modified
    diff = [x for x in whatthepatch.parse_patch(diff_patch)]
    if not diff:
        return []

    errors = []
    for old, new, content in diff[0].changes:
        if old is None:
            errors.append(new)
            if content == '':  # if content is blank line, add next line
                errors.append(new + 1)

    return errors
Example #38
0
def read_patch_data(patch_file_path):
    print('Using patch data from ' + patch_file_path)
    try:
        # noinspection PyPackageRequirements
        import whatthepatch
    except ImportError:
        print(
            'The --use-patch feature requires the whatthepatch library. Run "pip install whatthepatch"',
            file=sys.stderr)
        raise
    with open(patch_file_path) as f:
        diffs = whatthepatch.parse_patch(f.read())

    return {
        diff.header.new_path: {
            line_number
            for old_line_number, line_number, text in diff.changes
            if old_line_number is None
        }
        for diff in diffs
    }
Example #39
0
def load_patch(patch_text):
    patch = [diff for diff in whatthepatch.parse_patch(patch_text)]

    status_header = collections.namedtuple(
        'header',
        whatthepatch.patch.header._fields + ('path', 'status')
    )

    patch = [
        diff._replace(header=status_header(
            index_path=diff.header.index_path,
            old_path=diff.header.old_path,
            old_version=diff.header.old_version,
            new_path=diff.header.new_path,
            new_version=diff.header.new_version,
            path=diff.header.new_path,
            status='unchanged'
        ))
    for diff in patch]

    return patch
	def analyze_diff(self, diff):
		def status(change):
			if change[0] is None:
				return 'insert'
			elif change[1] is None:
				return 'delete'
			else:
				return 'equal'
		out = []
		for hunk in whatthepatch.parse_patch(diff.decode('utf-8')):
			file_changes = []
			in_changeset = False
			for mode in [('insert',1), ('delete',0)]:
				for change in hunk.changes:
					if status(change) == mode[0] and not in_changeset:
						in_changeset = True
						file_changes.append({'start':change[mode[1]], 'type':mode[0]})
					elif status(change) == 'equal' and in_changeset:
						in_changeset = False
						file_changes[-1]['end'] = change[mode[1]]-1
			out.append({'header':hunk.header, 'changes':file_changes})
		return out
Example #41
0
    def test_svn_unified_patch(self):
        with open('tests/casefiles/svn-unified.patch') as f:
            text = f.read()

        lines = text.splitlines()

        expected = [
            diffobj(header=headerobj(
                index_path='bugtrace/trunk/src/bugtrace/csc.py',
                old_path='bugtrace/trunk/src/bugtrace/csc.py',
                old_version=12783,
                new_path='bugtrace/trunk/src/bugtrace/csc.py',
                new_version=12784,
            ),
                    changes=CSC_CHANGES,
                    text='\n'.join(lines[:22]) + '\n'),
            diffobj(header=headerobj(
                index_path='bugtrace/trunk/src/bugtrace/Diffxplore.py',
                old_path='bugtrace/trunk/src/bugtrace/Diffxplore.py',
                old_version=12783,
                new_path='bugtrace/trunk/src/bugtrace/Diffxplore.py',
                new_version=12784,
            ),
                    changes=DIFFXPLORE_CHANGES,
                    text='\n'.join(lines[22:40]) + '\n'),
            diffobj(header=headerobj(
                index_path='bugtrace/trunk/src/bugtrace/Bugxplore.py',
                old_path='bugtrace/trunk/src/bugtrace/Bugxplore.py',
                old_version=12783,
                new_path='bugtrace/trunk/src/bugtrace/Bugxplore.py',
                new_version=12784,
            ),
                    changes=BUGXPLORE_CHANGES,
                    text='\n'.join(lines[40:]) + '\n')
        ]

        results = list(wtp.parse_patch(text))

        self.assert_diffs_equal(results, expected)
Example #42
0
    def test_git_new_empty_file(self):
        with open('tests/casefiles/git-new-empty-file.diff') as f:
            text = f.read()

        lines = text.splitlines()

        expected = [
            diffobj(
                header=headerobj(
                    index_path=None,
                    old_path='/dev/null',
                    old_version='0000000',
                    new_path='somefile.txt',
                    new_version='e69de29',
                ),
                changes=[],
                text='\n'.join(lines[:34]) + '\n'
            )
        ]

        results = list(wtp.parse_patch(text))
        self.assert_diffs_equal(results, expected)
Example #43
0
    def test_git_new_empty_file(self):
        with open("tests/casefiles/git-new-empty-file.diff") as f:
            text = f.read()

        lines = text.splitlines()

        expected = [
            diffobj(
                header=headerobj(
                    index_path=None,
                    old_path="/dev/null",
                    old_version="0000000",
                    new_path="somefile.txt",
                    new_version="e69de29",
                ),
                changes=[],
                text="\n".join(lines[:34]) + "\n",
            )
        ]

        results = list(wtp.parse_patch(text))
        self.assert_diffs_equal(results, expected)
Example #44
0
    def patch_analysis(self, patch):
        info = {'size': 0, 'test_size': 0, 'addlines': 0, 'rmlines': 0}

        for diff in whatthepatch.parse_patch(patch):
            if diff.header and diff.changes:
                h = diff.header
                new_path = h.new_path[2:] if h.new_path.startswith(
                    'b/') else h.new_path

                # Calc changes additions & deletions
                counts = [(old is None and new is not None, new is None
                           and old is not None)
                          for old, new, _ in diff.changes]
                counts = list(zip(*counts))  # inverse zip
                info['addlines'] += sum(counts[0])
                info['rmlines'] += sum(counts[1])

                if utils.is_test_file(new_path):
                    info['test_size'] += len(diff.changes)
                else:
                    info['size'] += len(diff.changes)

        return info
    def patch_analysis(self, patch):
        info = {'size': 0, 'test_size': 0, 'addlines': 0, 'rmlines': 0}

        for diff in whatthepatch.parse_patch(patch):
            if diff.header and diff.changes:
                h = diff.header
                new_path = h.new_path[2:] if h.new_path.startswith('b/') else h.new_path

                # Calc changes additions & deletions
                counts = [
                    (old is None and new is not None, new is None and old is not None)
                    for old, new, _ in diff.changes
                ]
                counts = list(zip(*counts))  # inverse zip
                info['addlines'] += sum(counts[0])
                info['rmlines'] += sum(counts[1])

                if utils.is_test_file(new_path):
                    info['test_size'] += len(diff.changes)
                else:
                    info['size'] += len(diff.changes)

        return info
Example #46
0
def get_commits_from_patch(patch, args):
    """Get the set of commits associated with added lines in the patch."""
    commits = set()

    for diff in whatthepatch.parse_patch(patch):
        for change in diff.changes:
            # Ignore lines that aren't insertions
            if change.old is not None or change.new is None:
                continue

            cmd = ('git', '-C', args.repo, 'blame',
                   '-L{0},{0}'.format(change.new), args.modified_rev,
                   diff.header.new_path)
            output = subprocess.run(cmd,
                                    capture_output=True,
                                    check=True,
                                    text=True)
            # Output looks like this:
            #
            # f9aa76a852485 (Dave Airlie 2012-04-17 14:12:29 +0100 132) 	.name = DRIVER_NAME,
            #
            # Discard everything from the first close paren on, then
            # get the commit hash and the data portion of the
            # output. (I'm not sure whether this is author date or
            # commit date.)
            header = output.stdout.split(')')[0]
            parts = header.split()
            commit_hash = parts[0]
            commit_date = parts[-4]
            commit_time = parts[-3]
            commit_zone = parts[-2]

            commit_datetime = '{}T{}{}'.format(commit_date, commit_time,
                                               commit_zone)
            commits.add((commit_datetime, commit_hash))

    return commits
Example #47
0
def _apply(src, diff_text, reverse=False, use_patch=False):
    diff = next(wtp.parse_patch(diff_text))
    return wtp.apply.apply_diff(diff, src, reverse, use_patch)
Example #48
0
    def test_svn_rcs_patch(self):
        with open('tests/casefiles/svn-rcs.patch') as f:
            text = f.read()

        lines = text.splitlines()

        csc_changes = [
            (None, 1, '# This is a basic script I wrote to run '
                      'Bugxplore over the dataset'),
            (None, 2, ''),
            (None, 3, ''),
            (8, None, None),
            (9, None, None),
            (None, 11, 'from Bugxplore import main'),
            (None, 12, 'from Bugxplore import _make_dir'),
        ]

        diffxplore_changes = [
            (49, None, None),
            (None, 49, "    optparser.set_defaults(output_dir='/tmp/diffs')"),
            (53, None, None),
            (None, 53, ''),
        ]

        bugxplore_changes = [
            (86, None, None),
            (None, 86, "    optparser.set_defaults(output_dir='/tmp/bugs')"),
            (91, None, None),
            (None, 91, ''),
        ]

        expected = [
            diffobj(
                header=headerobj(
                    index_path='bugtrace/trunk/src/bugtrace/csc.py',
                    old_path='bugtrace/trunk/src/bugtrace/csc.py',
                    old_version=None,
                    new_path='bugtrace/trunk/src/bugtrace/csc.py',
                    new_version=None,
                ),
                changes=csc_changes,
                text='\n'.join(lines[:10]) + '\n'
            ),
            diffobj(
                header=headerobj(
                    index_path='bugtrace/trunk/src/bugtrace/Diffxplore.py',
                    old_path='bugtrace/trunk/src/bugtrace/Diffxplore.py',
                    old_version=None,
                    new_path='bugtrace/trunk/src/bugtrace/Diffxplore.py',
                    new_version=None,
                ),
                changes=diffxplore_changes,
                text='\n'.join(lines[10:18]) + '\n'
            ),
            diffobj(
                header=headerobj(
                    index_path='bugtrace/trunk/src/bugtrace/Bugxplore.py',
                    old_path='bugtrace/trunk/src/bugtrace/Bugxplore.py',
                    old_version=None,
                    new_path='bugtrace/trunk/src/bugtrace/Bugxplore.py',
                    new_version=None,
                ),
                changes=bugxplore_changes,
                text='\n'.join(lines[18:]) + '\n'
            ),
        ]

        results = list(wtp.parse_patch(text))
        self.assert_diffs_equal(results, expected)
Example #49
0
    def test_svn_default_patch(self):
        with open('tests/casefiles/svn-default.patch') as f:
            text = f.read()

        lines = text.splitlines()

        csc_changes = [
            (None, 1, '# This is a basic script I wrote to run '
                      'Bugxplore over the dataset'),
            (None, 2, ''),
            (None, 3, ''),
            (8, None, 'from Main import main'),
            (9, None, 'from Main import _make_dir'),
            (None, 11, 'from Bugxplore import main'),
            (None, 12, 'from Bugxplore import _make_dir'),
        ]

        diffxplore_changes = indent(4, [
            (49, None, "optparser.set_defaults(output_dir='/tmp/sctdiffs',"
                       "project_name='default_project')"),
            (None, 49, "optparser.set_defaults(output_dir='/tmp/diffs')"),
            (53, None, "optparser.add_option('-a', '--append', "
                       "action='store_true', dest='app', default=False, "
                       "help='Append to existing MethTerms2 document')"),
            (None, 53, ''),
        ])

        bugxplore_changes = indent(4, [
            (86, None, "optparser.set_defaults(output_dir='/tmp/bugs',"
                       "project_name='default_project')"),
            (None, 86, "optparser.set_defaults(output_dir='/tmp/bugs')"),
            (91, None, "optparser.add_option('-a', '--append', "
                       "action='store_true', dest='app', default=False, "
                       "help='Append to existing MethTerms2 document')"),
            (None, 91, ''),
        ])

        expected = [
            diffobj(
                header=headerobj(
                    index_path='bugtrace/trunk/src/bugtrace/csc.py',
                    old_path='bugtrace/trunk/src/bugtrace/csc.py',
                    old_version=None,
                    new_path='bugtrace/trunk/src/bugtrace/csc.py',
                    new_version=None,
                ),
                changes=csc_changes,
                text='\n'.join(lines[:12]) + '\n'
            ),
            diffobj(
                header=headerobj(
                    index_path='bugtrace/trunk/src/bugtrace/Diffxplore.py',
                    old_path='bugtrace/trunk/src/bugtrace/Diffxplore.py',
                    old_version=None,
                    new_path='bugtrace/trunk/src/bugtrace/Diffxplore.py',
                    new_version=None,
                ),
                changes=diffxplore_changes,
                text='\n'.join(lines[12:22]) + '\n'
            ),
            diffobj(
                header=headerobj(
                    index_path='bugtrace/trunk/src/bugtrace/Bugxplore.py',
                    old_path='bugtrace/trunk/src/bugtrace/Bugxplore.py',
                    old_version=None,
                    new_path='bugtrace/trunk/src/bugtrace/Bugxplore.py',
                    new_version=None,
                ),
                changes=bugxplore_changes,
                text='\n'.join(lines[22:]) + '\n'
            )
        ]
        results = list(wtp.parse_patch(text))
        self.assert_diffs_equal(results, expected)
Example #50
0
    def test_git_patch(self):
        with open('tests/casefiles/git.patch') as f:
            text = f.read()

        lines = text.splitlines()

        novel_frame_changes = indent(4, [
            (135, 135, 'public void actionPerformed(ActionEvent e) {'),
            (136, 136, ''),
            (137, 137, '    if (e.getActionCommand().equals("OPEN")) {'),
            (138, None, '        prefsDialog(prefs.getImportPane());'),
            (None, 138, '        prefs.selectImportPane();'),
            (None, 139, '        prefsDialog();'),
            (139, 140, '    } else if (e.getActionCommand().equals("SET")) {'),
            (140, None, '        prefsDialog(prefs.getRepoPane());'),
            (None, 141, '        prefs.selectRepoPane();'),
            (None, 142, '        prefsDialog();'),
            (141, 143, '    } else if (e.getActionCommand().equals("PREFS"))'),
            (142, 144, '        prefsDialog();'),
            (143, 145, '    else if (e.getActionCommand().equals("EXIT"))'),
            (158, 160, ' * Create dialog to handle user preferences'),
            (159, 161, ' */'),
            (160, 162, 'public void prefsDialog() {'),
            (161, None, ''),
            (162, 163, '    prefs.setVisible(true);'),
            (163, 164, '}'),
            (164, 165, ''),
            (165, None, 'public void prefsDialog(Component c) {'),
            (166, None, '    prefs.setSelectedComponent(c);'),
            (167, None, '    prefsDialog();'),
            (168, None, '}'),
            (169, None, ''),
            (170, 166, '/**'),
            (171, 167, ' * Open software tutorials, '
                       'most likely to be hosted online'),
            (172, 168, ' * ')
        ])

        novel_frame_path = (
            'novel/src/java/edu/ua/eng/software/novel/NovelFrame.java'
        )
        novel_frame = diffobj(
            header=headerobj(
                index_path=None,
                old_path=novel_frame_path,
                old_version='aae63fe',
                new_path=novel_frame_path,
                new_version='5abbc99'
            ),
            changes=novel_frame_changes,
            text='\n'.join(lines[:34]) + '\n'
        )

        novel_pref_frame_path = (
            'novel/src/java/edu/ua/eng/software/novel/NovelPrefPane.java'
        )

        novel_pref_frame = diffobj(
            header=headerobj(
                index_path=None,
                old_path=novel_pref_frame_path,
                old_version='a63b57e',
                new_path=novel_pref_frame_path,
                new_version='919f413'
            ),
            changes=[
                (18, 18, ''),
                (19, 19, '    public abstract void apply();'),
                (20, 20, ''),
                (None, 21, '    public abstract void applyPrefs();'),
                (None, 22, ''),
                (21, 23, '    public abstract boolean isChanged();'),
                (22, 24, ''),
                (23, 25, '    protected Preferences prefs;')],
            text='\n'.join(lines[34:]) + '\n',
        )

        expected = [novel_frame, novel_pref_frame]

        results = list(wtp.parse_patch(text))

        self.assert_diffs_equal(results, expected)
Example #51
0
 def post(self, request):
     return self.render_to_response({'diffs': whatthepatch.parse_patch(request.POST['diff'])})