Revert r79002 - bug processing reviewer lists

TBR=maruel@chromium.org

git-svn-id: svn://svn.chromium.org/chrome/trunk/tools/depot_tools@79005 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/git_cl/git_cl.py b/git_cl/git_cl.py
index 552c094..218c9f3 100644
--- a/git_cl/git_cl.py
+++ b/git_cl/git_cl.py
@@ -9,6 +9,7 @@
 import re
 import subprocess
 import sys
+import tempfile
 import textwrap
 import urlparse
 import urllib2
@@ -20,17 +21,17 @@
 
 # TODO(dpranke): don't use relative import.
 import upload  # pylint: disable=W0403
-
-# TODO(dpranke): move this file up a directory so we don't need this.
-depot_tools_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
-sys.path.append(depot_tools_path)
-
-import breakpad  # pylint: disable=W0611
-
-import presubmit_support
-import scm
-import watchlists
-
+try:
+  # TODO(dpranke): We wrap this in a try block for a limited form of
+  # backwards-compatibility with older versions of git-cl that weren't
+  # dependent on depot_tools. This version should still work outside of
+  # depot_tools as long as --bypass-hooks is used. We should remove this
+  # once this has baked for a while and things seem safe.
+  depot_tools_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+  sys.path.append(depot_tools_path)
+  import breakpad  # pylint: disable=W0611
+except ImportError:
+  pass
 
 DEFAULT_SERVER = 'http://codereview.appspot.com'
 POSTUPSTREAM_HOOK_PATTERN = '.git/hooks/post-cl-%s'
@@ -332,7 +333,6 @@
     self.description = None
     self.has_patchset = False
     self.patchset = None
-    self.tbr = False
 
   def GetBranch(self):
     """Returns the short branch name, e.g. 'master'."""
@@ -535,6 +535,53 @@
   # svn-based hackery.
 
 
+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.
 
@@ -684,6 +731,36 @@
   return RunGit(['log', '--pretty=format:%s\n\n%b'] + log_args)
 
 
+def UserEditedLog(starting_text):
+  """Given some starting text, let the user edit it and return the result."""
+  editor = os.getenv('EDITOR', 'vi')
+
+  (file_handle, filename) = tempfile.mkstemp()
+  fileobj = os.fdopen(file_handle, 'w')
+  fileobj.write(starting_text)
+  fileobj.close()
+
+  # Open up the default editor in the system to get the CL description.
+  try:
+    cmd = '%s %s' % (editor, filename)
+    if sys.platform == 'win32' and os.environ.get('TERM') == 'msys':
+      # Msysgit requires the usage of 'env' to be present.
+      cmd = 'env ' + cmd
+    # shell=True to allow the shell to handle all forms of quotes in $EDITOR.
+    subprocess.check_call(cmd, shell=True)
+    fileobj = open(filename)
+    text = fileobj.read()
+    fileobj.close()
+  finally:
+    os.remove(filename)
+
+  if not text:
+    return
+
+  stripcomment_re = re.compile(r'^#.*$', re.MULTILINE)
+  return stripcomment_re.sub('', text).strip()
+
+
 def ConvertToInteger(inputval):
   """Convert a string to integer, but returns either an int or None."""
   try:
@@ -692,20 +769,12 @@
     return None
 
 
-class GitChangeDescription(presubmit_support.ChangeDescription):
-  def UserEdit(self):
-    header = (
-        "# 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"
-        "\n")
-    edited_text = self.editor(header + self.EditableDescription())
-    stripcomment_re = re.compile(r'^#.*$', re.MULTILINE)
-    self.Parse(stripcomment_re.sub('', edited_text).strip())
-
-
 def RunHook(committing, upstream_branch, rietveld_server, tbr, may_prompt):
   """Calls sys.exit() if the hook fails; returns a HookResults otherwise."""
+  import presubmit_support
+  import scm
+  import watchlists
+
   root = RunCommand(['git', 'rev-parse', '--show-cdup']).strip()
   if not root:
     root = '.'
@@ -774,13 +843,13 @@
   if options.upload:
     print '*** Presubmit checks for UPLOAD would report: ***'
     RunHook(committing=False, upstream_branch=base_branch,
-            rietveld_server=cl.GetRietveldServer(), tbr=cl.tbr,
+            rietveld_server=cl.GetRietveldServer(), tbr=False,
             may_prompt=False)
     return 0
   else:
     print '*** Presubmit checks for DCOMMIT would report: ***'
     RunHook(committing=True, upstream_branch=base_branch,
-            rietveld_server=cl.GetRietveldServer, tbr=cl.tbr,
+            rietveld_server=cl.GetRietveldServer, tbr=False,
             may_prompt=False)
     return 0
 
@@ -824,10 +893,10 @@
 
   if not options.bypass_hooks and not options.force:
     hook_results = RunHook(committing=False, upstream_branch=base_branch,
-                           rietveld_server=cl.GetRietveldServer(), tbr=cl.tbr,
+                           rietveld_server=cl.GetRietveldServer(), tbr=False,
                            may_prompt=True)
     if not options.reviewers and hook_results.reviewers:
-      options.reviewers = ','.join(hook_results.reviewers)
+      options.reviewers = hook_results.reviewers
 
 
   # --no-ext-diff is broken in some versions of Git, so try to work around
@@ -861,10 +930,10 @@
            "Adding patch to that issue." % cl.GetIssue())
   else:
     log_desc = CreateDescriptionFromLog(args)
-    change_desc = GitChangeDescription(subject=options.message,
-        description=log_desc, reviewers=options.reviewers, tbr=cl.tbr)
-    if not options.from_logs and (not options.force):
-      change_desc.UserEdit()
+    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."
@@ -975,7 +1044,7 @@
 
   if not options.bypass_hooks and not options.force:
     RunHook(committing=True, upstream_branch=base_branch,
-            rietveld_server=cl.GetRietveldServer(), tbr=(cl.tbr or options.tbr),
+            rietveld_server=cl.GetRietveldServer(), tbr=options.tbr,
             may_prompt=True)
 
     if cmd == 'dcommit':
@@ -1014,15 +1083,17 @@
       # create a template description. Eitherway, give the user a chance to edit
       # it to fill in the TBR= field.
       if cl.GetIssue():
-        change_desc = GitChangeDescription(description=cl.GetDescription())
+        description = cl.GetDescription()
 
+      # TODO(dpranke): Update to use ChangeDescription object.
       if not description:
-        log_desc = CreateDescriptionFromLog(args)
-        change_desc = GitChangeDescription(description=log_desc, tbr=True)
+        description = """# Enter a description of the change.
+# This will be used as the change log for the commit.
 
-      if not options.force:
-        change_desc.UserEdit()
-      description = change_desc.description
+"""
+        description += CreateDescriptionFromLog(args)
+
+      description = UserEditedLog(description + '\nTBR=')
 
     if not description:
       print "Description empty; aborting."