Update upload.py in depot_tools to infra.git@3d174a0959ba68a4cd83a5abce9f497e30bfce68

Includes fix for bug 388937.

R=agable@chromium.org, jrobbins@chromium.org, maruel@chromium.org
BUG=388937

Review URL: https://codereview.chromium.org/785433003

git-svn-id: svn://svn.chromium.org/chrome/trunk/tools/depot_tools@293259 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/third_party/upload.py b/third_party/upload.py
index a8039a5..6b71432 100755
--- a/third_party/upload.py
+++ b/third_party/upload.py
@@ -102,22 +102,18 @@
 VCS_UNKNOWN = "Unknown"
 
 VCS = [
-{
-    'name': VCS_MERCURIAL,
-    'aliases': ['hg', 'mercurial'],
-}, {
-    'name': VCS_SUBVERSION,
-    'aliases': ['svn', 'subversion'],
-}, {
-    'name': VCS_PERFORCE,
-    'aliases': ['p4', 'perforce'],
-}, {
-    'name': VCS_GIT,
-    'aliases': ['git'],
-}, {
-    'name': VCS_CVS,
-    'aliases': ['cvs'],
-}]
+  {'name': VCS_MERCURIAL,
+   'aliases': ['hg', 'mercurial']},
+  {'name': VCS_SUBVERSION,
+   'aliases': ['svn', 'subversion'],},
+  {'name': VCS_PERFORCE,
+   'aliases': ['p4', 'perforce']},
+  {'name': VCS_GIT,
+   'aliases': ['git']},
+  {'name': VCS_CVS,
+   'aliases': ['cvs']},
+  ]
+
 
 VCS_SHORT_NAMES = []    # hg, svn, ...
 VCS_ABBREVIATIONS = {}  # alias: name, ...
@@ -217,7 +213,7 @@
 
 def ErrorExit(msg):
   """Print an error message to stderr and exit."""
-  print >>sys.stderr, msg
+  print >> sys.stderr, msg
   sys.exit(1)
 
 
@@ -380,41 +376,41 @@
       try:
         auth_token = self._GetAuthToken(credentials[0], credentials[1])
       except ClientLoginError, e:
-        print >>sys.stderr, ''
+        print >> sys.stderr, ''
         if e.reason == "BadAuthentication":
           if e.info == "InvalidSecondFactor":
-            print >>sys.stderr, (
+            print >> sys.stderr, (
                 "Use an application-specific password instead "
                 "of your regular account password.\n"
                 "See http://www.google.com/"
                 "support/accounts/bin/answer.py?answer=185833")
           else:
-            print >>sys.stderr, "Invalid username or password."
+            print >> sys.stderr, "Invalid username or password."
         elif e.reason == "CaptchaRequired":
-          print >>sys.stderr, (
+          print >> sys.stderr, (
               "Please go to\n"
               "https://www.google.com/accounts/DisplayUnlockCaptcha\n"
               "and verify you are a human.  Then try again.\n"
               "If you are using a Google Apps account the URL is:\n"
               "https://www.google.com/a/yourdomain.com/UnlockCaptcha")
         elif e.reason == "NotVerified":
-          print >>sys.stderr, "Account not verified."
+          print >> sys.stderr, "Account not verified."
         elif e.reason == "TermsNotAgreed":
-          print >>sys.stderr, "User has not agreed to TOS."
+          print >> sys.stderr, "User has not agreed to TOS."
         elif e.reason == "AccountDeleted":
-          print >>sys.stderr, "The user account has been deleted."
+          print >> sys.stderr, "The user account has been deleted."
         elif e.reason == "AccountDisabled":
-          print >>sys.stderr, "The user account has been disabled."
+          print >> sys.stderr, "The user account has been disabled."
           break
         elif e.reason == "ServiceDisabled":
-          print >>sys.stderr, ("The user's access to the service has been "
+          print >> sys.stderr, ("The user's access to the service has been "
                                "disabled.")
         elif e.reason == "ServiceUnavailable":
-          print >>sys.stderr, "The service is not available; try again later."
+          print >> sys.stderr, "The service is not available; try again later."
         else:
           # Unknown error.
           raise
-        print >>sys.stderr, ''
+        print >> sys.stderr, ''
         continue
       self._GetAuthCookie(auth_token)
       return
@@ -461,7 +457,7 @@
           for header, value in extra_headers.items():
             req.add_header(header, value)
         try:
-          f = self.opener.open(req)
+          f = self.opener.open(req, timeout=70)
           response = f.read()
           f.close()
           return response
@@ -542,37 +538,37 @@
 
 
 class CondensedHelpFormatter(optparse.IndentedHelpFormatter):
-   """Frees more horizontal space by removing indentation from group
-      options and collapsing arguments between short and long, e.g.
-      '-o ARG, --opt=ARG' to -o --opt ARG"""
+  """Frees more horizontal space by removing indentation from group
+     options and collapsing arguments between short and long, e.g.
+     '-o ARG, --opt=ARG' to -o --opt ARG"""
 
-   def format_heading(self, heading):
-     return "%s:\n" % heading
+  def format_heading(self, heading):
+    return "%s:\n" % heading
 
-   def format_option(self, option):
-     self.dedent()
-     res = optparse.HelpFormatter.format_option(self, option)
-     self.indent()
-     return res
+  def format_option(self, option):
+    self.dedent()
+    res = optparse.HelpFormatter.format_option(self, option)
+    self.indent()
+    return res
 
-   def format_option_strings(self, option):
-     self.set_long_opt_delimiter(" ")
-     optstr = optparse.HelpFormatter.format_option_strings(self, option)
-     optlist = optstr.split(", ")
-     if len(optlist) > 1:
-       if option.takes_value():
-         # strip METAVAR from all but the last option
-         optlist = [x.split()[0] for x in optlist[:-1]] + optlist[-1:]
-       optstr = " ".join(optlist)
-     return optstr
+  def format_option_strings(self, option):
+    self.set_long_opt_delimiter(" ")
+    optstr = optparse.HelpFormatter.format_option_strings(self, option)
+    optlist = optstr.split(", ")
+    if len(optlist) > 1:
+      if option.takes_value():
+        # strip METAVAR from all but the last option
+        optlist = [x.split()[0] for x in optlist[:-1]] + optlist[-1:]
+      optstr = " ".join(optlist)
+    return optstr
 
 
 parser = optparse.OptionParser(
-    usage=("%prog [options] [-- diff_options] [path...]\n"
-           "See also: http://code.google.com/p/rietveld/wiki/UploadPyUsage"),
-    add_help_option=False,
-    formatter=CondensedHelpFormatter()
-)
+  usage=("%prog [options] [-- diff_options] [path...]\n"
+         "See also: http://code.google.com/p/rietveld/wiki/UploadPyUsage"),
+  add_help_option=False,
+  formatter=CondensedHelpFormatter()
+  )
 parser.add_option("-h", "--help", action="store_true",
                   help="Show this help message and exit.")
 parser.add_option("-y", "--assume_yes", action="store_true",
@@ -680,8 +676,11 @@
 group = parser.add_option_group("Git-specific options")
 group.add_option("--git_similarity", action="store", dest="git_similarity",
                  metavar="SIM", type="int", default=50,
-                 help=("Set the minimum similarity index for detecting renames "
-                       "and copies. See `git diff -C`. (default 50)."))
+                 help=("Set the minimum similarity percentage for detecting "
+                       "renames and copies. See `git diff -C`. (default 50)."))
+group.add_option("--git_only_search_patch", action="store_false", default=True,
+                 dest='git_find_copies_harder',
+                 help="Removes --find-copies-harder when seaching for copies")
 group.add_option("--git_no_find_copies", action="store_false", default=True,
                  dest="git_find_copies",
                  help=("Prevents git from looking for copies (default off)."))
@@ -1009,7 +1008,7 @@
 def RunShellWithReturnCodeAndStderr(command, print_output=False,
                            universal_newlines=True,
                            env=os.environ):
-  """Executes a command and returns the output from stdout, stderr and the return code.
+  """Run a command and return output from stdout, stderr and the return code.
 
   Args:
     command: Command to execute.
@@ -1040,7 +1039,7 @@
   p.wait()
   errout = p.stderr.read()
   if print_output and errout:
-    print >>sys.stderr, errout
+    print >> sys.stderr, errout
   p.stdout.close()
   p.stderr.close()
   return output, errout, p.returncode
@@ -1048,7 +1047,7 @@
 def RunShellWithReturnCode(command, print_output=False,
                            universal_newlines=True,
                            env=os.environ):
-  """Executes a command and returns the output from stdout and the return code."""
+  """Run a command and return output from stdout and the return code."""
   out, err, retcode = RunShellWithReturnCodeAndStderr(command, print_output,
                            universal_newlines, env)
   return out, retcode
@@ -1223,7 +1222,8 @@
     mimetype =  mimetypes.guess_type(filename)[0]
     if not mimetype:
       return False
-    return mimetype.startswith("image/") and not mimetype.startswith("image/svg")
+    return (mimetype.startswith("image/") and
+            not mimetype.startswith("image/svg"))
 
   def IsBinaryData(self, data):
     """Returns true if data contains a null byte."""
@@ -1269,21 +1269,21 @@
     """
     url = self._GetInfo("URL")
     if url:
-        scheme, netloc, path, params, query, fragment = urlparse.urlparse(url)
-        guess = ""
-        # TODO(anatoli) - repository specific hacks should be handled by server
-        if netloc == "svn.python.org" and scheme == "svn+ssh":
-          path = "projects" + path
-          scheme = "http"
-          guess = "Python "
-        elif netloc.endswith(".googlecode.com"):
-          scheme = "http"
-          guess = "Google Code "
-        path = path + "/"
-        base = urlparse.urlunparse((scheme, netloc, path, params,
-                                    query, fragment))
-        LOGGER.info("Guessed %sbase = %s", guess, base)
-        return base
+      scheme, netloc, path, params, query, fragment = urlparse.urlparse(url)
+      guess = ""
+      # TODO(anatoli) - repository specific hacks should be handled by server
+      if netloc == "svn.python.org" and scheme == "svn+ssh":
+        path = "projects" + path
+        scheme = "http"
+        guess = "Python "
+      elif netloc.endswith(".googlecode.com"):
+        scheme = "http"
+        guess = "Google Code "
+      path = path + "/"
+      base = urlparse.urlunparse((scheme, netloc, path, params,
+                                  query, fragment))
+      LOGGER.info("Guessed %sbase = %s", guess, base)
+      return base
     if required:
       ErrorExit("Can't find URL in output from svn info")
     return None
@@ -1338,9 +1338,10 @@
     }
 
     def repl(m):
-       if m.group(2):
-         return "$%s::%s$" % (m.group(1), " " * len(m.group(3)))
-       return "$%s$" % m.group(1)
+      if m.group(2):
+        return "$%s::%s$" % (m.group(1), " " * len(m.group(3)))
+      return "$%s$" % m.group(1)
+
     keywords = [keyword
                 for name in keyword_str.split(" ")
                 for keyword in svn_keywords.get(name, [])]
@@ -1393,7 +1394,8 @@
         if returncode:
           # Directory might not yet exist at start revison
           # svn: Unable to find repository location for 'abc' in revision nnn
-          if re.match('^svn: Unable to find repository location for .+ in revision \d+', err):
+          if re.match('^svn: Unable to find repository location '
+                      'for .+ in revision \d+', err):
             old_files = ()
           else:
             ErrorExit("Failed to get status for %s:\n%s" % (filename, err))
@@ -1615,16 +1617,18 @@
     # append a diff (with rename detection), without deletes.
     cmd = [
         "git", "diff", "--no-color", "--no-ext-diff", "--full-index",
-        "--ignore-submodules",
+        "--ignore-submodules", "--src-prefix=a/", "--dst-prefix=b/",
     ]
     diff = RunShell(
         cmd + ["--no-renames", "--diff-filter=D"] + extra_args,
         env=env, silent_ok=True)
+    assert 0 <= self.options.git_similarity <= 100
     if self.options.git_find_copies:
-      similarity_options = ["--find-copies-harder", "-l100000",
-                            "-C%s" % self.options.git_similarity ]
+      similarity_options = ["-l100000", "-C%d%%" % self.options.git_similarity]
+      if self.options.git_find_copies_harder:
+        similarity_options.append("--find-copies-harder")
     else:
-      similarity_options = ["-M%s" % self.options.git_similarity ]
+      similarity_options = ["-M%d%%" % self.options.git_similarity ]
     diff += RunShell(
         cmd + ["--diff-filter=AMCRT"] + similarity_options + extra_args,
         env=env, silent_ok=True)
@@ -2085,7 +2089,7 @@
       line_count = len(diffData.file_body.splitlines())
       diffData.change_summary = "@@ -0,0 +1"
       if line_count > 1:
-          diffData.change_summary += ",%d" % line_count
+        diffData.change_summary += ",%d" % line_count
       diffData.change_summary += " @@"
       diffData.prefix = "+"
       return diffData