| #!/usr/bin/env python | |
| # Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file | |
| # for details. All rights reserved. Use of this source code is governed by a | |
| # BSD-style license that can be found in the LICENSE file. | |
| # Script that automatically pulls and uploads all upstream direct and indirect | |
| # branches into the current branch. | |
| # | |
| # Example: | |
| # | |
| # $ git branch -vv | |
| # * feature_final xxxxxxxxx [feature_prereq_c: ...] ... | |
| # feature_prereq_c xxxxxxxxx [feature_prereq_b: ...] ... | |
| # feature_prereq_b xxxxxxxxx [feature_prereq_a: ...] ... | |
| # feature_prereq_a xxxxxxxxx [master: ...] ... | |
| # master xxxxxxxxx [origin/master] ... | |
| # | |
| # Executing `git_sync_cl_chain.py -m <message>` causes the following chain of | |
| # commands to be executed: | |
| # | |
| # $ git checkout feature_prereq_a; git pull; git cl upload -m <message> | |
| # $ git checkout feature_prereq_b; git pull; git cl upload -m <message> | |
| # $ git checkout feature_prereq_c; git pull; git cl upload -m <message> | |
| # $ git checkout feature_final; git pull; git cl upload -m <message> | |
| import optparse | |
| import os | |
| import sys | |
| import defines | |
| import utils | |
| REPO_ROOT = defines.REPO_ROOT | |
| class Repo(object): | |
| def __init__(self, name, is_current, upstream): | |
| self.name = name | |
| self.is_current = is_current | |
| self.upstream = upstream | |
| def ParseOptions(argv): | |
| result = optparse.OptionParser() | |
| result.add_option('--message', '-m', help='Message for patchset') | |
| result.add_option('--rebase', | |
| help='To use `git pull --rebase` instead of `git pull`', | |
| action='store_true') | |
| result.add_option('--no_upload', '--no-upload', | |
| help='Disable uploading to Gerrit', action='store_true') | |
| (options, args) = result.parse_args(argv) | |
| options.upload = not options.no_upload | |
| assert options.message, 'A message for the patchset is required.' | |
| assert len(args) == 0 | |
| return options | |
| def main(argv): | |
| options = ParseOptions(argv) | |
| rebase_args = ['--rebase'] if options.rebase else [] | |
| with utils.ChangedWorkingDirectory(REPO_ROOT, quiet=True): | |
| branches = [ | |
| parse(line) | |
| for line in utils.RunCmd(['git', 'branch', '-vv'], quiet=True)] | |
| current_branch = None | |
| for branch in branches: | |
| if branch.is_current: | |
| current_branch = branch | |
| break | |
| assert current_branch is not None | |
| if current_branch.upstream == None: | |
| print('Nothing to sync') | |
| return | |
| stack = [] | |
| while current_branch: | |
| stack.append(current_branch) | |
| if current_branch.upstream is None or current_branch.upstream == 'master': | |
| break | |
| current_branch = get_branch_with_name(current_branch.upstream, branches) | |
| while len(stack) > 0: | |
| branch = stack.pop() | |
| print('Syncing ' + branch.name) | |
| utils.RunCmd(['git', 'checkout', branch.name], quiet=True) | |
| utils.RunCmd(['git', 'pull'] + rebase_args, quiet=True) | |
| if options.upload: | |
| utils.RunCmd(['git', 'cl', 'upload', '-m', options.message], quiet=True) | |
| utils.RunCmd(['git', 'cl', 'issue']) | |
| def get_branch_with_name(name, branches): | |
| for branch in branches: | |
| if branch.name == name: | |
| return branch | |
| return None | |
| # Parses a line from the output of `git branch -vv`. | |
| # | |
| # Example output ('*' denotes the current branch): | |
| # | |
| # $ git branch -vv | |
| # * feature_final xxxxxxxxx [feature_prereq_c: ...] ... | |
| # feature_prereq_c xxxxxxxxx [feature_prereq_b: ...] ... | |
| # feature_prereq_b xxxxxxxxx [feature_prereq_a: ...] ... | |
| # feature_prereq_a xxxxxxxxx [master: ...] ... | |
| # master xxxxxxxxx [origin/master] ... | |
| def parse(line): | |
| is_current = False | |
| if line.startswith('*'): | |
| is_current = True | |
| line = line[1:].lstrip() | |
| else: | |
| line = line.lstrip() | |
| name_end_index = line.index(' ') | |
| name = line[:name_end_index] | |
| line = line[name_end_index:].lstrip() | |
| if ('[') not in line or ':' not in line: | |
| return Repo(name, is_current, None) | |
| upstream_start_index = line.index('[') | |
| line = line[upstream_start_index+1:] | |
| upstream_end_index = line.index(':') | |
| upstream = line[:upstream_end_index] | |
| return Repo(name, is_current, upstream) | |
| if __name__ == '__main__': | |
| sys.exit(main(sys.argv[1:])) |