git cl: implement git number footer generation.
BUG=642493
Change-Id: Ic8eb121b0ad7adcc7a3f3f1967ef2261f415e731
Reviewed-on: https://chromium-review.googlesource.com/414466
Reviewed-by: Michael Achenbach <machenbach@chromium.org>
Reviewed-by: Robbie Iannucci <iannucci@chromium.org>
diff --git a/git_cl.py b/git_cl.py
index 2646fd6..4b91f5d 100755
--- a/git_cl.py
+++ b/git_cl.py
@@ -2999,6 +2999,7 @@
R_LINE = r'^[ \t]*(TBR|R)[ \t]*=[ \t]*(.*?)[ \t]*$'
CC_LINE = r'^[ \t]*(CC)[ \t]*=[ \t]*(.*?)[ \t]*$'
BUG_LINE = r'^[ \t]*(BUG)[ \t]*=[ \t]*(.*?)[ \t]*$'
+ CHERRY_PICK_LINE = r'^\(cherry picked from commit [a-fA-F0-9]{40}\)$'
def __init__(self, description):
self._description_lines = (description or '').strip().splitlines()
@@ -3151,6 +3152,57 @@
cced = [match.group(2).strip() for match in matches if match]
return cleanup_list(cced)
+ def update_with_git_number_footers(self, parent_hash, parent_msg, dest_ref):
+ """Updates this commit description given the parent.
+
+ This is essentially what Gnumbd used to do.
+ Consult https://goo.gl/WMmpDe for more details.
+ """
+ assert parent_msg # No, orphan branch creation isn't supported.
+ assert parent_hash
+ assert dest_ref
+ parent_footer_map = git_footers.parse_footers(parent_msg)
+ # This will also happily parse svn-position, which GnumbD is no longer
+ # supporting. While we'd generate correct footers, the verifier plugin
+ # installed in Gerrit will block such commit (ie git push below will fail).
+ parent_position = git_footers.get_position(parent_footer_map)
+
+ # Cherry-picks may have last line obscuring their prior footers,
+ # from git_footers perspective. This is also what Gnumbd did.
+ cp_line = None
+ if (self._description_lines and
+ re.match(self.CHERRY_PICK_LINE, self._description_lines[-1])):
+ cp_line = self._description_lines.pop()
+
+ top_lines, _, parsed_footers = git_footers.split_footers(self.description)
+
+ # Original-ify all Cr- footers, to avoid re-lands, cherry-picks, or just
+ # user interference with actual footers we'd insert below.
+ for i, (k, v) in enumerate(parsed_footers):
+ if k.startswith('Cr-'):
+ parsed_footers[i] = (k.replace('Cr-', 'Cr-Original-'), v)
+
+ # Add Position and Lineage footers based on the parent.
+ lineage = parent_footer_map.get('Cr-Branched-From', [])
+ if parent_position[0] == dest_ref:
+ # Same branch as parent.
+ number = int(parent_position[1]) + 1
+ else:
+ number = 1 # New branch, and extra lineage.
+ lineage.insert(0, '%s-%s@{#%d}' % (parent_hash, parent_position[0],
+ int(parent_position[1])))
+
+ parsed_footers.append(('Cr-Commit-Position',
+ '%s@{#%d}' % (dest_ref, number)))
+ parsed_footers.extend(('Cr-Branched-From', v) for v in lineage)
+
+ self._description_lines = top_lines
+ if cp_line:
+ self._description_lines.append(cp_line)
+ if self._description_lines[-1] != '':
+ self._description_lines.append('') # Ensure footer separator.
+ self._description_lines.extend('%s: %s' % kv for kv in parsed_footers)
+
def get_approving_reviewers(props):
"""Retrieves the reviewers that approved a CL from the issue properties with
@@ -4441,6 +4493,21 @@
mirror = settings.GetGitMirror(remote)
pushurl = mirror.url if mirror else remote
pending_prefix = settings.GetPendingRefPrefix()
+
+ if ShouldGenerateGitNumberFooters():
+ # TODO(tandrii): run git fetch in a loop + autorebase when there there
+ # is no pending ref to push to?
+ logging.debug('Adding git number footers')
+ parent_msg = RunGit(['show', '-s', '--format=%B', merge_base]).strip()
+ commit_desc.update_with_git_number_footers(merge_base, parent_msg,
+ branch)
+ # TODO(tandrii): timestamp handling is missing here.
+ RunGitSilent(['commit', '--amend', '-m', commit_desc.description])
+ change_desc = ChangeDescription(commit_desc.description)
+ # If gnumbd is sitll ON and we ultimately push to branch with
+ # pending_prefix, gnumbd will modify footers we've just inserted with
+ # 'Original-', which is annoying but still technically correct.
+
if not pending_prefix or branch.startswith(pending_prefix):
# If not using refs/pending/heads/* at all, or target ref is already set
# to pending, then push to the target ref directly.
@@ -4507,6 +4574,7 @@
killed = True
if cl.GetIssue():
+ # TODO(tandrii): figure out story of to pending + git numberer.
to_pending = ' to pending queue' if pushed_to_pending else ''
viewvc_url = settings.GetViewVCUrl()
if not to_pending: