8 def print_(args
: argparse
.Namespace
, success
: bool, message
: str) -> None:
10 Print function with extra coloring when supported and/or requested,
11 and with a "quiet" switch
14 COLOR_SUCCESS
= '\033[32m'
15 COLOR_FAILURE
= '\033[31m'
16 COLOR_RESET
= '\033[0m'
21 if args
.color
== 'auto':
22 use_colors
= sys
.stdout
.isatty()
24 use_colors
= args
.color
== 'always'
41 def is_commit_valid(commit
: str) -> bool:
42 ret
= subprocess
.call(['git', 'cat-file', '-e', commit
],
43 stdout
=subprocess
.DEVNULL
,
44 stderr
=subprocess
.DEVNULL
)
48 def branch_has_commit(upstream
: str, branch
: str, commit
: str) -> bool:
50 Returns True if the commit is actually present in the branch
52 ret
= subprocess
.call(['git', 'merge-base', '--is-ancestor',
53 commit
, upstream
+ '/' + branch
],
54 stdout
=subprocess
.DEVNULL
,
55 stderr
=subprocess
.DEVNULL
)
59 def branch_has_backport_of_commit(upstream
: str, branch
: str, commit
: str) -> str:
61 Returns the commit hash if the commit has been backported to the branch,
62 or an empty string if is hasn't
64 out
= subprocess
.check_output(['git', 'log', '--format=%H',
65 branch
+ '-branchpoint..' + upstream
+ '/' + branch
,
66 '--grep', 'cherry picked from commit ' + commit
],
67 stderr
=subprocess
.DEVNULL
)
68 return out
.decode().strip()
71 def canonicalize_commit(commit
: str) -> str:
73 Takes a commit-ish and returns a commit sha1 if the commit exists
76 # Make sure input is valid first
77 if not is_commit_valid(commit
):
78 raise argparse
.ArgumentTypeError('invalid commit identifier: ' + commit
)
80 out
= subprocess
.check_output(['git', 'rev-parse', commit
],
81 stderr
=subprocess
.DEVNULL
)
82 return out
.decode().strip()
85 def validate_branch(branch
: str) -> str:
87 raise argparse
.ArgumentTypeError('must be in the form `remote/branch`')
89 out
= subprocess
.check_output(['git', 'remote', '--verbose'],
90 stderr
=subprocess
.DEVNULL
)
91 remotes
= out
.decode().splitlines()
92 (upstream
, _
) = branch
.split('/')
95 if line
.startswith(upstream
+ '\t'):
99 raise argparse
.ArgumentTypeError('Invalid remote: ' + upstream
)
101 if not is_commit_valid(branch
):
102 raise argparse
.ArgumentTypeError('Invalid branch: ' + branch
)
107 if __name__
== "__main__":
108 parser
= argparse
.ArgumentParser(description
="""
109 Returns 0 if the commit is present in the branch,
111 and 2 if it couldn't be determined (eg. invalid commit)
113 parser
.add_argument('commit',
114 type=canonicalize_commit
,
116 parser
.add_argument('branch',
117 type=validate_branch
,
118 help='branch to check, in the form `remote/branch`')
119 parser
.add_argument('--quiet',
121 help='suppress all output; exit code can still be used')
122 parser
.add_argument('--color',
123 choices
=['auto', 'always', 'never'],
125 help='colorize output (default: true if stdout is a terminal)')
126 args
= parser
.parse_args()
128 (upstream
, branch
) = args
.branch
.split('/')
130 if branch_has_commit(upstream
, branch
, args
.commit
):
131 print_(args
, True, 'Commit ' + args
.commit
+ ' is in branch ' + branch
)
134 backport
= branch_has_backport_of_commit(upstream
, branch
, args
.commit
)
137 'Commit ' + args
.commit
+ ' was backported to branch ' + branch
+ ' as commit ' + backport
)
140 print_(args
, False, 'Commit ' + args
.commit
+ ' is NOT in branch ' + branch
)