| #!/usr/bin/env python3 | |
| # 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. | |
| import argparse | |
| import collections | |
| import os | |
| import subprocess | |
| import sys | |
| def get_config(name): | |
| args = ['git', 'config', '--get', name] | |
| result = subprocess.run( | |
| args, capture_output=True, text=True, check=False) | |
| if result.returncode not in (0, 1): | |
| raise subprocess.CalledProcessError( | |
| result.returncode, ' '.join(args), output=result.stdout) | |
| return result.stdout.strip() | |
| def deref(name): | |
| return subprocess.run( | |
| ['git', 'rev-parse', '--symbolic-full-name', name], | |
| capture_output=True, text=True, check=True).stdout.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('args', nargs='*', metavar='REVIEWER_OR_HASHTAG', | |
| help='reviewer names or aliases, or #hashtags') | |
| p.add_argument('-t', '--topic', default='', metavar='TOPIC', | |
| help='topic for new changes') | |
| p.add_argument('-e', '--edit', action='store_true', | |
| help='upload as change edit') | |
| p.add_argument('-w', '--wip', action='store_true', help='upload as WIP') | |
| p.add_argument('-y', '--ready', action='store_true', help='set ready') | |
| 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) | |
| is_hashtag = lambda x: x.startswith('#') | |
| opts['r'].extend( | |
| (get_config('reviewer.' + r) or r) | |
| for r in args.args if not is_hashtag(r)) | |
| opts['t'].extend(t[1:] for t in args.args if is_hashtag(t)) | |
| if args.topic: | |
| opts['topic'].append(args.topic) | |
| if args.edit: | |
| opts['edit'].append(True) | |
| if args.wip: | |
| opts['wip'].append(True) | |
| if args.ready: | |
| opts['ready'].append(True) | |
| opts_strs = [] | |
| for k in opts: | |
| for v in opts[k]: | |
| if v == True: | |
| opts_strs.append(k) | |
| elif v != False: | |
| opts_strs.append(f'{k}={v}') | |
| opts_str = ','.join(opts_strs) | |
| if opts_str: | |
| opts_str = '%' + opts_str | |
| git_args = ['git', 'push', args.remote, | |
| f'HEAD:refs/for/{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)) |