Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(44)

Unified Diff: git_cl.py

Issue 13741015: Switch ChangeDescription usage to be stricter. (Closed) Base URL: https://chromium.googlesource.com/chromium/tools/depot_tools.git@master
Patch Set: append_line -> append_footer Created 7 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | tests/git_cl_test.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: git_cl.py
diff --git a/git_cl.py b/git_cl.py
index 35cfea3b7d3d9bb1d17df413008890c114b897b8..64cd9e86f091e00d0201b242187930fd393de70b 100755
--- a/git_cl.py
+++ b/git_cl.py
@@ -54,7 +54,8 @@ def DieWithError(message):
def RunCommand(args, error_ok=False, error_message=None, **kwargs):
try:
return subprocess2.check_output(args, shell=False, **kwargs)
- except subprocess2.CalledProcessError, e:
+ except subprocess2.CalledProcessError as e:
+ logging.debug('Failed running %s', args)
if not error_ok:
DieWithError(
'Command "%s" failed.\n%s' % (
@@ -797,44 +798,79 @@ def GetCodereviewSettingsInteractively():
class ChangeDescription(object):
"""Contains a parsed form of the change description."""
- def __init__(self, log_desc, reviewers):
- self.log_desc = log_desc
- self.reviewers = reviewers
- self.description = self.log_desc
-
- def Prompt(self):
- content = """# Enter a description of the change.
-# This will displayed on the codereview site.
-# The first line will also be used as the subject of the review.
-"""
- content += self.description
- if ('\nR=' not in self.description and
- '\nTBR=' not in self.description and
- self.reviewers):
- content += '\nR=' + self.reviewers
- if '\nBUG=' not in self.description:
- content += '\nBUG='
- content = content.rstrip('\n') + '\n'
- content = gclient_utils.RunEditor(content, True)
+ R_LINE = r'^\s*(TBR|R)\s*=\s*(.+)\s*$'
+
+ def __init__(self, description):
+ self._description = (description or '').strip()
+
+ @property
+ def description(self):
+ return self._description
+
+ def update_reviewers(self, reviewers):
+ """Rewrites the R=/TBR= line(s) as a single line."""
+ assert isinstance(reviewers, list), reviewers
+ if not reviewers:
+ return
+ regexp = re.compile(self.R_LINE, re.MULTILINE)
+ matches = list(regexp.finditer(self._description))
+ is_tbr = any(m.group(1) == 'TBR' for m in matches)
+ if len(matches) > 1:
+ # Erase all except the first one.
+ for i in xrange(len(matches) - 1, 0, -1):
+ self._description = (
+ self._description[:matches[i].start()] +
+ self._description[matches[i].end()+1:])
+
+ if is_tbr:
+ new_r_line = 'TBR=' + ', '.join(reviewers)
+ else:
+ new_r_line = 'R=' + ', '.join(reviewers)
+
+ if matches:
+ self._description = (
+ self._description[:matches[0].start()] + new_r_line +
+ self._description[matches[0].end()+1:])
+ else:
+ self.append_footer(new_r_line)
+
+ def prompt(self):
+ """Asks the user to update the description."""
+ self._description = (
+ '# Enter a description of the change.\n'
+ '# This will displayed on the codereview site.\n'
+ '# The first line will also be used as the subject of the review.\n'
+ ) + self._description
+
+ if '\nBUG=' not in self._description:
+ self.append_footer('BUG=')
+ content = gclient_utils.RunEditor(self._description, True)
if not content:
DieWithError('Running editor failed')
+
+ # Strip off comments.
content = re.compile(r'^#.*$', re.MULTILINE).sub('', content).strip()
- if not content.strip():
+ if not content:
DieWithError('No CL description, aborting')
- self.description = content
+ self._description = content
- def ParseDescription(self):
- """Updates the list of reviewers and subject from the description."""
- self.description = self.description.strip('\n') + '\n'
- # Retrieves all reviewer lines
- regexp = re.compile(r'^\s*(TBR|R)=(.+)$', re.MULTILINE)
- reviewers = ', '.join(
- i.group(2).strip() for i in regexp.finditer(self.description))
- if reviewers:
- self.reviewers = reviewers
+ def append_footer(self, line):
+ # Adds a LF if the last line did not have 'FOO=BAR' or if the new one isn't.
+ if self._description:
+ if '\n' not in self._description:
+ self._description += '\n'
+ else:
+ last_line = self._description.rsplit('\n', 1)[1]
+ if (not presubmit_support.Change.TAG_LINE_RE.match(last_line) or
+ not presubmit_support.Change.TAG_LINE_RE.match(line)):
+ self._description += '\n'
+ self._description += '\n' + line
- def IsEmpty(self):
- return not self.description
+ def get_reviewers(self):
+ """Retrieves the list of reviewers."""
+ regexp = re.compile(self.R_LINE, re.MULTILINE)
+ reviewers = [i.group(2) for i in regexp.finditer(self._description)]
+ return cleanup_list(reviewers)
def FindCodereviewSettingsFile(filename='codereview.settings'):
@@ -1102,16 +1138,15 @@ def GerritUpload(options, args, cl):
if options.target_branch:
branch = options.target_branch
- log_desc = options.message or CreateDescriptionFromLog(args)
- if CHANGE_ID not in log_desc:
- AddChangeIdToCommitMessage(options, args)
- if options.reviewers:
- log_desc += '\nR=' + ', '.join(options.reviewers)
- change_desc = ChangeDescription(log_desc, ', '.join(options.reviewers))
- change_desc.ParseDescription()
- if change_desc.IsEmpty():
+ change_desc = ChangeDescription(
+ options.message or CreateDescriptionFromLog(args))
+ if not change_desc.description:
print "Description is empty; aborting."
return 1
+ if CHANGE_ID not in change_desc.description:
+ AddChangeIdToCommitMessage(options, args)
+ if options.reviewers:
+ change_desc.update_reviewers(options.reviewers)
receive_options = []
cc = cl.GetCCList().split(',')
@@ -1120,11 +1155,9 @@ def GerritUpload(options, args, cl):
cc = filter(None, cc)
if cc:
receive_options += ['--cc=' + email for email in cc]
- if change_desc.reviewers:
- reviewers = filter(
- None, (r.strip() for r in change_desc.reviewers.split(',')))
- if reviewers:
- receive_options += ['--reviewer=' + email for email in reviewers]
+ if change_desc.get_reviewers():
+ receive_options.extend(
+ '--reviewer=' + email for email in change_desc.get_reviewers())
git_command = ['push']
if receive_options:
@@ -1160,24 +1193,21 @@ def RietveldUpload(options, args, cl):
if options.title:
upload_args.extend(['--title', options.title])
message = options.title or options.message or CreateDescriptionFromLog(args)
- change_desc = ChangeDescription(message, ','.join(options.reviewers))
+ change_desc = ChangeDescription(message)
+ if options.reviewers:
+ change_desc.update_reviewers(options.reviewers)
if not options.force:
- change_desc.Prompt()
- change_desc.ParseDescription()
+ change_desc.prompt()
- if change_desc.IsEmpty():
+ if not change_desc.description:
print "Description is empty; aborting."
return 1
upload_args.extend(['--message', change_desc.description])
- if change_desc.reviewers:
- upload_args.extend(
- [
- '--reviewers',
- ','.join(r.strip() for r in change_desc.reviewers.split(',')),
- ])
+ if change_desc.get_reviewers():
+ upload_args.append('--reviewers=' + ','.join(change_desc.get_reviewers()))
if options.send_mail:
- if not change_desc.reviewers:
+ if not change_desc.get_reviewers():
DieWithError("Must specify reviewers to send email.")
upload_args.append('--send_mail')
cc = ','.join(filter(None, (cl.GetCCList(), ','.join(options.cc))))
@@ -1440,24 +1470,28 @@ def SendUpstream(parser, args, cmd):
(cl.GetRietveldServer(), cl.GetIssue()),
verbose=False)
- description = options.message
- if not description and cl.GetIssue():
- description = cl.GetDescription()
+ change_desc = ChangeDescription(options.message)
+ if not change_desc.description and cl.GetIssue():
+ change_desc = ChangeDescription(cl.GetDescription())
- if not description:
+ if not change_desc.description:
if not cl.GetIssue() and options.bypass_hooks:
- description = CreateDescriptionFromLog([base_branch])
+ change_desc = ChangeDescription(CreateDescriptionFromLog([base_branch]))
else:
print 'No description set.'
print 'Visit %s/edit to set it.' % (cl.GetIssueURL())
return 1
+ # Keep a separate copy for the commit message, because the commit message
+ # contains the link to the Rietveld issue, while the Rietveld message contains
+ # the commit viewvc url.
+ commit_desc = ChangeDescription(change_desc.description)
if cl.GetIssue():
- description += "\n\nReview URL: %s" % cl.GetIssueURL()
-
+ commit_desc.append_footer('Review URL: %s' % cl.GetIssueURL())
if options.contributor:
- description += "\nPatch from %s." % options.contributor
- print 'Description:', repr(description)
+ commit_desc.append_footer('Patch from %s.' % options.contributor)
+
+ print 'Description:', repr(commit_desc.description)
branches = [base_branch, cl.GetBranchRef()]
if not options.force:
@@ -1493,9 +1527,13 @@ def SendUpstream(parser, args, cmd):
RunGit(['checkout', '-q', '-b', MERGE_BRANCH])
RunGit(['reset', '--soft', base_branch])
if options.contributor:
- RunGit(['commit', '--author', options.contributor, '-m', description])
+ RunGit(
+ [
+ 'commit', '--author', options.contributor,
+ '-m', commit_desc.description,
+ ])
else:
- RunGit(['commit', '-m', description])
+ RunGit(['commit', '-m', commit_desc.description])
if base_has_submodules:
cherry_pick_commit = RunGit(['rev-list', 'HEAD^!']).rstrip()
RunGit(['branch', CHERRY_PICK_BRANCH, svn_head])
@@ -1534,12 +1572,12 @@ def SendUpstream(parser, args, cmd):
return 1
viewvc_url = settings.GetViewVCUrl()
if viewvc_url and revision:
- cl.description += ('\n\nCommitted: ' + viewvc_url + revision)
+ change_desc.append_footer('Committed: ' + viewvc_url + revision)
elif revision:
- cl.description += ('\n\nCommitted: ' + revision)
+ change_desc.append_footer('Committed: ' + revision)
print ('Closing issue '
'(you may be prompted for your codereview password)...')
- cl.UpdateDescription(cl.description)
+ cl.UpdateDescription(change_desc.description)
cl.CloseIssue()
props = cl.RpcServer().get_issue_properties(cl.GetIssue(), False)
patch_num = len(props['patchsets'])
« no previous file with comments | « no previous file | tests/git_cl_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698