Index: git_cl/git_cl.py |
diff --git a/git_cl/git_cl.py b/git_cl/git_cl.py |
index ec3bd9770f0fba980cc42c68618548075de85467..c729f9b1cf0c6b2005f6522599576e3710e54b28 100644 |
--- a/git_cl/git_cl.py |
+++ b/git_cl/git_cl.py |
@@ -54,7 +54,8 @@ def Popen(cmd, **kwargs): |
def RunCommand(cmd, error_ok=False, error_message=None, |
- redirect_stdout=True, swallow_stderr=False, **kwargs): |
+ redirect_stdout=True, swallow_stderr=False, |
+ tee_filter=None, **kwargs): |
if redirect_stdout: |
stdout = subprocess.PIPE |
else: |
@@ -64,7 +65,18 @@ def RunCommand(cmd, error_ok=False, error_message=None, |
else: |
stderr = None |
proc = Popen(cmd, stdout=stdout, stderr=stderr, **kwargs) |
- output = proc.communicate()[0] |
+ if tee_filter: |
+ output = '' |
+ while True: |
+ line = proc.stdout.readline() |
+ if line == '' and proc.poll() != None: |
+ break |
+ if not tee_filter(line): |
+ print line |
+ output += line + '\n' |
+ else: |
+ output = proc.communicate()[0] |
+ |
if not error_ok and proc.returncode != 0: |
DieWithError('Command "%s" failed.\n' % (' '.join(cmd)) + |
(error_message or output or '')) |
@@ -479,6 +491,76 @@ def GetCodereviewSettingsInteractively(): |
# svn-based hackery. |
+class HookResults(object): |
+ """Contains the parsed output of the presubmit hooks.""" |
+ def __init__(self, output_from_hooks=None): |
+ self.reviewers = [] |
+ self.output = None |
+ self._ParseOutputFromHooks(output_from_hooks) |
+ |
+ def _ParseOutputFromHooks(self, output_from_hooks): |
+ if not output_from_hooks: |
+ return |
+ lines = [] |
+ reviewers = [] |
+ reviewer_regexp = re.compile('ADD: R=(.+)') |
+ for l in output_from_hooks.splitlines(): |
+ m = reviewer_regexp.match(l) |
+ if m: |
+ reviewers.extend(m.group(1).split(',')) |
+ else: |
+ lines.append(l) |
+ self.output = '\n'.join(lines) |
+ self.reviewers = ','.join(reviewers) |
+ |
+ |
+class ChangeDescription(object): |
+ """Contains a parsed form of the change description.""" |
+ def __init__(self, subject, log_desc, reviewers): |
+ self.subject = subject |
+ self.log_desc = log_desc |
+ self.reviewers = reviewers |
+ self.description = self.log_desc |
+ |
+ def Update(self): |
+ initial_text = """# 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. |
+""" |
+ initial_text += self.description |
+ if 'R=' not in self.description and self.reviewers: |
+ initial_text += '\nR=' + self.reviewers |
+ if 'BUG=' not in self.description: |
+ initial_text += '\nBUG=' |
+ if 'TEST=' not in self.description: |
+ initial_text += '\nTEST=' |
+ self._ParseDescription(UserEditedLog(initial_text)) |
+ |
+ def _ParseDescription(self, description): |
+ if not description: |
+ self.description = description |
+ return |
+ |
+ parsed_lines = [] |
+ reviewers_regexp = re.compile('\s*R=(.+)') |
+ reviewers = '' |
+ subject = '' |
+ for l in description.splitlines(): |
+ if not subject: |
+ subject = l |
+ matched_reviewers = reviewers_regexp.match(l) |
+ if matched_reviewers: |
+ reviewers = matched_reviewers.group(1) |
+ parsed_lines.append(l) |
+ |
+ self.description = '\n'.join(parsed_lines) + '\n' |
+ self.subject = subject |
+ self.reviewers = reviewers |
+ |
+ def IsEmpty(self): |
+ return not self.description |
+ |
+ |
def FindCodereviewSettingsFile(filename='codereview.settings'): |
"""Finds the given file starting in the cwd and going up. |
@@ -669,13 +751,19 @@ def UserEditedLog(starting_text): |
return stripcomment_re.sub('', text).strip() |
-def RunHook(hook, upstream_branch, error_ok=False): |
+def RunHook(hook, upstream_branch, rietveld_server, tbr=False, |
+ error_ok=False): |
"""Run a given hook if it exists. By default, we fail on errors.""" |
hook = '%s/%s' % (settings.GetRoot(), hook) |
if not os.path.exists(hook): |
- return |
- return RunCommand([hook, upstream_branch], error_ok=error_ok, |
- redirect_stdout=False) |
+ return HookResults() |
+ cmd = [hook] |
+ if tbr: |
+ cmd.append('--tbr') |
+ cmd.extend(['--host-url=' + rietveld_server, upstream_branch]) |
+ output = RunCommand(cmd, error_ok=error_ok, redirect_stdout=True, |
+ tee_filter=lambda l: l.startswith('ADD:')) |
+ return HookResults(output) |
def CMDpresubmit(parser, args): |
@@ -691,20 +779,23 @@ def CMDpresubmit(parser, args): |
print 'Cannot presubmit with a dirty tree. You must commit locally first.' |
return 1 |
+ cl = Changelist() |
if args: |
base_branch = args[0] |
else: |
# Default to diffing against the "upstream" branch. |
- base_branch = Changelist().GetUpstreamBranch() |
+ base_branch = cl.GetUpstreamBranch() |
if options.upload: |
print '*** Presubmit checks for UPLOAD would report: ***' |
return not RunHook(PREUPLOAD_HOOK, upstream_branch=base_branch, |
- error_ok=True) |
+ rietveld_server=cl.GetRietveldServer(), tbr=False, |
+ error_ok=True).output |
else: |
print '*** Presubmit checks for DCOMMIT would report: ***' |
return not RunHook(PREDCOMMIT_HOOK, upstream_branch=base_branch, |
- error_ok=True) |
+ rietveld_server=cl.GetRietveldServer(), tbr=options.tbr, |
+ error_ok=True).output |
@usage('[args to "git diff"]') |
@@ -743,7 +834,14 @@ def CMDupload(parser, args): |
args = [base_branch + "..."] |
if not options.bypass_hooks: |
- RunHook(PREUPLOAD_HOOK, upstream_branch=base_branch, error_ok=False) |
+ hook_results = RunHook(PREUPLOAD_HOOK, upstream_branch=base_branch, |
+ rietveld_server=cl.GetRietveldServer(), tbr=False, |
+ error_ok=False) |
+ else: |
+ hook_results = HookResults() |
+ |
+ if not options.reviewers and hook_results.reviewers: |
+ options.reviewers = hook_results.reviewers |
# --no-ext-diff is broken in some versions of Git, so try to work around |
# this by overriding the environment (but there is still a problem if the |
@@ -756,8 +854,6 @@ def CMDupload(parser, args): |
upload_args = ['--assume_yes'] # Don't ask about untracked files. |
upload_args.extend(['--server', cl.GetRietveldServer()]) |
- if options.reviewers: |
- upload_args.extend(['--reviewers', options.reviewers]) |
if options.emulate_svn_auto_props: |
upload_args.append('--emulate_svn_auto_props') |
if options.send_mail: |
@@ -778,28 +874,19 @@ def CMDupload(parser, args): |
"Adding patch to that issue." % cl.GetIssue()) |
else: |
log_desc = CreateDescriptionFromLog(args) |
- if options.from_logs: |
- # Uses logs as description and message as subject. |
- subject = options.message |
- change_desc = subject + '\n\n' + log_desc |
- else: |
- initial_text = """# 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. |
-""" |
- if 'BUG=' not in log_desc: |
- log_desc += '\nBUG=' |
- if 'TEST=' not in log_desc: |
- log_desc += '\nTEST=' |
- change_desc = UserEditedLog(initial_text + log_desc) |
- subject = '' |
- if change_desc: |
- subject = change_desc.splitlines()[0] |
- if not change_desc: |
+ change_desc = ChangeDescription(options.message, log_desc, |
+ options.reviewers) |
+ if not options.from_logs: |
+ change_desc.Update() |
+ |
+ if change_desc.IsEmpty(): |
print "Description is empty; aborting." |
return 1 |
- upload_args.extend(['--message', subject]) |
- upload_args.extend(['--description', change_desc]) |
+ |
+ upload_args.extend(['--message', change_desc.subject]) |
+ upload_args.extend(['--description', change_desc.description]) |
+ if change_desc.reviewers: |
+ upload_args.extend(['--reviewers', change_desc.reviewers]) |
cc = ','.join(filter(None, (settings.GetCCList(), options.cc))) |
if cc: |
upload_args.extend(['--cc', cc]) |
@@ -831,7 +918,7 @@ def CMDupload(parser, args): |
print '\nGot exception while uploading -- saving description to %s\n' \ |
% backup_path |
backup_file = open(backup_path, 'w') |
- backup_file.write(change_desc) |
+ backup_file.write(change_desc.description) |
backup_file.close() |
raise |
@@ -899,8 +986,10 @@ def SendUpstream(parser, args, cmd): |
'before attempting to %s.' % (base_branch, cmd)) |
return 1 |
- if not options.force and not options.bypass_hooks: |
- RunHook(PREDCOMMIT_HOOK, upstream_branch=base_branch, error_ok=False) |
+ if not options.bypass_hooks: |
+ RunHook(PREDCOMMIT_HOOK, upstream_branch=base_branch, |
+ rietveld_server=cl.GetRietveldServer(), tbr=options.tbr, |
+ error_ok=False) |
if cmd == 'dcommit': |
# Check the tree status if the tree status URL is set. |
@@ -940,6 +1029,7 @@ def SendUpstream(parser, args, cmd): |
if cl.GetIssue(): |
description = cl.GetDescription() |
+ # TODO(dpranke): Update to use ChangeDescription object. |
if not description: |
description = """# Enter a description of the change. |
# This will be used as the change log for the commit. |
@@ -1031,8 +1121,9 @@ def SendUpstream(parser, args, cmd): |
if retcode == 0: |
hook = POSTUPSTREAM_HOOK_PATTERN % cmd |
if os.path.isfile(hook): |
- RunHook(hook, upstream_branch=base_branch, error_ok=True) |
- |
+ RunHook(hook, upstream_branch=base_branch, |
+ rietveld_server=cl.GetRietveldServer(), tbr=options.tbr, |
+ error_ok=True) |
return 0 |