diff --git a/contrib/git-push-review b/contrib/git-push-review new file mode 100755 index 0000000000..898b023f09 --- /dev/null +++ b/contrib/git-push-review @@ -0,0 +1,88 @@ +#!/usr/bin/python +# Copyright (C) 2014 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import print_function + +import argparse +import collections +import os +import subprocess +import sys + + +def get_config(name): + args = ['git', 'config', '--get', name] + p = subprocess.Popen(args, stdout=subprocess.PIPE) + out, _ = p.communicate() + ret = p.poll() + if ret not in (0, 1): + raise subprocess.CalledProcessError(ret, ' '.join(args), output=out) + return out.strip() + + +def deref(name): + p = subprocess.Popen( + ['git', 'rev-parse', '--symbolic-full-name', name], + stdout=subprocess.PIPE, stderr=subprocess.PIPE) + out, _ = p.communicate() + return out.strip() + + +def main(argv): + p = argparse.ArgumentParser(description='Push changes to Gerrit for review') + p.add_argument('-r', '--remote', default='', metavar='REMOTE', + help='remote name or URL to push to') + p.add_argument('-b', '--branch', default='', metavar='BRANCH', + help='remote branch name, refs/for/BRANCH') + p.add_argument('reviewers', nargs='*', metavar='REVIEWER', + help='reviewer names or aliases') + p.add_argument('-t', '--topic', default='', metavar='TOPIC', + help='topic for new changes') + p.add_argument('--dry-run', action='store_true', + help='dry run, print git command and exit') + args = p.parse_args() + + if not args.remote or not args.branch: + hp = 'refs/heads/' + upstream = deref('HEAD') + while upstream.startswith(hp): + upstream = deref(upstream[len(hp):] + '@{u}') + + rp = 'refs/remotes/' + if upstream.startswith(rp): + def_remote, def_branch = upstream[len(rp):].split('/', 1) + else: + def_remote, def_branch = 'origin', 'master' + args.remote = args.remote or def_remote + args.branch = args.branch or def_branch + + opts = collections.defaultdict(list) + opts['r'].extend((get_config('reviewer.' + r) or r) for r in args.reviewers) + if args.topic: + opts['topic'].append(args.topic) + opts_str = ','.join('%s=%s' % (k, v) for k in opts for v in opts[k]) + if opts_str: + opts_str = '%' + opts_str + + git_args = ['git', 'push', args.remote, + 'HEAD:refs/for/%s%s' % (args.branch, opts_str)] + if args.dry_run: + print(' '.join(git_args)) + return 0 + os.execvp('git', git_args) + + +if __name__ == '__main__': + sys.exit(main(sys.argv))