Fix various type annotations in git_cl.py as reported by pytype.
Most of the changes are straightforward typo fixes.
Two more significant changes:
- the entire ChangeDescription class was moved earlier in the file so
that it is defined before any type annotations that reference it.
- annotations that use list[...] have been changed to use List[...].
While this is supported >= python3.9, it seems some parts of the
Python toolchain (e.g. Python formatting) do not support this yet.
Change-Id: Ifb60f10e2424f79a19229328d19d9fd1d0e87dea
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/tools/depot_tools/+/4824618
Reviewed-by: Gavin Mak <gavinmak@google.com>
Commit-Queue: Daniel Cheng <dcheng@chromium.org>
diff --git a/git_cl.py b/git_cl.py
index 0b879d4..98108da 100755
--- a/git_cl.py
+++ b/git_cl.py
@@ -30,6 +30,10 @@
import zlib
from third_party import colorama
+from typing import Any
+from typing import List
+from typing import Mapping
+from typing import NoReturn
from typing import Optional
from typing import Sequence
from typing import Tuple
@@ -167,7 +171,7 @@
pass
-def DieWithError(message, change_desc=None):
+def DieWithError(message, change_desc=None) -> NoReturn:
if change_desc:
SaveDescriptionBackup(change_desc)
print('\n ** Content of CL description **\n' +
@@ -1020,6 +1024,228 @@
])
+class ChangeDescription(object):
+ """Contains a parsed form of the change description."""
+ R_LINE = r'^[ \t]*(TBR|R)[ \t]*=[ \t]*(.*?)[ \t]*$'
+ CC_LINE = r'^[ \t]*(CC)[ \t]*=[ \t]*(.*?)[ \t]*$'
+ BUG_LINE = r'^[ \t]*(?:(BUG)[ \t]*=|Bug:)[ \t]*(.*?)[ \t]*$'
+ FIXED_LINE = r'^[ \t]*Fixed[ \t]*:[ \t]*(.*?)[ \t]*$'
+ CHERRY_PICK_LINE = r'^\(cherry picked from commit [a-fA-F0-9]{40}\)$'
+ STRIP_HASH_TAG_PREFIX = r'^(\s*(revert|reland)( "|:)?\s*)*'
+ BRACKET_HASH_TAG = r'\s*\[([^\[\]]+)\]'
+ COLON_SEPARATED_HASH_TAG = r'^([a-zA-Z0-9_\- ]+):($|[^:])'
+ BAD_HASH_TAG_CHUNK = r'[^a-zA-Z0-9]+'
+
+ def __init__(self, description, bug=None, fixed=None):
+ self._description_lines = (description or '').strip().splitlines()
+ if bug:
+ regexp = re.compile(self.BUG_LINE)
+ prefix = settings.GetBugPrefix()
+ if not any((regexp.match(line) for line in self._description_lines)):
+ values = list(_get_bug_line_values(prefix, bug))
+ self.append_footer('Bug: %s' % ', '.join(values))
+ if fixed:
+ regexp = re.compile(self.FIXED_LINE)
+ prefix = settings.GetBugPrefix()
+ if not any((regexp.match(line) for line in self._description_lines)):
+ values = list(_get_bug_line_values(prefix, fixed))
+ self.append_footer('Fixed: %s' % ', '.join(values))
+
+ @property # www.logilab.org/ticket/89786
+ def description(self): # pylint: disable=method-hidden
+ return '\n'.join(self._description_lines)
+
+ def set_description(self, desc):
+ if isinstance(desc, str):
+ lines = desc.splitlines()
+ else:
+ lines = [line.rstrip() for line in desc]
+ while lines and not lines[0]:
+ lines.pop(0)
+ while lines and not lines[-1]:
+ lines.pop(-1)
+ self._description_lines = lines
+
+ def ensure_change_id(self, change_id):
+ description = self.description
+ footer_change_ids = git_footers.get_footer_change_id(description)
+ # Make sure that the Change-Id in the description matches the given one.
+ if footer_change_ids != [change_id]:
+ if footer_change_ids:
+ # Remove any existing Change-Id footers since they don't match the
+ # expected change_id footer.
+ description = git_footers.remove_footer(description, 'Change-Id')
+ print('WARNING: Change-Id has been set to %s. Use `git cl issue 0` '
+ 'if you want to set a new one.')
+ # Add the expected Change-Id footer.
+ description = git_footers.add_footer_change_id(description, change_id)
+ self.set_description(description)
+
+ def update_reviewers(self, reviewers):
+ """Rewrites the R= line(s) as a single line each.
+
+ Args:
+ reviewers (list(str)) - list of additional emails to use for reviewers.
+ """
+ if not reviewers:
+ return
+
+ reviewers = set(reviewers)
+
+ # Get the set of R= lines and remove them from the description.
+ regexp = re.compile(self.R_LINE)
+ matches = [regexp.match(line) for line in self._description_lines]
+ new_desc = [
+ l for i, l in enumerate(self._description_lines) if not matches[i]
+ ]
+ self.set_description(new_desc)
+
+ # Construct new unified R= lines.
+
+ # First, update reviewers with names from the R= lines (if any).
+ for match in matches:
+ if not match:
+ continue
+ reviewers.update(cleanup_list([match.group(2).strip()]))
+
+ new_r_line = 'R=' + ', '.join(sorted(reviewers))
+
+ # Put the new lines in the description where the old first R= line was.
+ line_loc = next((i for i, match in enumerate(matches) if match), -1)
+ if 0 <= line_loc < len(self._description_lines):
+ self._description_lines.insert(line_loc, new_r_line)
+ else:
+ self.append_footer(new_r_line)
+
+ def set_preserve_tryjobs(self):
+ """Ensures description footer contains 'Cq-Do-Not-Cancel-Tryjobs: true'."""
+ footers = git_footers.parse_footers(self.description)
+ for v in footers.get('Cq-Do-Not-Cancel-Tryjobs', []):
+ if v.lower() == 'true':
+ return
+ self.append_footer('Cq-Do-Not-Cancel-Tryjobs: true')
+
+ def prompt(self):
+ """Asks the user to update the description."""
+ self.set_description([
+ '# Enter a description of the change.',
+ '# This will be displayed on the codereview site.',
+ '# The first line will also be used as the subject of the review.',
+ '#--------------------This line is 72 characters long'
+ '--------------------',
+ ] + self._description_lines)
+ bug_regexp = re.compile(self.BUG_LINE)
+ fixed_regexp = re.compile(self.FIXED_LINE)
+ prefix = settings.GetBugPrefix()
+ has_issue = lambda l: bug_regexp.match(l) or fixed_regexp.match(l)
+
+ if not any((has_issue(line) for line in self._description_lines)):
+ self.append_footer('Bug: %s' % prefix)
+
+ print('Waiting for editor...')
+ content = gclient_utils.RunEditor(self.description,
+ True,
+ git_editor=settings.GetGitEditor())
+ if not content:
+ DieWithError('Running editor failed')
+ lines = content.splitlines()
+
+ # Strip off comments and default inserted "Bug:" line.
+ clean_lines = [
+ line.rstrip() for line in lines
+ if not (line.startswith('#') or line.rstrip() == "Bug:"
+ or line.rstrip() == "Bug: " + prefix)
+ ]
+ if not clean_lines:
+ DieWithError('No CL description, aborting')
+ self.set_description(clean_lines)
+
+ def append_footer(self, line):
+ """Adds a footer line to the description.
+
+ Differentiates legacy "KEY=xxx" footers (used to be called tags) and
+ Gerrit's footers in the form of "Footer-Key: footer any value" and ensures
+ that Gerrit footers are always at the end.
+ """
+ parsed_footer_line = git_footers.parse_footer(line)
+ if parsed_footer_line:
+ # Line is a gerrit footer in the form: Footer-Key: any value.
+ # Thus, must be appended observing Gerrit footer rules.
+ self.set_description(
+ git_footers.add_footer(self.description,
+ key=parsed_footer_line[0],
+ value=parsed_footer_line[1]))
+ return
+
+ if not self._description_lines:
+ self._description_lines.append(line)
+ return
+
+ top_lines, gerrit_footers, _ = git_footers.split_footers(self.description)
+ if gerrit_footers:
+ # git_footers.split_footers ensures that there is an empty line before
+ # actual (gerrit) footers, if any. We have to keep it that way.
+ assert top_lines and top_lines[-1] == ''
+ top_lines, separator = top_lines[:-1], top_lines[-1:]
+ else:
+ separator = [] # No need for separator if there are no gerrit_footers.
+
+ prev_line = top_lines[-1] if top_lines else ''
+ if (not presubmit_support.Change.TAG_LINE_RE.match(prev_line)
+ or not presubmit_support.Change.TAG_LINE_RE.match(line)):
+ top_lines.append('')
+ top_lines.append(line)
+ self._description_lines = top_lines + separator + gerrit_footers
+
+ def get_reviewers(self, tbr_only=False):
+ """Retrieves the list of reviewers."""
+ matches = [re.match(self.R_LINE, line) for line in self._description_lines]
+ reviewers = [
+ match.group(2).strip() for match in matches
+ if match and (not tbr_only or match.group(1).upper() == 'TBR')
+ ]
+ return cleanup_list(reviewers)
+
+ def get_cced(self):
+ """Retrieves the list of reviewers."""
+ matches = [re.match(self.CC_LINE, line) for line in self._description_lines]
+ cced = [match.group(2).strip() for match in matches if match]
+ return cleanup_list(cced)
+
+ def get_hash_tags(self):
+ """Extracts and sanitizes a list of Gerrit hashtags."""
+ subject = (self._description_lines or ('', ))[0]
+ subject = re.sub(self.STRIP_HASH_TAG_PREFIX,
+ '',
+ subject,
+ flags=re.IGNORECASE)
+
+ tags = []
+ start = 0
+ bracket_exp = re.compile(self.BRACKET_HASH_TAG)
+ while True:
+ m = bracket_exp.match(subject, start)
+ if not m:
+ break
+ tags.append(self.sanitize_hash_tag(m.group(1)))
+ start = m.end()
+
+ if not tags:
+ # Try "Tag: " prefix.
+ m = re.match(self.COLON_SEPARATED_HASH_TAG, subject)
+ if m:
+ tags.append(self.sanitize_hash_tag(m.group(1)))
+ return tags
+
+ @classmethod
+ def sanitize_hash_tag(cls, tag):
+ """Returns a sanitized Gerrit hash tag.
+
+ A sanitized hashtag can be used as a git push refspec parameter value.
+ """
+ return re.sub(cls.BAD_HASH_TAG_CHUNK, '-', tag).strip('-').lower()
+
+
class Changelist(object):
"""Changelist works with one changelist in local branch.
@@ -1199,7 +1425,7 @@
self._remote = (remote, 'refs/remotes/%s/%s' % (remote, branch))
return self._remote
- def GetRemoteUrl(self):
+ def GetRemoteUrl(self) -> Optional[str]:
"""Return the configured remote URL, e.g. 'git://example.org/foo.git/'.
Returns None if there is no remote.
@@ -1340,8 +1566,9 @@
self.issue = None
self.patchset = None
- def GetAffectedFiles(self, upstream, end_commit=None):
- # type: (str, Optional[str]) -> Sequence[str]
+ def GetAffectedFiles(self,
+ upstream: str,
+ end_commit: Optional[str] = None) -> Sequence[str]:
"""Returns the list of affected files for the given commit range."""
try:
return [
@@ -1442,13 +1669,11 @@
realm=realm)
def _RunPresubmit(self,
- args,
- description,
- resultdb=None,
- realm=None):
- # type: (Sequence[str], str, bool, Optional[bool], Optional[str]
- # ) -> Mapping[str, Any]
- args = args[:]
+ args: Sequence[str],
+ description: str,
+ resultdb: bool = False,
+ realm: Optional[str] = None) -> Mapping[str, Any]:
+ args = list(args)
with gclient_utils.temporary_file() as description_file:
with gclient_utils.temporary_file() as json_output:
@@ -1484,9 +1709,9 @@
args.extend(['--description_file', description_file])
subprocess2.Popen(['vpython3', PRESUBMIT_SUPPORT] + args).wait()
- def _GetDescriptionForUpload(self, options, git_diff_args, files):
- # type: (optparse.Values, Sequence[str], Sequence[str]
- # ) -> ChangeDescription
+ def _GetDescriptionForUpload(self, options: optparse.Values,
+ git_diff_args: Sequence[str],
+ files: Sequence[str]) -> ChangeDescription:
"""Get description message for upload."""
if self.GetIssue():
description = self.FetchDescription()
@@ -1570,13 +1795,10 @@
return user_title or title
def _GetRefSpecOptions(self,
- options,
- change_desc,
- multi_change_upload=False,
- dogfood_path=False):
- # type: (optparse.Values, Sequence[Changelist], Optional[bool],
- # Optional[bool]) -> Sequence[str]
-
+ options: optparse.Values,
+ change_desc: ChangeDescription,
+ multi_change_upload: bool = False,
+ dogfood_path: bool = False) -> List[str]:
# Extra options that can be specified at push time. Doc:
# https://gerrit-review.googlesource.com/Documentation/user-upload.html
refspec_opts = []
@@ -1647,11 +1869,10 @@
return refspec_opts
def PrepareSquashedCommit(self,
- options,
- parent,
- orig_parent,
- end_commit=None):
- # type: (optparse.Values, str, str, Optional[str]) -> _NewUpload()
+ options: optparse.Values,
+ parent: str,
+ orig_parent: str,
+ end_commit: Optional[str] = None) -> _NewUpload:
"""Create a squashed commit to upload.
@@ -1683,8 +1904,8 @@
return _NewUpload(reviewers, ccs, commit_to_push, end_commit, parent,
change_desc, prev_patchset)
- def PrepareCherryPickSquashedCommit(self, options, parent):
- # type: (optparse.Values, str) -> _NewUpload()
+ def PrepareCherryPickSquashedCommit(self, options: optparse.Values,
+ parent: str) -> _NewUpload:
"""Create a commit cherry-picked on parent to push."""
# The `parent` is what we will cherry-pick on top of.
@@ -1719,9 +1940,9 @@
return _NewUpload(reviewers, ccs, commit_to_push, new_upload_hash,
cherry_pick_base, change_desc, prev_patchset)
- def _PrepareChange(self, options, parent, end_commit):
- # type: (optparse.Values, str, str) ->
- # Tuple[Sequence[str], Sequence[str], ChangeDescription]
+ def _PrepareChange(
+ self, options: optparse.Values, parent: str, end_commit: str
+ ) -> Tuple[Sequence[str], Sequence[str], ChangeDescription]:
"""Prepares the change to be uploaded."""
self.EnsureCanUploadPatchset(options.force)
@@ -1790,8 +2011,8 @@
return change_desc.get_reviewers(), ccs, change_desc
- def PostUploadUpdates(self, options, new_upload, change_number):
- # type: (optparse.Values, _NewUpload, change_number) -> None
+ def PostUploadUpdates(self, options: optparse.Values, new_upload: _NewUpload,
+ change_number: str) -> None:
"""Makes necessary post upload changes to the local and remote cl."""
if not self.GetIssue():
self.SetIssue(change_number)
@@ -2609,10 +2830,10 @@
env['GIT_TRACE_CURL_NO_DATA'] = '1'
env['GIT_TRACE_PACKET'] = os.path.join(traces_dir, 'trace-packet')
+ push_returncode = 0
+ before_push = time_time()
try:
- push_returncode = 0
remote_url = self.GetRemoteUrl()
- before_push = time_time()
push_cmd = ['git', 'push', remote_url, refspec]
if git_push_options:
for opt in git_push_options:
@@ -3160,222 +3381,6 @@
yield other
-class ChangeDescription(object):
- """Contains a parsed form of the change description."""
- R_LINE = r'^[ \t]*(TBR|R)[ \t]*=[ \t]*(.*?)[ \t]*$'
- CC_LINE = r'^[ \t]*(CC)[ \t]*=[ \t]*(.*?)[ \t]*$'
- BUG_LINE = r'^[ \t]*(?:(BUG)[ \t]*=|Bug:)[ \t]*(.*?)[ \t]*$'
- FIXED_LINE = r'^[ \t]*Fixed[ \t]*:[ \t]*(.*?)[ \t]*$'
- CHERRY_PICK_LINE = r'^\(cherry picked from commit [a-fA-F0-9]{40}\)$'
- STRIP_HASH_TAG_PREFIX = r'^(\s*(revert|reland)( "|:)?\s*)*'
- BRACKET_HASH_TAG = r'\s*\[([^\[\]]+)\]'
- COLON_SEPARATED_HASH_TAG = r'^([a-zA-Z0-9_\- ]+):($|[^:])'
- BAD_HASH_TAG_CHUNK = r'[^a-zA-Z0-9]+'
-
- def __init__(self, description, bug=None, fixed=None):
- self._description_lines = (description or '').strip().splitlines()
- if bug:
- regexp = re.compile(self.BUG_LINE)
- prefix = settings.GetBugPrefix()
- if not any((regexp.match(line) for line in self._description_lines)):
- values = list(_get_bug_line_values(prefix, bug))
- self.append_footer('Bug: %s' % ', '.join(values))
- if fixed:
- regexp = re.compile(self.FIXED_LINE)
- prefix = settings.GetBugPrefix()
- if not any((regexp.match(line) for line in self._description_lines)):
- values = list(_get_bug_line_values(prefix, fixed))
- self.append_footer('Fixed: %s' % ', '.join(values))
-
- @property # www.logilab.org/ticket/89786
- def description(self): # pylint: disable=method-hidden
- return '\n'.join(self._description_lines)
-
- def set_description(self, desc):
- if isinstance(desc, str):
- lines = desc.splitlines()
- else:
- lines = [line.rstrip() for line in desc]
- while lines and not lines[0]:
- lines.pop(0)
- while lines and not lines[-1]:
- lines.pop(-1)
- self._description_lines = lines
-
- def ensure_change_id(self, change_id):
- description = self.description
- footer_change_ids = git_footers.get_footer_change_id(description)
- # Make sure that the Change-Id in the description matches the given one.
- if footer_change_ids != [change_id]:
- if footer_change_ids:
- # Remove any existing Change-Id footers since they don't match the
- # expected change_id footer.
- description = git_footers.remove_footer(description, 'Change-Id')
- print('WARNING: Change-Id has been set to %s. Use `git cl issue 0` '
- 'if you want to set a new one.')
- # Add the expected Change-Id footer.
- description = git_footers.add_footer_change_id(description, change_id)
- self.set_description(description)
-
- def update_reviewers(self, reviewers):
- """Rewrites the R= line(s) as a single line each.
-
- Args:
- reviewers (list(str)) - list of additional emails to use for reviewers.
- """
- if not reviewers:
- return
-
- reviewers = set(reviewers)
-
- # Get the set of R= lines and remove them from the description.
- regexp = re.compile(self.R_LINE)
- matches = [regexp.match(line) for line in self._description_lines]
- new_desc = [l for i, l in enumerate(self._description_lines)
- if not matches[i]]
- self.set_description(new_desc)
-
- # Construct new unified R= lines.
-
- # First, update reviewers with names from the R= lines (if any).
- for match in matches:
- if not match:
- continue
- reviewers.update(cleanup_list([match.group(2).strip()]))
-
- new_r_line = 'R=' + ', '.join(sorted(reviewers))
-
- # Put the new lines in the description where the old first R= line was.
- line_loc = next((i for i, match in enumerate(matches) if match), -1)
- if 0 <= line_loc < len(self._description_lines):
- self._description_lines.insert(line_loc, new_r_line)
- else:
- self.append_footer(new_r_line)
-
- def set_preserve_tryjobs(self):
- """Ensures description footer contains 'Cq-Do-Not-Cancel-Tryjobs: true'."""
- footers = git_footers.parse_footers(self.description)
- for v in footers.get('Cq-Do-Not-Cancel-Tryjobs', []):
- if v.lower() == 'true':
- return
- self.append_footer('Cq-Do-Not-Cancel-Tryjobs: true')
-
- def prompt(self):
- """Asks the user to update the description."""
- self.set_description([
- '# Enter a description of the change.',
- '# This will be displayed on the codereview site.',
- '# The first line will also be used as the subject of the review.',
- '#--------------------This line is 72 characters long'
- '--------------------',
- ] + self._description_lines)
- bug_regexp = re.compile(self.BUG_LINE)
- fixed_regexp = re.compile(self.FIXED_LINE)
- prefix = settings.GetBugPrefix()
- has_issue = lambda l: bug_regexp.match(l) or fixed_regexp.match(l)
-
- if not any((has_issue(line) for line in self._description_lines)):
- self.append_footer('Bug: %s' % prefix)
-
- print('Waiting for editor...')
- content = gclient_utils.RunEditor(self.description, True,
- git_editor=settings.GetGitEditor())
- if not content:
- DieWithError('Running editor failed')
- lines = content.splitlines()
-
- # Strip off comments and default inserted "Bug:" line.
- clean_lines = [line.rstrip() for line in lines if not
- (line.startswith('#') or
- line.rstrip() == "Bug:" or
- line.rstrip() == "Bug: " + prefix)]
- if not clean_lines:
- DieWithError('No CL description, aborting')
- self.set_description(clean_lines)
-
- def append_footer(self, line):
- """Adds a footer line to the description.
-
- Differentiates legacy "KEY=xxx" footers (used to be called tags) and
- Gerrit's footers in the form of "Footer-Key: footer any value" and ensures
- that Gerrit footers are always at the end.
- """
- parsed_footer_line = git_footers.parse_footer(line)
- if parsed_footer_line:
- # Line is a gerrit footer in the form: Footer-Key: any value.
- # Thus, must be appended observing Gerrit footer rules.
- self.set_description(
- git_footers.add_footer(self.description,
- key=parsed_footer_line[0],
- value=parsed_footer_line[1]))
- return
-
- if not self._description_lines:
- self._description_lines.append(line)
- return
-
- top_lines, gerrit_footers, _ = git_footers.split_footers(self.description)
- if gerrit_footers:
- # git_footers.split_footers ensures that there is an empty line before
- # actual (gerrit) footers, if any. We have to keep it that way.
- assert top_lines and top_lines[-1] == ''
- top_lines, separator = top_lines[:-1], top_lines[-1:]
- else:
- separator = [] # No need for separator if there are no gerrit_footers.
-
- prev_line = top_lines[-1] if top_lines else ''
- if (not presubmit_support.Change.TAG_LINE_RE.match(prev_line) or
- not presubmit_support.Change.TAG_LINE_RE.match(line)):
- top_lines.append('')
- top_lines.append(line)
- self._description_lines = top_lines + separator + gerrit_footers
-
- def get_reviewers(self, tbr_only=False):
- """Retrieves the list of reviewers."""
- matches = [re.match(self.R_LINE, line) for line in self._description_lines]
- reviewers = [match.group(2).strip()
- for match in matches
- if match and (not tbr_only or match.group(1).upper() == 'TBR')]
- return cleanup_list(reviewers)
-
- def get_cced(self):
- """Retrieves the list of reviewers."""
- matches = [re.match(self.CC_LINE, line) for line in self._description_lines]
- cced = [match.group(2).strip() for match in matches if match]
- return cleanup_list(cced)
-
- def get_hash_tags(self):
- """Extracts and sanitizes a list of Gerrit hashtags."""
- subject = (self._description_lines or ('',))[0]
- subject = re.sub(
- self.STRIP_HASH_TAG_PREFIX, '', subject, flags=re.IGNORECASE)
-
- tags = []
- start = 0
- bracket_exp = re.compile(self.BRACKET_HASH_TAG)
- while True:
- m = bracket_exp.match(subject, start)
- if not m:
- break
- tags.append(self.sanitize_hash_tag(m.group(1)))
- start = m.end()
-
- if not tags:
- # Try "Tag: " prefix.
- m = re.match(self.COLON_SEPARATED_HASH_TAG, subject)
- if m:
- tags.append(self.sanitize_hash_tag(m.group(1)))
- return tags
-
- @classmethod
- def sanitize_hash_tag(cls, tag):
- """Returns a sanitized Gerrit hash tag.
-
- A sanitized hashtag can be used as a git push refspec parameter value.
- """
- return re.sub(cls.BAD_HASH_TAG_CHUNK, '-', tag).strip('-').lower()
-
-
def FindCodereviewSettingsFile(filename='codereview.settings'):
"""Finds the given file starting in the cwd and going up.
@@ -4419,7 +4424,9 @@
# Process cpplint arguments, if any.
filters = presubmit_canned_checks.GetCppLintFilters(options.filter)
- command = ['--filter=' + ','.join(filters)] + args + files
+ command = ['--filter=' + ','.join(filters)]
+ command.extend(args)
+ command.extend(files)
filenames = cpplint.ParseArguments(command)
include_regex = re.compile(settings.GetLintRegex())
@@ -4869,7 +4876,7 @@
cls, cherry_pick_current = _UploadAllPrecheck(options, orig_args)
# Create commits.
- uploads_by_cl: list[Tuple[Changelist, _NewUpload]] = []
+ uploads_by_cl: List[Tuple[Changelist, _NewUpload]] = []
if cherry_pick_current:
parent = cls[1]._GitGetBranchConfigValue(GERRIT_SQUASH_HASH_CONFIG_KEY)
new_upload = cls[0].PrepareCherryPickSquashedCommit(options, parent)