From c0fb88f19e96f0bd604c237ee13010973fc8b7c3 Mon Sep 17 00:00:00 2001 From: dianlujitao Date: Sun, 14 Jan 2024 17:59:24 +0800 Subject: [PATCH] repopick: Organize imports and format with black Change-Id: I94080fb8adba0af83d86f0c67af8fac460ffbea0 --- build/tools/repopick.py | 589 +++++++++++++++++++++++++++------------- 1 file changed, 394 insertions(+), 195 deletions(-) diff --git a/build/tools/repopick.py b/build/tools/repopick.py index 3cb2c8d4..5f579aa1 100755 --- a/build/tools/repopick.py +++ b/build/tools/repopick.py @@ -20,12 +20,12 @@ # Run repopick.py -h for a description of this utility. # -import sys +import argparse import json import os -import subprocess import re -import argparse +import subprocess +import sys import textwrap from functools import cmp_to_key from xml.etree import ElementTree @@ -44,58 +44,77 @@ def cmp(a, b): # Verifies whether pathA is a subdirectory (or the same) as pathB def is_subdir(a, b): - a = os.path.realpath(a) + '/' - b = os.path.realpath(b) + '/' - return b == a[:len(b)] + a = os.path.realpath(a) + "/" + b = os.path.realpath(b) + "/" + return b == a[: len(b)] def fetch_query_via_ssh(remote_url, query): """Given a remote_url and a query, return the list of changes that fit it - This function is slightly messy - the ssh api does not return data in the same structure as the HTTP REST API - We have to get the data, then transform it to match what we're expecting from the HTTP RESET API""" - if remote_url.count(':') == 2: - (uri, userhost, port) = remote_url.split(':') + This function is slightly messy - the ssh api does not return data in the same structure as the HTTP REST API + We have to get the data, then transform it to match what we're expecting from the HTTP RESET API + """ + if remote_url.count(":") == 2: + (uri, userhost, port) = remote_url.split(":") userhost = userhost[2:] - elif remote_url.count(':') == 1: - (uri, userhost) = remote_url.split(':') + elif remote_url.count(":") == 1: + (uri, userhost) = remote_url.split(":") userhost = userhost[2:] port = 29418 else: - raise Exception('Malformed URI: Expecting ssh://[user@]host[:port]') + raise Exception("Malformed URI: Expecting ssh://[user@]host[:port]") - out = subprocess.check_output(['ssh', '-x', '-p{0}'.format(port), userhost, 'gerrit', 'query', '--format=JSON --patch-sets --current-patch-set', query]) - if not hasattr(out, 'encode'): + out = subprocess.check_output( + [ + "ssh", + "-x", + "-p{0}".format(port), + userhost, + "gerrit", + "query", + "--format=JSON --patch-sets --current-patch-set", + query, + ] + ) + if not hasattr(out, "encode"): out = out.decode() reviews = [] - for line in out.split('\n'): + for line in out.split("\n"): try: data = json.loads(line) # make our data look like the http rest api data review = { - 'branch': data['branch'], - 'change_id': data['id'], - 'current_revision': data['currentPatchSet']['revision'], - 'number': int(data['number']), - 'revisions': {patch_set['revision']: { - '_number': int(patch_set['number']), - 'fetch': { - 'ssh': { - 'ref': patch_set['ref'], - 'url': 'ssh://{0}:{1}/{2}'.format(userhost, port, data['project']) - } - }, - 'commit': { - 'parents': [{'commit': parent} for parent in patch_set['parents']] - }, - } for patch_set in data['patchSets']}, - 'subject': data['subject'], - 'project': data['project'], - 'status': data['status'] + "branch": data["branch"], + "change_id": data["id"], + "current_revision": data["currentPatchSet"]["revision"], + "number": int(data["number"]), + "revisions": { + patch_set["revision"]: { + "_number": int(patch_set["number"]), + "fetch": { + "ssh": { + "ref": patch_set["ref"], + "url": "ssh://{0}:{1}/{2}".format( + userhost, port, data["project"] + ), + } + }, + "commit": { + "parents": [ + {"commit": parent} for parent in patch_set["parents"] + ] + }, + } + for patch_set in data["patchSets"] + }, + "subject": data["subject"], + "project": data["project"], + "status": data["status"], } reviews.append(review) except: pass - args.quiet or print('Found {0} reviews'.format(len(reviews))) + args.quiet or print("Found {0} reviews".format(len(reviews))) return reviews @@ -107,44 +126,57 @@ def fetch_query_via_http(remote_url, query): for line in f: parts = line.rstrip().split("|") if parts[0] in remote_url: - auth = requests.auth.HTTPBasicAuth(username=parts[1], password=parts[2]) - status_code = '-1' + auth = requests.auth.HTTPBasicAuth( + username=parts[1], password=parts[2] + ) + status_code = "-1" if auth: - url = '{0}/a/changes/?q={1}&o=CURRENT_REVISION&o=ALL_REVISIONS&o=ALL_COMMITS'.format(remote_url, query) + url = "{0}/a/changes/?q={1}&o=CURRENT_REVISION&o=ALL_REVISIONS&o=ALL_COMMITS".format( + remote_url, query + ) data = requests.get(url, auth=auth) status_code = str(data.status_code) - if status_code != '200': - #They didn't get good authorization or data, Let's try the old way - url = '{0}/changes/?q={1}&o=CURRENT_REVISION&o=ALL_REVISIONS&o=ALL_COMMITS'.format(remote_url, query) + if status_code != "200": + # They didn't get good authorization or data, Let's try the old way + url = "{0}/changes/?q={1}&o=CURRENT_REVISION&o=ALL_REVISIONS&o=ALL_COMMITS".format( + remote_url, query + ) data = requests.get(url) reviews = json.loads(data.text[5:]) else: """Given a query, fetch the change numbers via http""" - url = '{0}/changes/?q={1}&o=CURRENT_REVISION&o=ALL_REVISIONS&o=ALL_COMMITS'.format(remote_url, query) - data = urllib.request.urlopen(url).read().decode('utf-8') + url = "{0}/changes/?q={1}&o=CURRENT_REVISION&o=ALL_REVISIONS&o=ALL_COMMITS".format( + remote_url, query + ) + data = urllib.request.urlopen(url).read().decode("utf-8") reviews = json.loads(data[5:]) for review in reviews: - review['number'] = review.pop('_number') + review["number"] = review.pop("_number") return reviews def fetch_query(remote_url, query): """Wrapper for fetch_query_via_proto functions""" - if remote_url[0:3] == 'ssh': + if remote_url[0:3] == "ssh": return fetch_query_via_ssh(remote_url, query) - elif remote_url[0:4] == 'http': - return fetch_query_via_http(remote_url, query.replace(' ', '+')) + elif remote_url[0:4] == "http": + return fetch_query_via_http(remote_url, query.replace(" ", "+")) else: - raise Exception('Gerrit URL should be in the form http[s]://hostname/ or ssh://[user@]host[:port]') + raise Exception( + "Gerrit URL should be in the form http[s]://hostname/ or ssh://[user@]host[:port]" + ) -if __name__ == '__main__': +if __name__ == "__main__": # Default to LineageOS Gerrit - default_gerrit = 'https://review.lineageos.org' + default_gerrit = "https://review.lineageos.org" - parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter, description=textwrap.dedent('''\ + parser = argparse.ArgumentParser( + formatter_class=argparse.RawDescriptionHelpFormatter, + description=textwrap.dedent( + """\ repopick.py is a utility to simplify the process of cherry picking patches from LineageOS's Gerrit instance (or any gerrit instance of your choosing) @@ -158,109 +190,179 @@ if __name__ == '__main__': The --abandon-first argument, when used in conjunction with the --start-branch option, will cause repopick to abandon the specified - branch in all repos first before performing any cherry picks.''')) - parser.add_argument('change_number', nargs='*', - help='change number to cherry pick. Use {change number}/{patchset number} to get a specific revision.') - parser.add_argument('-i', '--ignore-missing', action='store_true', - help='do not error out if a patch applies to a missing directory') - parser.add_argument('-s', '--start-branch', nargs=1, - metavar='', help='start the specified branch before cherry picking') - parser.add_argument('-r', '--reset', action='store_true', - help='reset to initial state (abort cherry-pick) if there is a conflict') - parser.add_argument('-a', '--abandon-first', action='store_true', - help='before cherry picking, abandon the branch specified in --start-branch') - parser.add_argument('-b', '--auto-branch', action='store_true', - help='shortcut to "--start-branch auto --abandon-first --ignore-missing"') - parser.add_argument('-q', '--quiet', action='store_true', help='print as little as possible') - parser.add_argument('-v', '--verbose', action='store_true', help='print extra information to aid in debug') - parser.add_argument('-f', '--force', action='store_true', help='force cherry pick even if change is closed') - parser.add_argument('-p', '--pull', action='store_true', help='execute pull instead of cherry-pick') - parser.add_argument('-P', '--path', metavar='', help='use the specified path for the change') - parser.add_argument('-t', '--topic', metavar='', help='pick all commits from a specified topic') - parser.add_argument('-Q', '--query', metavar='', help='pick all commits using the specified query') - parser.add_argument('-g', '--gerrit', default=default_gerrit, - metavar='', help='Gerrit Instance to use. Form proto://[user@]host[:port]') - parser.add_argument('-e', '--exclude', nargs=1, - metavar='', help='exclude a list of commit numbers separated by a ,') - parser.add_argument('-c', '--check-picked', type=int, default=10, - metavar='', help='pass the amount of commits to check for already picked changes') + branch in all repos first before performing any cherry picks.""" + ), + ) + parser.add_argument( + "change_number", + nargs="*", + help="change number to cherry pick. Use {change number}/{patchset number} to get a specific revision.", + ) + parser.add_argument( + "-i", + "--ignore-missing", + action="store_true", + help="do not error out if a patch applies to a missing directory", + ) + parser.add_argument( + "-s", + "--start-branch", + nargs=1, + metavar="", + help="start the specified branch before cherry picking", + ) + parser.add_argument( + "-r", + "--reset", + action="store_true", + help="reset to initial state (abort cherry-pick) if there is a conflict", + ) + parser.add_argument( + "-a", + "--abandon-first", + action="store_true", + help="before cherry picking, abandon the branch specified in --start-branch", + ) + parser.add_argument( + "-b", + "--auto-branch", + action="store_true", + help='shortcut to "--start-branch auto --abandon-first --ignore-missing"', + ) + parser.add_argument( + "-q", "--quiet", action="store_true", help="print as little as possible" + ) + parser.add_argument( + "-v", + "--verbose", + action="store_true", + help="print extra information to aid in debug", + ) + parser.add_argument( + "-f", + "--force", + action="store_true", + help="force cherry pick even if change is closed", + ) + parser.add_argument( + "-p", "--pull", action="store_true", help="execute pull instead of cherry-pick" + ) + parser.add_argument( + "-P", "--path", metavar="", help="use the specified path for the change" + ) + parser.add_argument( + "-t", "--topic", metavar="", help="pick all commits from a specified topic" + ) + parser.add_argument( + "-Q", "--query", metavar="", help="pick all commits using the specified query" + ) + parser.add_argument( + "-g", + "--gerrit", + default=default_gerrit, + metavar="", + help="Gerrit Instance to use. Form proto://[user@]host[:port]", + ) + parser.add_argument( + "-e", + "--exclude", + nargs=1, + metavar="", + help="exclude a list of commit numbers separated by a ,", + ) + parser.add_argument( + "-c", + "--check-picked", + type=int, + default=10, + metavar="", + help="pass the amount of commits to check for already picked changes", + ) args = parser.parse_args() if not args.start_branch and args.abandon_first: - parser.error('if --abandon-first is set, you must also give the branch name with --start-branch') + parser.error( + "if --abandon-first is set, you must also give the branch name with --start-branch" + ) if args.auto_branch: args.abandon_first = True args.ignore_missing = True if not args.start_branch: - args.start_branch = ['auto'] + args.start_branch = ["auto"] if args.quiet and args.verbose: - parser.error('--quiet and --verbose cannot be specified together') + parser.error("--quiet and --verbose cannot be specified together") if (1 << bool(args.change_number) << bool(args.topic) << bool(args.query)) != 2: - parser.error('One (and only one) of change_number, topic, and query are allowed') + parser.error( + "One (and only one) of change_number, topic, and query are allowed" + ) # Change current directory to the top of the tree - if 'ANDROID_BUILD_TOP' in os.environ: - top = os.environ['ANDROID_BUILD_TOP'] + if "ANDROID_BUILD_TOP" in os.environ: + top = os.environ["ANDROID_BUILD_TOP"] if not is_subdir(os.getcwd(), top): - sys.stderr.write('ERROR: You must run this tool from within $ANDROID_BUILD_TOP!\n') + sys.stderr.write( + "ERROR: You must run this tool from within $ANDROID_BUILD_TOP!\n" + ) sys.exit(1) - os.chdir(os.environ['ANDROID_BUILD_TOP']) + os.chdir(os.environ["ANDROID_BUILD_TOP"]) # Sanity check that we are being run from the top level of the tree - if not os.path.isdir('.repo'): - sys.stderr.write('ERROR: No .repo directory found. Please run this from the top of your tree.\n') + if not os.path.isdir(".repo"): + sys.stderr.write( + "ERROR: No .repo directory found. Please run this from the top of your tree.\n" + ) sys.exit(1) # If --abandon-first is given, abandon the branch before starting if args.abandon_first: # Determine if the branch already exists; skip the abandon if it does not - plist = subprocess.check_output(['repo', 'info']) - if not hasattr(plist, 'encode'): + plist = subprocess.check_output(["repo", "info"]) + if not hasattr(plist, "encode"): plist = plist.decode() needs_abandon = False for pline in plist.splitlines(): - matchObj = re.match(r'Local Branches.*\[(.*)\]', pline) + matchObj = re.match(r"Local Branches.*\[(.*)\]", pline) if matchObj: - local_branches = re.split(r'\s*,\s*', matchObj.group(1)) + local_branches = re.split(r"\s*,\s*", matchObj.group(1)) if any(args.start_branch[0] in s for s in local_branches): needs_abandon = True if needs_abandon: # Perform the abandon only if the branch already exists if not args.quiet: - print('Abandoning branch: %s' % args.start_branch[0]) - subprocess.check_output(['repo', 'abandon', args.start_branch[0]]) + print("Abandoning branch: %s" % args.start_branch[0]) + subprocess.check_output(["repo", "abandon", args.start_branch[0]]) if not args.quiet: - print('') + print("") # Get the main manifest from repo # - convert project name and revision to a path project_name_to_data = {} - manifest = subprocess.check_output(['repo', 'manifest']) + manifest = subprocess.check_output(["repo", "manifest"]) xml_root = ElementTree.fromstring(manifest) - projects = xml_root.findall('project') - remotes = xml_root.findall('remote') - default_revision = xml_root.findall('default')[0].get('revision') + projects = xml_root.findall("project") + remotes = xml_root.findall("remote") + default_revision = xml_root.findall("default")[0].get("revision") # dump project data into the a list of dicts with the following data: # {project: {path, revision}} for project in projects: - name = project.get('name') + name = project.get("name") # when name and path are equal, "repo manifest" doesn't return a path at all, so fall back to name - path = project.get('path', name) - revision = project.get('upstream') + path = project.get("path", name) + revision = project.get("upstream") if revision is None: for remote in remotes: - if remote.get('name') == project.get('remote'): - revision = remote.get('revision') + if remote.get("name") == project.get("remote"): + revision = remote.get("revision") if revision is None: - revision = project.get('revision', default_revision) + revision = project.get("revision", default_revision) if name not in project_name_to_data: project_name_to_data[name] = {} - revision = revision.split('refs/heads/')[-1] + revision = revision.split("refs/heads/")[-1] project_name_to_data[name][revision] = path # get data on requested changes @@ -268,36 +370,47 @@ if __name__ == '__main__': change_numbers = [] def cmp_reviews(review_a, review_b): - current_a = review_a['current_revision'] - parents_a = [r['commit'] for r in review_a['revisions'][current_a]['commit']['parents']] - current_b = review_b['current_revision'] - parents_b = [r['commit'] for r in review_b['revisions'][current_b]['commit']['parents']] + current_a = review_a["current_revision"] + parents_a = [ + r["commit"] for r in review_a["revisions"][current_a]["commit"]["parents"] + ] + current_b = review_b["current_revision"] + parents_b = [ + r["commit"] for r in review_b["revisions"][current_b]["commit"]["parents"] + ] if current_a in parents_b: return -1 elif current_b in parents_a: return 1 else: - return cmp(review_a['number'], review_b['number']) + return cmp(review_a["number"], review_b["number"]) if args.topic: - reviews = fetch_query(args.gerrit, 'topic:{0}'.format(args.topic)) - change_numbers = [str(r['number']) for r in sorted(reviews, key=cmp_to_key(cmp_reviews))] + reviews = fetch_query(args.gerrit, "topic:{0}".format(args.topic)) + change_numbers = [ + str(r["number"]) for r in sorted(reviews, key=cmp_to_key(cmp_reviews)) + ] if args.query: reviews = fetch_query(args.gerrit, args.query) - change_numbers = [str(r['number']) for r in sorted(reviews, key=cmp_to_key(cmp_reviews))] + change_numbers = [ + str(r["number"]) for r in sorted(reviews, key=cmp_to_key(cmp_reviews)) + ] if args.change_number: - change_url_re = re.compile(r'https?://.+?/([0-9]+(?:/[0-9]+)?)/?') + change_url_re = re.compile(r"https?://.+?/([0-9]+(?:/[0-9]+)?)/?") for c in args.change_number: change_number = change_url_re.findall(c) if change_number: change_numbers.extend(change_number) - elif '-' in c: - templist = c.split('-') + elif "-" in c: + templist = c.split("-") for i in range(int(templist[0]), int(templist[1]) + 1): change_numbers.append(str(i)) else: change_numbers.append(c) - reviews = fetch_query(args.gerrit, ' OR '.join('change:{0}'.format(x.split('/')[0]) for x in change_numbers)) + reviews = fetch_query( + args.gerrit, + " OR ".join("change:{0}".format(x.split("/")[0]) for x in change_numbers), + ) # make list of things to actually merge mergables = [] @@ -305,12 +418,12 @@ if __name__ == '__main__': # If --exclude is given, create the list of commits to ignore exclude = [] if args.exclude: - exclude = args.exclude[0].split(',') + exclude = args.exclude[0].split(",") for change in change_numbers: patchset = None - if '/' in change: - (change, patchset) = change.split('/') + if "/" in change: + (change, patchset) = change.split("/") if change in exclude: continue @@ -320,90 +433,144 @@ if __name__ == '__main__': if patchset: patchset = int(patchset) - review = next((x for x in reviews if x['number'] == change), None) + review = next((x for x in reviews if x["number"] == change), None) if review is None: - print('Change %d not found, skipping' % change) + print("Change %d not found, skipping" % change) continue - mergables.append({ - 'subject': review['subject'], - 'project': review['project'], - 'branch': review['branch'], - 'change_id': review['change_id'], - 'change_number': review['number'], - 'status': review['status'], - 'fetch': None, - 'patchset': review['revisions'][review['current_revision']]['_number'], - }) + mergables.append( + { + "subject": review["subject"], + "project": review["project"], + "branch": review["branch"], + "change_id": review["change_id"], + "change_number": review["number"], + "status": review["status"], + "fetch": None, + "patchset": review["revisions"][review["current_revision"]]["_number"], + } + ) - mergables[-1]['fetch'] = review['revisions'][review['current_revision']]['fetch'] - mergables[-1]['id'] = change + mergables[-1]["fetch"] = review["revisions"][review["current_revision"]][ + "fetch" + ] + mergables[-1]["id"] = change if patchset: try: - mergables[-1]['fetch'] = [review['revisions'][x]['fetch'] for x in review['revisions'] if review['revisions'][x]['_number'] == patchset][0] - mergables[-1]['id'] = '{0}/{1}'.format(change, patchset) - mergables[-1]['patchset'] = patchset + mergables[-1]["fetch"] = [ + review["revisions"][x]["fetch"] + for x in review["revisions"] + if review["revisions"][x]["_number"] == patchset + ][0] + mergables[-1]["id"] = "{0}/{1}".format(change, patchset) + mergables[-1]["patchset"] = patchset except (IndexError, ValueError): - args.quiet or print('ERROR: The patch set {0}/{1} could not be found, using CURRENT_REVISION instead.'.format(change, patchset)) + args.quiet or print( + "ERROR: The patch set {0}/{1} could not be found, using CURRENT_REVISION instead.".format( + change, patchset + ) + ) for item in mergables: - args.quiet or print('Applying change number {0}...'.format(item['id'])) + args.quiet or print("Applying change number {0}...".format(item["id"])) # Check if change is open and exit if it's not, unless -f is specified - if (item['status'] != 'OPEN' and item['status'] != 'NEW' and item['status'] != 'DRAFT'): + if ( + item["status"] != "OPEN" + and item["status"] != "NEW" + and item["status"] != "DRAFT" + ): if args.force: - print('!! Force-picking a closed change !!\n') + print("!! Force-picking a closed change !!\n") else: - print('Change status is ' + item['status'] + '. Skipping the cherry pick.\nUse -f to force this pick.') + print( + "Change status is " + + item["status"] + + ". Skipping the cherry pick.\nUse -f to force this pick." + ) continue # Convert the project name to a project path # - check that the project path exists project_path = None - if item['project'] in project_name_to_data and item['branch'] in project_name_to_data[item['project']]: - project_path = project_name_to_data[item['project']][item['branch']] + if ( + item["project"] in project_name_to_data + and item["branch"] in project_name_to_data[item["project"]] + ): + project_path = project_name_to_data[item["project"]][item["branch"]] elif args.path: project_path = args.path - elif item['project'] in project_name_to_data and len(project_name_to_data[item['project']]) == 1: - local_branch = list(project_name_to_data[item['project']])[0] - project_path = project_name_to_data[item['project']][local_branch] - print('WARNING: Project {0} has a different branch ("{1}" != "{2}")'.format(project_path, local_branch, item['branch'])) + elif ( + item["project"] in project_name_to_data + and len(project_name_to_data[item["project"]]) == 1 + ): + local_branch = list(project_name_to_data[item["project"]])[0] + project_path = project_name_to_data[item["project"]][local_branch] + print( + 'WARNING: Project {0} has a different branch ("{1}" != "{2}")'.format( + project_path, local_branch, item["branch"] + ) + ) elif args.ignore_missing: - print('WARNING: Skipping {0} since there is no project directory for: {1}\n'.format(item['id'], item['project'])) + print( + "WARNING: Skipping {0} since there is no project directory for: {1}\n".format( + item["id"], item["project"] + ) + ) continue else: - sys.stderr.write('ERROR: For {0}, could not determine the project path for project {1}\n'.format(item['id'], item['project'])) + sys.stderr.write( + "ERROR: For {0}, could not determine the project path for project {1}\n".format( + item["id"], item["project"] + ) + ) sys.exit(1) # If --start-branch is given, create the branch (more than once per path is okay; repo ignores gracefully) if args.start_branch: - subprocess.check_output(['repo', 'start', args.start_branch[0], project_path]) + subprocess.check_output( + ["repo", "start", args.start_branch[0], project_path] + ) # Determine the maximum commits to check already picked changes check_picked_count = args.check_picked - max_count = '--max-count={0}'.format(check_picked_count + 1) - branch_commits_count = int(subprocess.check_output(['git', 'rev-list', '--count', max_count, 'HEAD'], cwd=project_path)) + max_count = "--max-count={0}".format(check_picked_count + 1) + branch_commits_count = int( + subprocess.check_output( + ["git", "rev-list", "--count", max_count, "HEAD"], cwd=project_path + ) + ) if branch_commits_count <= check_picked_count: check_picked_count = branch_commits_count - 1 # Check if change is already picked to HEAD...HEAD~check_picked_count found_change = False for i in range(0, check_picked_count): - if subprocess.call(['git', 'cat-file', '-e', 'HEAD~{0}'.format(i)], cwd=project_path, stderr=open(os.devnull, 'wb')): + if subprocess.call( + ["git", "cat-file", "-e", "HEAD~{0}".format(i)], + cwd=project_path, + stderr=open(os.devnull, "wb"), + ): continue - output = subprocess.check_output(['git', 'show', '-q', 'HEAD~{0}'.format(i)], cwd=project_path) + output = subprocess.check_output( + ["git", "show", "-q", "HEAD~{0}".format(i)], cwd=project_path + ) # make sure we have a string on Python 3 if isinstance(output, bytes): - output = output.decode('utf-8') + output = output.decode("utf-8") output = output.split() - if 'Change-Id:' in output: - head_change_id = '' + if "Change-Id:" in output: + head_change_id = "" for j, t in enumerate(reversed(output)): - if t == 'Change-Id:': + if t == "Change-Id:": head_change_id = output[len(output) - j] break - if head_change_id.strip() == item['change_id']: - print('Skipping {0} - already picked in {1} as HEAD~{2}'.format(item['id'], project_path, i)) + if head_change_id.strip() == item["change_id"]: + print( + "Skipping {0} - already picked in {1} as HEAD~{2}".format( + item["id"], project_path, i + ) + ) found_change = True break if found_change: @@ -411,76 +578,108 @@ if __name__ == '__main__': # Print out some useful info if not args.quiet: - print('--> Subject: "{0}"'.format(item['subject'])) - print('--> Project path: {0}'.format(project_path)) - print('--> Change number: {0} (Patch Set {1})'.format(item['id'], item['patchset'])) + print('--> Subject: "{0}"'.format(item["subject"])) + print("--> Project path: {0}".format(project_path)) + print( + "--> Change number: {0} (Patch Set {1})".format( + item["id"], item["patchset"] + ) + ) - if 'anonymous http' in item['fetch']: - method = 'anonymous http' + if "anonymous http" in item["fetch"]: + method = "anonymous http" else: - method = 'ssh' + method = "ssh" # Try fetching from GitHub first if using default gerrit if args.gerrit == default_gerrit: if args.verbose: - print('Trying to fetch the change from GitHub') + print("Trying to fetch the change from GitHub") if args.pull: - cmd = ['git pull --no-edit github', item['fetch'][method]['ref']] + cmd = ["git pull --no-edit github", item["fetch"][method]["ref"]] else: - cmd = ['git fetch github', item['fetch'][method]['ref']] + cmd = ["git fetch github", item["fetch"][method]["ref"]] if args.quiet: - cmd.append('--quiet') + cmd.append("--quiet") else: print(cmd) - result = subprocess.call([' '.join(cmd)], cwd=project_path, shell=True) - FETCH_HEAD = '{0}/.git/FETCH_HEAD'.format(project_path) + result = subprocess.call([" ".join(cmd)], cwd=project_path, shell=True) + FETCH_HEAD = "{0}/.git/FETCH_HEAD".format(project_path) if result != 0 and os.stat(FETCH_HEAD).st_size != 0: - print('ERROR: git command failed') + print("ERROR: git command failed") sys.exit(result) # Check if it worked if args.gerrit != default_gerrit or os.stat(FETCH_HEAD).st_size == 0: # If not using the default gerrit or github failed, fetch from gerrit. if args.verbose: if args.gerrit == default_gerrit: - print('Fetching from GitHub didn\'t work, trying to fetch the change from Gerrit') + print( + "Fetching from GitHub didn't work, trying to fetch the change from Gerrit" + ) else: - print('Fetching from {0}'.format(args.gerrit)) + print("Fetching from {0}".format(args.gerrit)) if args.pull: - cmd = ['git pull --no-edit', item['fetch'][method]['url'], item['fetch'][method]['ref']] + cmd = [ + "git pull --no-edit", + item["fetch"][method]["url"], + item["fetch"][method]["ref"], + ] else: - cmd = ['git fetch', item['fetch'][method]['url'], item['fetch'][method]['ref']] + cmd = [ + "git fetch", + item["fetch"][method]["url"], + item["fetch"][method]["ref"], + ] if args.quiet: - cmd.append('--quiet') + cmd.append("--quiet") else: print(cmd) - result = subprocess.call([' '.join(cmd)], cwd=project_path, shell=True) + result = subprocess.call([" ".join(cmd)], cwd=project_path, shell=True) if result != 0: - print('ERROR: git command failed') + print("ERROR: git command failed") sys.exit(result) # Perform the cherry-pick if not args.pull: - cmd = ['git cherry-pick --ff FETCH_HEAD'] + cmd = ["git cherry-pick --ff FETCH_HEAD"] if args.quiet: - cmd_out = open(os.devnull, 'wb') + cmd_out = open(os.devnull, "wb") else: cmd_out = None - result = subprocess.call(cmd, cwd=project_path, shell=True, stdout=cmd_out, stderr=cmd_out) + result = subprocess.call( + cmd, cwd=project_path, shell=True, stdout=cmd_out, stderr=cmd_out + ) if result != 0: - cmd = ['git diff-index --quiet HEAD --'] - result = subprocess.call(cmd, cwd=project_path, shell=True, stdout=cmd_out, stderr=cmd_out) + cmd = ["git diff-index --quiet HEAD --"] + result = subprocess.call( + cmd, cwd=project_path, shell=True, stdout=cmd_out, stderr=cmd_out + ) if result == 0: - print('WARNING: git command resulted with an empty commit, aborting cherry-pick') - cmd = ['git cherry-pick --abort'] - subprocess.call(cmd, cwd=project_path, shell=True, stdout=cmd_out, stderr=cmd_out) + print( + "WARNING: git command resulted with an empty commit, aborting cherry-pick" + ) + cmd = ["git cherry-pick --abort"] + subprocess.call( + cmd, + cwd=project_path, + shell=True, + stdout=cmd_out, + stderr=cmd_out, + ) elif args.reset: - print('ERROR: git command failed, aborting cherry-pick') - cmd = ['git cherry-pick --abort'] - subprocess.call(cmd, cwd=project_path, shell=True, stdout=cmd_out, stderr=cmd_out) + print("ERROR: git command failed, aborting cherry-pick") + cmd = ["git cherry-pick --abort"] + subprocess.call( + cmd, + cwd=project_path, + shell=True, + stdout=cmd_out, + stderr=cmd_out, + ) sys.exit(result) else: - print('ERROR: git command failed') + print("ERROR: git command failed") sys.exit(result) if not args.quiet: - print('') + print("")