<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;">from collections import defaultdict
from copy import deepcopy

from rbtools.hooks.common import execute, get_review_request_id


def get_branch_name(ref_name):
    """Returns the branch name corresponding to the specified ref name."""
    branch_ref_prefix = 'refs/heads/'

    if ref_name.startswith(branch_ref_prefix):
        return ref_name[len(branch_ref_prefix):]

def get_commit_hashes(old_rev, new_rev):
    """Returns a list of abbreviated commit hashes from old_rev to new_rev."""
    git_command = ('git rev-list --abbrev-commit --reverse %s..%s'
                   % (old_rev, new_rev))
    return execute(git_command).split('\n')

def get_unique_commit_hashes(ref_name):
    """Returns a list of abbreviated commit hashes unique to ref_name."""
    excluded_branches = get_excluded_branches(ref_name)
    git_command = ('git rev-list %s --abbrev-commit --reverse --not %s'
                   % (ref_name, excluded_branches))
    return execute(git_command).strip().split('\n')

def get_excluded_branches(ref_name):
    """Returns a string with all branches, excluding the specified branch."""
    git_command = ('git for-each-ref refs/heads/ --format="%%(refname)" '
                   '| grep -v %s' % ref_name)
    return execute(git_command).replace('\n', ' ')

def get_branches_containing_commit(commit_hash):
    """Returns a list of all branches containing the specified commit."""
    git_command = ('git branch --contains %s' % commit_hash)
    branches = execute(git_command).replace('*', '').split('\n')
    return [branch.strip() for branch in branches]

def get_commit_message(commit):
    """Returns the specified commit's commit message."""
    git_command = ('git show -s --pretty=format:"%%B" %s' % commit)
    return execute(git_command).strip()

def get_review_id_to_commits_map(lines, regex):
    """Returns a dictionary, mapping a review request ID to a list of commits.

    The commits must be in the form: oldrev newrev refname (separated by
    newlines), as given by a Git pre-receive or post-receive hook.

    If a commit's commit message does not contain a review request ID, we append
    the commit to the key '0'.
    """
    review_id_to_commits_map = defaultdict(list)

    # Store a list of new branches (which have an all-zero old_rev value)
    # created in this push to handle them specially.
    new_branches = []
    null_sha1 = '0000000000000000000000000000000000000000'

    for line in lines:
        old_rev, new_rev, ref_name = line.split()
        branch_name = get_branch_name(ref_name)

        if not branch_name or new_rev == null_sha1:
            continue

        if old_rev == null_sha1:
            new_branches.append(branch_name)
            commit_hashes = get_unique_commit_hashes(ref_name)
        else:
            commit_hashes = get_commit_hashes(old_rev, new_rev)

        for commit_hash in commit_hashes:

            if commit_hash:
                commit_message = get_commit_message(commit_hash)
                review_request_id = get_review_request_id(regex, commit_message)

                if not review_request_id:
                    review_request_id = '0'

                commit = '%s (%s)' % (branch_name, commit_hash)
                review_id_to_commits_map[review_request_id].append(commit)

    # If there are new branches, check every commit in the dictionary
    # (corresponding to only old branches) to see if the new branches also
    # contain that commit.
    if new_branches:
        review_id_to_commits_map_copy = deepcopy(review_id_to_commits_map)

        for review_id, commit_list in review_id_to_commits_map_copy.iteritems():

            for commit in commit_list:
                commit_branch = commit[:commit.find('(') - 1]

                if commit_branch in new_branches:
                    continue

                commit_hash = commit[commit.find('(') + 1:-1]
                commit_branches = get_branches_containing_commit(commit_hash)

                for branch in set(new_branches).intersection(commit_branches):
                    new_commit = '%s (%s)' % (branch, commit_hash)
                    review_id_to_commits_map[review_id].append(new_commit)

    return review_id_to_commits_map</pre></body></html>