third_party_mesa3d/bin/commit_in_branch.py
Eric Engestrom 7f61f4180b introduce commit_in_branch.py script to help devs figure this out
It's been pointed out to me that determining whether a commit is present
in a stable branch is non-trivial (cherry-picks are a pain to search for)
and the commands are hard to remember, making it too much to ask.

This script aims to solve that problem; at its simplest form, it only
takes a commit and a branch and tells the user whether that commit
predates the branch, was cherry-picked to it, or is not present in any
form in the branch.

    $ bin/commit_in_branch.py e58a10af64 fdo/20.1
    Commit e58a10af64 is in branch 20.1
    $ echo $?
    0

    $ bin/commit_in_branch.py dd2bd68fa6 fdo/20.1
    Commit dd2bd68fa6 was backported to branch 20.1 as commit d043d24654c851f0be57dbbf48274b5373dea42b
    $ echo $?
    0

    $ bin/commit_in_branch.py master fdo/20.1
    Commit 2fbcfe170bf50fcbcd2fc70a564a4d69096d968c is NOT in branch 20.1
    $ echo $?
    1

Signed-off-by: Eric Engestrom <eric@engestrom.ch>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/5306>
2020-07-10 20:01:32 +00:00

142 lines
4.4 KiB
Python
Executable File

#!/usr/bin/env python3
import argparse
import subprocess
import sys
def print_(args: argparse.Namespace, success: bool, message: str) -> None:
"""
Print function with extra coloring when supported and/or requested,
and with a "quiet" switch
"""
COLOR_SUCCESS = '\033[32m'
COLOR_FAILURE = '\033[31m'
COLOR_RESET = '\033[0m'
if args.quiet:
return
if args.color == 'auto':
use_colors = sys.stdout.isatty()
else:
use_colors = args.color == 'always'
s = ''
if use_colors:
if success:
s += COLOR_SUCCESS
else:
s += COLOR_FAILURE
s += message
if use_colors:
s += COLOR_RESET
print(s)
def is_commit_valid(commit: str) -> bool:
ret = subprocess.call(['git', 'cat-file', '-e', commit],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL)
return ret == 0
def branch_has_commit(upstream: str, branch: str, commit: str) -> bool:
"""
Returns True if the commit is actually present in the branch
"""
ret = subprocess.call(['git', 'merge-base', '--is-ancestor',
commit, upstream + '/' + branch],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL)
return ret == 0
def branch_has_backport_of_commit(upstream: str, branch: str, commit: str) -> str:
"""
Returns the commit hash if the commit has been backported to the branch,
or an empty string if is hasn't
"""
out = subprocess.check_output(['git', 'log', '--format=%H',
branch + '-branchpoint..' + upstream + '/' + branch,
'--grep', 'cherry picked from commit ' + commit],
stderr=subprocess.DEVNULL)
return out.decode().strip()
def canonicalize_commit(commit: str) -> str:
"""
Takes a commit-ish and returns a commit sha1 if the commit exists
"""
# Make sure input is valid first
if not is_commit_valid(commit):
raise argparse.ArgumentTypeError('invalid commit identifier: ' + commit)
out = subprocess.check_output(['git', 'rev-parse', commit],
stderr=subprocess.DEVNULL)
return out.decode().strip()
def validate_branch(branch: str) -> str:
if '/' not in branch:
raise argparse.ArgumentTypeError('must be in the form `remote/branch`')
out = subprocess.check_output(['git', 'remote', '--verbose'],
stderr=subprocess.DEVNULL)
remotes = out.decode().splitlines()
(upstream, _) = branch.split('/')
valid_remote = False
for line in remotes:
if line.startswith(upstream + '\t'):
valid_remote = True
if not valid_remote:
raise argparse.ArgumentTypeError('Invalid remote: ' + upstream)
if not is_commit_valid(branch):
raise argparse.ArgumentTypeError('Invalid branch: ' + branch)
return branch
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="""
Returns 0 if the commit is present in the branch,
1 if it's not,
and 2 if it couldn't be determined (eg. invalid commit)
""")
parser.add_argument('commit',
type=canonicalize_commit,
help='commit sha1')
parser.add_argument('branch',
type=validate_branch,
help='branch to check, in the form `remote/branch`')
parser.add_argument('--quiet',
action='store_true',
help='suppress all output; exit code can still be used')
parser.add_argument('--color',
choices=['auto', 'always', 'never'],
default='auto',
help='colorize output (default: true if stdout is a terminal)')
args = parser.parse_args()
(upstream, branch) = args.branch.split('/')
if branch_has_commit(upstream, branch, args.commit):
print_(args, True, 'Commit ' + args.commit + ' is in branch ' + branch)
exit(0)
backport = branch_has_backport_of_commit(upstream, branch, args.commit)
if backport:
print_(args, True,
'Commit ' + args.commit + ' was backported to branch ' + branch + ' as commit ' + backport)
exit(0)
print_(args, False, 'Commit ' + args.commit + ' is NOT in branch ' + branch)
exit(1)