xref: /aosp_15_r20/external/mesa3d/bin/commit_in_branch.py (revision 6104692788411f58d303aa86923a9ff6ecaded22)
1*61046927SAndroid Build Coastguard Worker#!/usr/bin/env python3
2*61046927SAndroid Build Coastguard Worker
3*61046927SAndroid Build Coastguard Workerimport argparse
4*61046927SAndroid Build Coastguard Workerimport subprocess
5*61046927SAndroid Build Coastguard Workerimport sys
6*61046927SAndroid Build Coastguard Worker
7*61046927SAndroid Build Coastguard Worker
8*61046927SAndroid Build Coastguard Workerdef print_(args: argparse.Namespace, success: bool, message: str) -> None:
9*61046927SAndroid Build Coastguard Worker    """
10*61046927SAndroid Build Coastguard Worker    Print function with extra coloring when supported and/or requested,
11*61046927SAndroid Build Coastguard Worker    and with a "quiet" switch
12*61046927SAndroid Build Coastguard Worker    """
13*61046927SAndroid Build Coastguard Worker
14*61046927SAndroid Build Coastguard Worker    COLOR_SUCCESS = '\033[32m'
15*61046927SAndroid Build Coastguard Worker    COLOR_FAILURE = '\033[31m'
16*61046927SAndroid Build Coastguard Worker    COLOR_RESET = '\033[0m'
17*61046927SAndroid Build Coastguard Worker
18*61046927SAndroid Build Coastguard Worker    if args.quiet:
19*61046927SAndroid Build Coastguard Worker        return
20*61046927SAndroid Build Coastguard Worker
21*61046927SAndroid Build Coastguard Worker    if args.color == 'auto':
22*61046927SAndroid Build Coastguard Worker        use_colors = sys.stdout.isatty()
23*61046927SAndroid Build Coastguard Worker    else:
24*61046927SAndroid Build Coastguard Worker        use_colors = args.color == 'always'
25*61046927SAndroid Build Coastguard Worker
26*61046927SAndroid Build Coastguard Worker    s = ''
27*61046927SAndroid Build Coastguard Worker    if use_colors:
28*61046927SAndroid Build Coastguard Worker        if success:
29*61046927SAndroid Build Coastguard Worker            s += COLOR_SUCCESS
30*61046927SAndroid Build Coastguard Worker        else:
31*61046927SAndroid Build Coastguard Worker            s += COLOR_FAILURE
32*61046927SAndroid Build Coastguard Worker
33*61046927SAndroid Build Coastguard Worker    s += message
34*61046927SAndroid Build Coastguard Worker
35*61046927SAndroid Build Coastguard Worker    if use_colors:
36*61046927SAndroid Build Coastguard Worker        s += COLOR_RESET
37*61046927SAndroid Build Coastguard Worker
38*61046927SAndroid Build Coastguard Worker    print(s)
39*61046927SAndroid Build Coastguard Worker
40*61046927SAndroid Build Coastguard Worker
41*61046927SAndroid Build Coastguard Workerdef is_commit_valid(commit: str) -> bool:
42*61046927SAndroid Build Coastguard Worker    ret = subprocess.call(['git', 'cat-file', '-e', commit],
43*61046927SAndroid Build Coastguard Worker                          stdout=subprocess.DEVNULL,
44*61046927SAndroid Build Coastguard Worker                          stderr=subprocess.DEVNULL)
45*61046927SAndroid Build Coastguard Worker    return ret == 0
46*61046927SAndroid Build Coastguard Worker
47*61046927SAndroid Build Coastguard Worker
48*61046927SAndroid Build Coastguard Workerdef branch_has_commit(upstream_branch: str, commit: str) -> bool:
49*61046927SAndroid Build Coastguard Worker    """
50*61046927SAndroid Build Coastguard Worker    Returns True if the commit is actually present in the branch
51*61046927SAndroid Build Coastguard Worker    """
52*61046927SAndroid Build Coastguard Worker    ret = subprocess.call(['git', 'merge-base', '--is-ancestor',
53*61046927SAndroid Build Coastguard Worker                           commit, upstream_branch],
54*61046927SAndroid Build Coastguard Worker                          stdout=subprocess.DEVNULL,
55*61046927SAndroid Build Coastguard Worker                          stderr=subprocess.DEVNULL)
56*61046927SAndroid Build Coastguard Worker    return ret == 0
57*61046927SAndroid Build Coastguard Worker
58*61046927SAndroid Build Coastguard Worker
59*61046927SAndroid Build Coastguard Workerdef branch_has_backport_of_commit(upstream_branch: str, commit: str) -> str:
60*61046927SAndroid Build Coastguard Worker    """
61*61046927SAndroid Build Coastguard Worker    Returns the commit hash if the commit has been backported to the branch,
62*61046927SAndroid Build Coastguard Worker    or an empty string if is hasn't
63*61046927SAndroid Build Coastguard Worker    """
64*61046927SAndroid Build Coastguard Worker    upstream, _ = upstream_branch.split('/', 1)
65*61046927SAndroid Build Coastguard Worker
66*61046927SAndroid Build Coastguard Worker    out = subprocess.check_output(['git', 'log', '--format=%H',
67*61046927SAndroid Build Coastguard Worker                                   upstream + '..' + upstream_branch,
68*61046927SAndroid Build Coastguard Worker                                   '--grep', 'cherry picked from commit ' + commit],
69*61046927SAndroid Build Coastguard Worker                                  stderr=subprocess.DEVNULL)
70*61046927SAndroid Build Coastguard Worker    return out.decode().strip()
71*61046927SAndroid Build Coastguard Worker
72*61046927SAndroid Build Coastguard Worker
73*61046927SAndroid Build Coastguard Workerdef canonicalize_commit(commit: str) -> str:
74*61046927SAndroid Build Coastguard Worker    """
75*61046927SAndroid Build Coastguard Worker    Takes a commit-ish and returns a commit sha1 if the commit exists
76*61046927SAndroid Build Coastguard Worker    """
77*61046927SAndroid Build Coastguard Worker
78*61046927SAndroid Build Coastguard Worker    # Make sure input is valid first
79*61046927SAndroid Build Coastguard Worker    if not is_commit_valid(commit):
80*61046927SAndroid Build Coastguard Worker        raise argparse.ArgumentTypeError('invalid commit identifier: ' + commit)
81*61046927SAndroid Build Coastguard Worker
82*61046927SAndroid Build Coastguard Worker    out = subprocess.check_output(['git', 'rev-parse', commit],
83*61046927SAndroid Build Coastguard Worker                                  stderr=subprocess.DEVNULL)
84*61046927SAndroid Build Coastguard Worker    return out.decode().strip()
85*61046927SAndroid Build Coastguard Worker
86*61046927SAndroid Build Coastguard Worker
87*61046927SAndroid Build Coastguard Workerdef validate_branch(branch: str) -> str:
88*61046927SAndroid Build Coastguard Worker    if '/' not in branch:
89*61046927SAndroid Build Coastguard Worker        raise argparse.ArgumentTypeError('must be in the form `remote/branch`')
90*61046927SAndroid Build Coastguard Worker
91*61046927SAndroid Build Coastguard Worker    out = subprocess.check_output(['git', 'remote', '--verbose'],
92*61046927SAndroid Build Coastguard Worker                                  stderr=subprocess.DEVNULL)
93*61046927SAndroid Build Coastguard Worker    remotes = out.decode().splitlines()
94*61046927SAndroid Build Coastguard Worker    upstream, _ = branch.split('/', 1)
95*61046927SAndroid Build Coastguard Worker    valid_remote = False
96*61046927SAndroid Build Coastguard Worker    for line in remotes:
97*61046927SAndroid Build Coastguard Worker        if line.startswith(upstream + '\t'):
98*61046927SAndroid Build Coastguard Worker            valid_remote = True
99*61046927SAndroid Build Coastguard Worker
100*61046927SAndroid Build Coastguard Worker    if not valid_remote:
101*61046927SAndroid Build Coastguard Worker        raise argparse.ArgumentTypeError('Invalid remote: ' + upstream)
102*61046927SAndroid Build Coastguard Worker
103*61046927SAndroid Build Coastguard Worker    if not is_commit_valid(branch):
104*61046927SAndroid Build Coastguard Worker        raise argparse.ArgumentTypeError('Invalid branch: ' + branch)
105*61046927SAndroid Build Coastguard Worker
106*61046927SAndroid Build Coastguard Worker    return branch
107*61046927SAndroid Build Coastguard Worker
108*61046927SAndroid Build Coastguard Worker
109*61046927SAndroid Build Coastguard Workerif __name__ == "__main__":
110*61046927SAndroid Build Coastguard Worker    parser = argparse.ArgumentParser(description="""
111*61046927SAndroid Build Coastguard Worker    Returns 0 if the commit is present in the branch,
112*61046927SAndroid Build Coastguard Worker    1 if it's not,
113*61046927SAndroid Build Coastguard Worker    and 2 if it couldn't be determined (eg. invalid commit)
114*61046927SAndroid Build Coastguard Worker    """)
115*61046927SAndroid Build Coastguard Worker    parser.add_argument('commit',
116*61046927SAndroid Build Coastguard Worker                        type=canonicalize_commit,
117*61046927SAndroid Build Coastguard Worker                        help='commit sha1')
118*61046927SAndroid Build Coastguard Worker    parser.add_argument('branch',
119*61046927SAndroid Build Coastguard Worker                        type=validate_branch,
120*61046927SAndroid Build Coastguard Worker                        help='branch to check, in the form `remote/branch`')
121*61046927SAndroid Build Coastguard Worker    parser.add_argument('--quiet',
122*61046927SAndroid Build Coastguard Worker                        action='store_true',
123*61046927SAndroid Build Coastguard Worker                        help='suppress all output; exit code can still be used')
124*61046927SAndroid Build Coastguard Worker    parser.add_argument('--color',
125*61046927SAndroid Build Coastguard Worker                        choices=['auto', 'always', 'never'],
126*61046927SAndroid Build Coastguard Worker                        default='auto',
127*61046927SAndroid Build Coastguard Worker                        help='colorize output (default: true if stdout is a terminal)')
128*61046927SAndroid Build Coastguard Worker    args = parser.parse_args()
129*61046927SAndroid Build Coastguard Worker
130*61046927SAndroid Build Coastguard Worker    if branch_has_commit(args.branch, args.commit):
131*61046927SAndroid Build Coastguard Worker        print_(args, True, 'Commit ' + args.commit + ' is in branch ' + args.branch)
132*61046927SAndroid Build Coastguard Worker        exit(0)
133*61046927SAndroid Build Coastguard Worker
134*61046927SAndroid Build Coastguard Worker    backport = branch_has_backport_of_commit(args.branch, args.commit)
135*61046927SAndroid Build Coastguard Worker    if backport:
136*61046927SAndroid Build Coastguard Worker        print_(args, True,
137*61046927SAndroid Build Coastguard Worker               'Commit ' + args.commit + ' was backported to branch ' + args.branch + ' as commit ' + backport)
138*61046927SAndroid Build Coastguard Worker        exit(0)
139*61046927SAndroid Build Coastguard Worker
140*61046927SAndroid Build Coastguard Worker    print_(args, False, 'Commit ' + args.commit + ' is NOT in branch ' + args.branch)
141*61046927SAndroid Build Coastguard Worker    exit(1)
142