| OLD | NEW | 
|---|
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python | 
| 2 # coding: utf-8 | 2 # coding: utf-8 | 
| 3 # | 3 # | 
| 4 # Copyright 2007 Google Inc. | 4 # Copyright 2007 Google Inc. | 
| 5 # | 5 # | 
| 6 # Licensed under the Apache License, Version 2.0 (the "License"); | 6 # Licensed under the Apache License, Version 2.0 (the "License"); | 
| 7 # you may not use this file except in compliance with the License. | 7 # you may not use this file except in compliance with the License. | 
| 8 # You may obtain a copy of the License at | 8 # You may obtain a copy of the License at | 
| 9 # | 9 # | 
| 10 #     http://www.apache.org/licenses/LICENSE-2.0 | 10 #     http://www.apache.org/licenses/LICENSE-2.0 | 
| (...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 96 | 96 | 
| 97 # Constants for version control names.  Used by GuessVCSName. | 97 # Constants for version control names.  Used by GuessVCSName. | 
| 98 VCS_GIT = "Git" | 98 VCS_GIT = "Git" | 
| 99 VCS_MERCURIAL = "Mercurial" | 99 VCS_MERCURIAL = "Mercurial" | 
| 100 VCS_SUBVERSION = "Subversion" | 100 VCS_SUBVERSION = "Subversion" | 
| 101 VCS_PERFORCE = "Perforce" | 101 VCS_PERFORCE = "Perforce" | 
| 102 VCS_CVS = "CVS" | 102 VCS_CVS = "CVS" | 
| 103 VCS_UNKNOWN = "Unknown" | 103 VCS_UNKNOWN = "Unknown" | 
| 104 | 104 | 
| 105 VCS = [ | 105 VCS = [ | 
| 106 { | 106   {'name': VCS_MERCURIAL, | 
| 107     'name': VCS_MERCURIAL, | 107    'aliases': ['hg', 'mercurial']}, | 
| 108     'aliases': ['hg', 'mercurial'], | 108   {'name': VCS_SUBVERSION, | 
| 109 }, { | 109    'aliases': ['svn', 'subversion'],}, | 
| 110     'name': VCS_SUBVERSION, | 110   {'name': VCS_PERFORCE, | 
| 111     'aliases': ['svn', 'subversion'], | 111    'aliases': ['p4', 'perforce']}, | 
| 112 }, { | 112   {'name': VCS_GIT, | 
| 113     'name': VCS_PERFORCE, | 113    'aliases': ['git']}, | 
| 114     'aliases': ['p4', 'perforce'], | 114   {'name': VCS_CVS, | 
| 115 }, { | 115    'aliases': ['cvs']}, | 
| 116     'name': VCS_GIT, | 116   ] | 
| 117     'aliases': ['git'], | 117 | 
| 118 }, { |  | 
| 119     'name': VCS_CVS, |  | 
| 120     'aliases': ['cvs'], |  | 
| 121 }] |  | 
| 122 | 118 | 
| 123 VCS_SHORT_NAMES = []    # hg, svn, ... | 119 VCS_SHORT_NAMES = []    # hg, svn, ... | 
| 124 VCS_ABBREVIATIONS = {}  # alias: name, ... | 120 VCS_ABBREVIATIONS = {}  # alias: name, ... | 
| 125 for vcs in VCS: | 121 for vcs in VCS: | 
| 126   VCS_SHORT_NAMES.append(min(vcs['aliases'], key=len)) | 122   VCS_SHORT_NAMES.append(min(vcs['aliases'], key=len)) | 
| 127   VCS_ABBREVIATIONS.update((alias, vcs['name']) for alias in vcs['aliases']) | 123   VCS_ABBREVIATIONS.update((alias, vcs['name']) for alias in vcs['aliases']) | 
| 128 | 124 | 
| 129 | 125 | 
| 130 # OAuth 2.0-Related Constants | 126 # OAuth 2.0-Related Constants | 
| 131 LOCALHOST_IP = '127.0.0.1' | 127 LOCALHOST_IP = '127.0.0.1' | 
| (...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 211 | 207 | 
| 212   Args: | 208   Args: | 
| 213     msg: The string to print. | 209     msg: The string to print. | 
| 214   """ | 210   """ | 
| 215   if verbosity > 0: | 211   if verbosity > 0: | 
| 216     print msg | 212     print msg | 
| 217 | 213 | 
| 218 | 214 | 
| 219 def ErrorExit(msg): | 215 def ErrorExit(msg): | 
| 220   """Print an error message to stderr and exit.""" | 216   """Print an error message to stderr and exit.""" | 
| 221   print >>sys.stderr, msg | 217   print >> sys.stderr, msg | 
| 222   sys.exit(1) | 218   sys.exit(1) | 
| 223 | 219 | 
| 224 | 220 | 
| 225 class ClientLoginError(urllib2.HTTPError): | 221 class ClientLoginError(urllib2.HTTPError): | 
| 226   """Raised to indicate there was an error authenticating with ClientLogin.""" | 222   """Raised to indicate there was an error authenticating with ClientLogin.""" | 
| 227 | 223 | 
| 228   def __init__(self, url, code, msg, headers, args): | 224   def __init__(self, url, code, msg, headers, args): | 
| 229     urllib2.HTTPError.__init__(self, url, code, msg, headers, None) | 225     urllib2.HTTPError.__init__(self, url, code, msg, headers, None) | 
| 230     self.args = args | 226     self.args = args | 
| 231     self._reason = args["Error"] | 227     self._reason = args["Error"] | 
| (...skipping 142 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 374 | 370 | 
| 375     If we attempt to access the upload API without first obtaining an | 371     If we attempt to access the upload API without first obtaining an | 
| 376     authentication cookie, it returns a 401 response (or a 302) and | 372     authentication cookie, it returns a 401 response (or a 302) and | 
| 377     directs us to authenticate ourselves with ClientLogin. | 373     directs us to authenticate ourselves with ClientLogin. | 
| 378     """ | 374     """ | 
| 379     for i in range(3): | 375     for i in range(3): | 
| 380       credentials = self.auth_function() | 376       credentials = self.auth_function() | 
| 381       try: | 377       try: | 
| 382         auth_token = self._GetAuthToken(credentials[0], credentials[1]) | 378         auth_token = self._GetAuthToken(credentials[0], credentials[1]) | 
| 383       except ClientLoginError, e: | 379       except ClientLoginError, e: | 
| 384         print >>sys.stderr, '' | 380         print >> sys.stderr, '' | 
| 385         if e.reason == "BadAuthentication": | 381         if e.reason == "BadAuthentication": | 
| 386           if e.info == "InvalidSecondFactor": | 382           if e.info == "InvalidSecondFactor": | 
| 387             print >>sys.stderr, ( | 383             print >> sys.stderr, ( | 
| 388                 "Use an application-specific password instead " | 384                 "Use an application-specific password instead " | 
| 389                 "of your regular account password.\n" | 385                 "of your regular account password.\n" | 
| 390                 "See http://www.google.com/" | 386                 "See http://www.google.com/" | 
| 391                 "support/accounts/bin/answer.py?answer=185833") | 387                 "support/accounts/bin/answer.py?answer=185833") | 
| 392           else: | 388           else: | 
| 393             print >>sys.stderr, "Invalid username or password." | 389             print >> sys.stderr, "Invalid username or password." | 
| 394         elif e.reason == "CaptchaRequired": | 390         elif e.reason == "CaptchaRequired": | 
| 395           print >>sys.stderr, ( | 391           print >> sys.stderr, ( | 
| 396               "Please go to\n" | 392               "Please go to\n" | 
| 397               "https://www.google.com/accounts/DisplayUnlockCaptcha\n" | 393               "https://www.google.com/accounts/DisplayUnlockCaptcha\n" | 
| 398               "and verify you are a human.  Then try again.\n" | 394               "and verify you are a human.  Then try again.\n" | 
| 399               "If you are using a Google Apps account the URL is:\n" | 395               "If you are using a Google Apps account the URL is:\n" | 
| 400               "https://www.google.com/a/yourdomain.com/UnlockCaptcha") | 396               "https://www.google.com/a/yourdomain.com/UnlockCaptcha") | 
| 401         elif e.reason == "NotVerified": | 397         elif e.reason == "NotVerified": | 
| 402           print >>sys.stderr, "Account not verified." | 398           print >> sys.stderr, "Account not verified." | 
| 403         elif e.reason == "TermsNotAgreed": | 399         elif e.reason == "TermsNotAgreed": | 
| 404           print >>sys.stderr, "User has not agreed to TOS." | 400           print >> sys.stderr, "User has not agreed to TOS." | 
| 405         elif e.reason == "AccountDeleted": | 401         elif e.reason == "AccountDeleted": | 
| 406           print >>sys.stderr, "The user account has been deleted." | 402           print >> sys.stderr, "The user account has been deleted." | 
| 407         elif e.reason == "AccountDisabled": | 403         elif e.reason == "AccountDisabled": | 
| 408           print >>sys.stderr, "The user account has been disabled." | 404           print >> sys.stderr, "The user account has been disabled." | 
| 409           break | 405           break | 
| 410         elif e.reason == "ServiceDisabled": | 406         elif e.reason == "ServiceDisabled": | 
| 411           print >>sys.stderr, ("The user's access to the service has been " | 407           print >> sys.stderr, ("The user's access to the service has been " | 
| 412                                "disabled.") | 408                                "disabled.") | 
| 413         elif e.reason == "ServiceUnavailable": | 409         elif e.reason == "ServiceUnavailable": | 
| 414           print >>sys.stderr, "The service is not available; try again later." | 410           print >> sys.stderr, "The service is not available; try again later." | 
| 415         else: | 411         else: | 
| 416           # Unknown error. | 412           # Unknown error. | 
| 417           raise | 413           raise | 
| 418         print >>sys.stderr, '' | 414         print >> sys.stderr, '' | 
| 419         continue | 415         continue | 
| 420       self._GetAuthCookie(auth_token) | 416       self._GetAuthCookie(auth_token) | 
| 421       return | 417       return | 
| 422 | 418 | 
| 423   def Send(self, request_path, payload=None, | 419   def Send(self, request_path, payload=None, | 
| 424            content_type="application/octet-stream", | 420            content_type="application/octet-stream", | 
| 425            timeout=None, | 421            timeout=None, | 
| 426            extra_headers=None, | 422            extra_headers=None, | 
| 427            **kwargs): | 423            **kwargs): | 
| 428     """Sends an RPC and returns the response. | 424     """Sends an RPC and returns the response. | 
| (...skipping 26 matching lines...) Expand all  Loading... | 
| 455         args = dict(kwargs) | 451         args = dict(kwargs) | 
| 456         url = "%s%s" % (self.host, request_path) | 452         url = "%s%s" % (self.host, request_path) | 
| 457         if args: | 453         if args: | 
| 458           url += "?" + urllib.urlencode(args) | 454           url += "?" + urllib.urlencode(args) | 
| 459         req = self._CreateRequest(url=url, data=payload) | 455         req = self._CreateRequest(url=url, data=payload) | 
| 460         req.add_header("Content-Type", content_type) | 456         req.add_header("Content-Type", content_type) | 
| 461         if extra_headers: | 457         if extra_headers: | 
| 462           for header, value in extra_headers.items(): | 458           for header, value in extra_headers.items(): | 
| 463             req.add_header(header, value) | 459             req.add_header(header, value) | 
| 464         try: | 460         try: | 
| 465           f = self.opener.open(req) | 461           f = self.opener.open(req, timeout=70) | 
| 466           response = f.read() | 462           response = f.read() | 
| 467           f.close() | 463           f.close() | 
| 468           return response | 464           return response | 
| 469         except urllib2.HTTPError, e: | 465         except urllib2.HTTPError, e: | 
| 470           if tries > 3: | 466           if tries > 3: | 
| 471             raise | 467             raise | 
| 472           elif e.code == 401 or e.code == 302: | 468           elif e.code == 401 or e.code == 302: | 
| 473             if not self.auth_function: | 469             if not self.auth_function: | 
| 474               raise | 470               raise | 
| 475             self._Authenticate() | 471             self._Authenticate() | 
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 536       # Always chmod the cookie file | 532       # Always chmod the cookie file | 
| 537       os.chmod(self.cookie_file, 0600) | 533       os.chmod(self.cookie_file, 0600) | 
| 538     else: | 534     else: | 
| 539       # Don't save cookies across runs of update.py. | 535       # Don't save cookies across runs of update.py. | 
| 540       self.cookie_jar = cookielib.CookieJar() | 536       self.cookie_jar = cookielib.CookieJar() | 
| 541     opener.add_handler(urllib2.HTTPCookieProcessor(self.cookie_jar)) | 537     opener.add_handler(urllib2.HTTPCookieProcessor(self.cookie_jar)) | 
| 542     return opener | 538     return opener | 
| 543 | 539 | 
| 544 | 540 | 
| 545 class CondensedHelpFormatter(optparse.IndentedHelpFormatter): | 541 class CondensedHelpFormatter(optparse.IndentedHelpFormatter): | 
| 546    """Frees more horizontal space by removing indentation from group | 542   """Frees more horizontal space by removing indentation from group | 
| 547       options and collapsing arguments between short and long, e.g. | 543      options and collapsing arguments between short and long, e.g. | 
| 548       '-o ARG, --opt=ARG' to -o --opt ARG""" | 544      '-o ARG, --opt=ARG' to -o --opt ARG""" | 
| 549 | 545 | 
| 550    def format_heading(self, heading): | 546   def format_heading(self, heading): | 
| 551      return "%s:\n" % heading | 547     return "%s:\n" % heading | 
| 552 | 548 | 
| 553    def format_option(self, option): | 549   def format_option(self, option): | 
| 554      self.dedent() | 550     self.dedent() | 
| 555      res = optparse.HelpFormatter.format_option(self, option) | 551     res = optparse.HelpFormatter.format_option(self, option) | 
| 556      self.indent() | 552     self.indent() | 
| 557      return res | 553     return res | 
| 558 | 554 | 
| 559    def format_option_strings(self, option): | 555   def format_option_strings(self, option): | 
| 560      self.set_long_opt_delimiter(" ") | 556     self.set_long_opt_delimiter(" ") | 
| 561      optstr = optparse.HelpFormatter.format_option_strings(self, option) | 557     optstr = optparse.HelpFormatter.format_option_strings(self, option) | 
| 562      optlist = optstr.split(", ") | 558     optlist = optstr.split(", ") | 
| 563      if len(optlist) > 1: | 559     if len(optlist) > 1: | 
| 564        if option.takes_value(): | 560       if option.takes_value(): | 
| 565          # strip METAVAR from all but the last option | 561         # strip METAVAR from all but the last option | 
| 566          optlist = [x.split()[0] for x in optlist[:-1]] + optlist[-1:] | 562         optlist = [x.split()[0] for x in optlist[:-1]] + optlist[-1:] | 
| 567        optstr = " ".join(optlist) | 563       optstr = " ".join(optlist) | 
| 568      return optstr | 564     return optstr | 
| 569 | 565 | 
| 570 | 566 | 
| 571 parser = optparse.OptionParser( | 567 parser = optparse.OptionParser( | 
| 572     usage=("%prog [options] [-- diff_options] [path...]\n" | 568   usage=("%prog [options] [-- diff_options] [path...]\n" | 
| 573            "See also: http://code.google.com/p/rietveld/wiki/UploadPyUsage"), | 569          "See also: http://code.google.com/p/rietveld/wiki/UploadPyUsage"), | 
| 574     add_help_option=False, | 570   add_help_option=False, | 
| 575     formatter=CondensedHelpFormatter() | 571   formatter=CondensedHelpFormatter() | 
| 576 ) | 572   ) | 
| 577 parser.add_option("-h", "--help", action="store_true", | 573 parser.add_option("-h", "--help", action="store_true", | 
| 578                   help="Show this help message and exit.") | 574                   help="Show this help message and exit.") | 
| 579 parser.add_option("-y", "--assume_yes", action="store_true", | 575 parser.add_option("-y", "--assume_yes", action="store_true", | 
| 580                   dest="assume_yes", default=False, | 576                   dest="assume_yes", default=False, | 
| 581                   help="Assume that the answer to yes/no questions is 'yes'.") | 577                   help="Assume that the answer to yes/no questions is 'yes'.") | 
| 582 # Logging | 578 # Logging | 
| 583 group = parser.add_option_group("Logging options") | 579 group = parser.add_option_group("Logging options") | 
| 584 group.add_option("-q", "--quiet", action="store_const", const=0, | 580 group.add_option("-q", "--quiet", action="store_const", const=0, | 
| 585                  dest="verbose", help="Print errors only.") | 581                  dest="verbose", help="Print errors only.") | 
| 586 group.add_option("-v", "--verbose", action="store_const", const=2, | 582 group.add_option("-v", "--verbose", action="store_const", const=2, | 
| (...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 674                  metavar="VCS", default=None, | 670                  metavar="VCS", default=None, | 
| 675                  help=("Explicitly specify version control system (%s)" | 671                  help=("Explicitly specify version control system (%s)" | 
| 676                        % ", ".join(VCS_SHORT_NAMES))) | 672                        % ", ".join(VCS_SHORT_NAMES))) | 
| 677 group.add_option("--emulate_svn_auto_props", action="store_true", | 673 group.add_option("--emulate_svn_auto_props", action="store_true", | 
| 678                  dest="emulate_svn_auto_props", default=False, | 674                  dest="emulate_svn_auto_props", default=False, | 
| 679                  help=("Emulate Subversion's auto properties feature.")) | 675                  help=("Emulate Subversion's auto properties feature.")) | 
| 680 # Git-specific | 676 # Git-specific | 
| 681 group = parser.add_option_group("Git-specific options") | 677 group = parser.add_option_group("Git-specific options") | 
| 682 group.add_option("--git_similarity", action="store", dest="git_similarity", | 678 group.add_option("--git_similarity", action="store", dest="git_similarity", | 
| 683                  metavar="SIM", type="int", default=50, | 679                  metavar="SIM", type="int", default=50, | 
| 684                  help=("Set the minimum similarity index for detecting renames " | 680                  help=("Set the minimum similarity percentage for detecting " | 
| 685                        "and copies. See `git diff -C`. (default 50).")) | 681                        "renames and copies. See `git diff -C`. (default 50).")) | 
|  | 682 group.add_option("--git_only_search_patch", action="store_false", default=True, | 
|  | 683                  dest='git_find_copies_harder', | 
|  | 684                  help="Removes --find-copies-harder when seaching for copies") | 
| 686 group.add_option("--git_no_find_copies", action="store_false", default=True, | 685 group.add_option("--git_no_find_copies", action="store_false", default=True, | 
| 687                  dest="git_find_copies", | 686                  dest="git_find_copies", | 
| 688                  help=("Prevents git from looking for copies (default off).")) | 687                  help=("Prevents git from looking for copies (default off).")) | 
| 689 # Perforce-specific | 688 # Perforce-specific | 
| 690 group = parser.add_option_group("Perforce-specific options " | 689 group = parser.add_option_group("Perforce-specific options " | 
| 691                                 "(overrides P4 environment variables)") | 690                                 "(overrides P4 environment variables)") | 
| 692 group.add_option("--p4_port", action="store", dest="p4_port", | 691 group.add_option("--p4_port", action="store", dest="p4_port", | 
| 693                  metavar="P4_PORT", default=None, | 692                  metavar="P4_PORT", default=None, | 
| 694                  help=("Perforce server and port (optional)")) | 693                  help=("Perforce server and port (optional)")) | 
| 695 group.add_option("--p4_changelist", action="store", dest="p4_changelist", | 694 group.add_option("--p4_changelist", action="store", dest="p4_changelist", | 
| (...skipping 307 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 1003   """Helper to guess the content-type from the filename.""" | 1002   """Helper to guess the content-type from the filename.""" | 
| 1004   return mimetypes.guess_type(filename)[0] or 'application/octet-stream' | 1003   return mimetypes.guess_type(filename)[0] or 'application/octet-stream' | 
| 1005 | 1004 | 
| 1006 | 1005 | 
| 1007 # Use a shell for subcommands on Windows to get a PATH search. | 1006 # Use a shell for subcommands on Windows to get a PATH search. | 
| 1008 use_shell = sys.platform.startswith("win") | 1007 use_shell = sys.platform.startswith("win") | 
| 1009 | 1008 | 
| 1010 def RunShellWithReturnCodeAndStderr(command, print_output=False, | 1009 def RunShellWithReturnCodeAndStderr(command, print_output=False, | 
| 1011                            universal_newlines=True, | 1010                            universal_newlines=True, | 
| 1012                            env=os.environ): | 1011                            env=os.environ): | 
| 1013   """Executes a command and returns the output from stdout, stderr and the retur
      n code. | 1012   """Run a command and return output from stdout, stderr and the return code. | 
| 1014 | 1013 | 
| 1015   Args: | 1014   Args: | 
| 1016     command: Command to execute. | 1015     command: Command to execute. | 
| 1017     print_output: If True, the output is printed to stdout. | 1016     print_output: If True, the output is printed to stdout. | 
| 1018                   If False, both stdout and stderr are ignored. | 1017                   If False, both stdout and stderr are ignored. | 
| 1019     universal_newlines: Use universal_newlines flag (default: True). | 1018     universal_newlines: Use universal_newlines flag (default: True). | 
| 1020 | 1019 | 
| 1021   Returns: | 1020   Returns: | 
| 1022     Tuple (stdout, stderr, return code) | 1021     Tuple (stdout, stderr, return code) | 
| 1023   """ | 1022   """ | 
| (...skipping 10 matching lines...) Expand all  Loading... | 
| 1034       if not line: | 1033       if not line: | 
| 1035         break | 1034         break | 
| 1036       print line.strip("\n") | 1035       print line.strip("\n") | 
| 1037       output_array.append(line) | 1036       output_array.append(line) | 
| 1038     output = "".join(output_array) | 1037     output = "".join(output_array) | 
| 1039   else: | 1038   else: | 
| 1040     output = p.stdout.read() | 1039     output = p.stdout.read() | 
| 1041   p.wait() | 1040   p.wait() | 
| 1042   errout = p.stderr.read() | 1041   errout = p.stderr.read() | 
| 1043   if print_output and errout: | 1042   if print_output and errout: | 
| 1044     print >>sys.stderr, errout | 1043     print >> sys.stderr, errout | 
| 1045   p.stdout.close() | 1044   p.stdout.close() | 
| 1046   p.stderr.close() | 1045   p.stderr.close() | 
| 1047   return output, errout, p.returncode | 1046   return output, errout, p.returncode | 
| 1048 | 1047 | 
| 1049 def RunShellWithReturnCode(command, print_output=False, | 1048 def RunShellWithReturnCode(command, print_output=False, | 
| 1050                            universal_newlines=True, | 1049                            universal_newlines=True, | 
| 1051                            env=os.environ): | 1050                            env=os.environ): | 
| 1052   """Executes a command and returns the output from stdout and the return code."
      "" | 1051   """Run a command and return output from stdout and the return code.""" | 
| 1053   out, err, retcode = RunShellWithReturnCodeAndStderr(command, print_output, | 1052   out, err, retcode = RunShellWithReturnCodeAndStderr(command, print_output, | 
| 1054                            universal_newlines, env) | 1053                            universal_newlines, env) | 
| 1055   return out, retcode | 1054   return out, retcode | 
| 1056 | 1055 | 
| 1057 def RunShell(command, silent_ok=False, universal_newlines=True, | 1056 def RunShell(command, silent_ok=False, universal_newlines=True, | 
| 1058              print_output=False, env=os.environ): | 1057              print_output=False, env=os.environ): | 
| 1059   data, retcode = RunShellWithReturnCode(command, print_output, | 1058   data, retcode = RunShellWithReturnCode(command, print_output, | 
| 1060                                          universal_newlines, env) | 1059                                          universal_newlines, env) | 
| 1061   if retcode: | 1060   if retcode: | 
| 1062     ErrorExit("Got error status from %s:\n%s" % (command, data)) | 1061     ErrorExit("Got error status from %s:\n%s" % (command, data)) | 
| (...skipping 154 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 1217 | 1216 | 
| 1218     for t in threads: | 1217     for t in threads: | 
| 1219       print t.get(timeout=60) | 1218       print t.get(timeout=60) | 
| 1220 | 1219 | 
| 1221 | 1220 | 
| 1222   def IsImage(self, filename): | 1221   def IsImage(self, filename): | 
| 1223     """Returns true if the filename has an image extension.""" | 1222     """Returns true if the filename has an image extension.""" | 
| 1224     mimetype =  mimetypes.guess_type(filename)[0] | 1223     mimetype =  mimetypes.guess_type(filename)[0] | 
| 1225     if not mimetype: | 1224     if not mimetype: | 
| 1226       return False | 1225       return False | 
| 1227     return mimetype.startswith("image/") and not mimetype.startswith("image/svg"
      ) | 1226     return (mimetype.startswith("image/") and | 
|  | 1227             not mimetype.startswith("image/svg")) | 
| 1228 | 1228 | 
| 1229   def IsBinaryData(self, data): | 1229   def IsBinaryData(self, data): | 
| 1230     """Returns true if data contains a null byte.""" | 1230     """Returns true if data contains a null byte.""" | 
| 1231     # Derived from how Mercurial's heuristic, see | 1231     # Derived from how Mercurial's heuristic, see | 
| 1232     # http://selenic.com/hg/file/848a6658069e/mercurial/util.py#l229 | 1232     # http://selenic.com/hg/file/848a6658069e/mercurial/util.py#l229 | 
| 1233     return bool(data and "\0" in data) | 1233     return bool(data and "\0" in data) | 
| 1234 | 1234 | 
| 1235 | 1235 | 
| 1236 class SubversionVCS(VersionControlSystem): | 1236 class SubversionVCS(VersionControlSystem): | 
| 1237   """Implementation of the VersionControlSystem interface for Subversion.""" | 1237   """Implementation of the VersionControlSystem interface for Subversion.""" | 
| (...skipping 25 matching lines...) Expand all  Loading... | 
| 1263 | 1263 | 
| 1264   def _GuessBase(self, required): | 1264   def _GuessBase(self, required): | 
| 1265     """Returns base URL for current diff. | 1265     """Returns base URL for current diff. | 
| 1266 | 1266 | 
| 1267     Args: | 1267     Args: | 
| 1268       required: If true, exits if the url can't be guessed, otherwise None is | 1268       required: If true, exits if the url can't be guessed, otherwise None is | 
| 1269         returned. | 1269         returned. | 
| 1270     """ | 1270     """ | 
| 1271     url = self._GetInfo("URL") | 1271     url = self._GetInfo("URL") | 
| 1272     if url: | 1272     if url: | 
| 1273         scheme, netloc, path, params, query, fragment = urlparse.urlparse(url) | 1273       scheme, netloc, path, params, query, fragment = urlparse.urlparse(url) | 
| 1274         guess = "" | 1274       guess = "" | 
| 1275         # TODO(anatoli) - repository specific hacks should be handled by server | 1275       # TODO(anatoli) - repository specific hacks should be handled by server | 
| 1276         if netloc == "svn.python.org" and scheme == "svn+ssh": | 1276       if netloc == "svn.python.org" and scheme == "svn+ssh": | 
| 1277           path = "projects" + path | 1277         path = "projects" + path | 
| 1278           scheme = "http" | 1278         scheme = "http" | 
| 1279           guess = "Python " | 1279         guess = "Python " | 
| 1280         elif netloc.endswith(".googlecode.com"): | 1280       elif netloc.endswith(".googlecode.com"): | 
| 1281           scheme = "http" | 1281         scheme = "http" | 
| 1282           guess = "Google Code " | 1282         guess = "Google Code " | 
| 1283         path = path + "/" | 1283       path = path + "/" | 
| 1284         base = urlparse.urlunparse((scheme, netloc, path, params, | 1284       base = urlparse.urlunparse((scheme, netloc, path, params, | 
| 1285                                     query, fragment)) | 1285                                   query, fragment)) | 
| 1286         LOGGER.info("Guessed %sbase = %s", guess, base) | 1286       LOGGER.info("Guessed %sbase = %s", guess, base) | 
| 1287         return base | 1287       return base | 
| 1288     if required: | 1288     if required: | 
| 1289       ErrorExit("Can't find URL in output from svn info") | 1289       ErrorExit("Can't find URL in output from svn info") | 
| 1290     return None | 1290     return None | 
| 1291 | 1291 | 
| 1292   def _GetInfo(self, key): | 1292   def _GetInfo(self, key): | 
| 1293     """Parses 'svn info' for current dir. Returns value for key or None""" | 1293     """Parses 'svn info' for current dir. Returns value for key or None""" | 
| 1294     for line in RunShell(["svn", "info"]).splitlines(): | 1294     for line in RunShell(["svn", "info"]).splitlines(): | 
| 1295       if line.startswith(key + ": "): | 1295       if line.startswith(key + ": "): | 
| 1296         return line.split(":", 1)[1].strip() | 1296         return line.split(":", 1)[1].strip() | 
| 1297 | 1297 | 
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 1332       'Id':                  ['Id'], | 1332       'Id':                  ['Id'], | 
| 1333 | 1333 | 
| 1334       # Aliases | 1334       # Aliases | 
| 1335       'LastChangedDate':     ['LastChangedDate', 'Date'], | 1335       'LastChangedDate':     ['LastChangedDate', 'Date'], | 
| 1336       'LastChangedRevision': ['LastChangedRevision', 'Rev', 'Revision'], | 1336       'LastChangedRevision': ['LastChangedRevision', 'Rev', 'Revision'], | 
| 1337       'LastChangedBy':       ['LastChangedBy', 'Author'], | 1337       'LastChangedBy':       ['LastChangedBy', 'Author'], | 
| 1338       'URL':                 ['URL', 'HeadURL'], | 1338       'URL':                 ['URL', 'HeadURL'], | 
| 1339     } | 1339     } | 
| 1340 | 1340 | 
| 1341     def repl(m): | 1341     def repl(m): | 
| 1342        if m.group(2): | 1342       if m.group(2): | 
| 1343          return "$%s::%s$" % (m.group(1), " " * len(m.group(3))) | 1343         return "$%s::%s$" % (m.group(1), " " * len(m.group(3))) | 
| 1344        return "$%s$" % m.group(1) | 1344       return "$%s$" % m.group(1) | 
|  | 1345 | 
| 1345     keywords = [keyword | 1346     keywords = [keyword | 
| 1346                 for name in keyword_str.split(" ") | 1347                 for name in keyword_str.split(" ") | 
| 1347                 for keyword in svn_keywords.get(name, [])] | 1348                 for keyword in svn_keywords.get(name, [])] | 
| 1348     return re.sub(r"\$(%s):(:?)([^\$]+)\$" % '|'.join(keywords), repl, content) | 1349     return re.sub(r"\$(%s):(:?)([^\$]+)\$" % '|'.join(keywords), repl, content) | 
| 1349 | 1350 | 
| 1350   def GetUnknownFiles(self): | 1351   def GetUnknownFiles(self): | 
| 1351     status = RunShell(["svn", "status", "--ignore-externals"], silent_ok=True) | 1352     status = RunShell(["svn", "status", "--ignore-externals"], silent_ok=True) | 
| 1352     unknown_files = [] | 1353     unknown_files = [] | 
| 1353     for line in status.split("\n"): | 1354     for line in status.split("\n"): | 
| 1354       if line and line[0] == "?": | 1355       if line and line[0] == "?": | 
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 1387     # the correct status for a file. | 1388     # the correct status for a file. | 
| 1388     else: | 1389     else: | 
| 1389       dirname, relfilename = os.path.split(filename) | 1390       dirname, relfilename = os.path.split(filename) | 
| 1390       if dirname not in self.svnls_cache: | 1391       if dirname not in self.svnls_cache: | 
| 1391         cmd = ["svn", "list", "-r", self.rev_start, | 1392         cmd = ["svn", "list", "-r", self.rev_start, | 
| 1392                self._EscapeFilename(dirname) or "."] | 1393                self._EscapeFilename(dirname) or "."] | 
| 1393         out, err, returncode = RunShellWithReturnCodeAndStderr(cmd) | 1394         out, err, returncode = RunShellWithReturnCodeAndStderr(cmd) | 
| 1394         if returncode: | 1395         if returncode: | 
| 1395           # Directory might not yet exist at start revison | 1396           # Directory might not yet exist at start revison | 
| 1396           # svn: Unable to find repository location for 'abc' in revision nnn | 1397           # svn: Unable to find repository location for 'abc' in revision nnn | 
| 1397           if re.match('^svn: Unable to find repository location for .+ in revisi
      on \d+', err): | 1398           if re.match('^svn: Unable to find repository location ' | 
|  | 1399                       'for .+ in revision \d+', err): | 
| 1398             old_files = () | 1400             old_files = () | 
| 1399           else: | 1401           else: | 
| 1400             ErrorExit("Failed to get status for %s:\n%s" % (filename, err)) | 1402             ErrorExit("Failed to get status for %s:\n%s" % (filename, err)) | 
| 1401         else: | 1403         else: | 
| 1402           old_files = out.splitlines() | 1404           old_files = out.splitlines() | 
| 1403         args = ["svn", "list"] | 1405         args = ["svn", "list"] | 
| 1404         if self.rev_end: | 1406         if self.rev_end: | 
| 1405           args += ["-r", self.rev_end] | 1407           args += ["-r", self.rev_end] | 
| 1406         cmd = args + [self._EscapeFilename(dirname) or "."] | 1408         cmd = args + [self._EscapeFilename(dirname) or "."] | 
| 1407         out, returncode = RunShellWithReturnCode(cmd) | 1409         out, returncode = RunShellWithReturnCode(cmd) | 
| (...skipping 201 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 1609     # git config key "diff.external" is used). | 1611     # git config key "diff.external" is used). | 
| 1610     env = os.environ.copy() | 1612     env = os.environ.copy() | 
| 1611     if "GIT_EXTERNAL_DIFF" in env: | 1613     if "GIT_EXTERNAL_DIFF" in env: | 
| 1612       del env["GIT_EXTERNAL_DIFF"] | 1614       del env["GIT_EXTERNAL_DIFF"] | 
| 1613     # -M/-C will not print the diff for the deleted file when a file is renamed. | 1615     # -M/-C will not print the diff for the deleted file when a file is renamed. | 
| 1614     # This is confusing because the original file will not be shown on the | 1616     # This is confusing because the original file will not be shown on the | 
| 1615     # review when a file is renamed. So, get a diff with ONLY deletes, then | 1617     # review when a file is renamed. So, get a diff with ONLY deletes, then | 
| 1616     # append a diff (with rename detection), without deletes. | 1618     # append a diff (with rename detection), without deletes. | 
| 1617     cmd = [ | 1619     cmd = [ | 
| 1618         "git", "diff", "--no-color", "--no-ext-diff", "--full-index", | 1620         "git", "diff", "--no-color", "--no-ext-diff", "--full-index", | 
| 1619         "--ignore-submodules", | 1621         "--ignore-submodules", "--src-prefix=a/", "--dst-prefix=b/", | 
| 1620     ] | 1622     ] | 
| 1621     diff = RunShell( | 1623     diff = RunShell( | 
| 1622         cmd + ["--no-renames", "--diff-filter=D"] + extra_args, | 1624         cmd + ["--no-renames", "--diff-filter=D"] + extra_args, | 
| 1623         env=env, silent_ok=True) | 1625         env=env, silent_ok=True) | 
|  | 1626     assert 0 <= self.options.git_similarity <= 100 | 
| 1624     if self.options.git_find_copies: | 1627     if self.options.git_find_copies: | 
| 1625       similarity_options = ["--find-copies-harder", "-l100000", | 1628       similarity_options = ["-l100000", "-C%d%%" % self.options.git_similarity] | 
| 1626                             "-C%s" % self.options.git_similarity ] | 1629       if self.options.git_find_copies_harder: | 
|  | 1630         similarity_options.append("--find-copies-harder") | 
| 1627     else: | 1631     else: | 
| 1628       similarity_options = ["-M%s" % self.options.git_similarity ] | 1632       similarity_options = ["-M%d%%" % self.options.git_similarity ] | 
| 1629     diff += RunShell( | 1633     diff += RunShell( | 
| 1630         cmd + ["--diff-filter=AMCRT"] + similarity_options + extra_args, | 1634         cmd + ["--diff-filter=AMCRT"] + similarity_options + extra_args, | 
| 1631         env=env, silent_ok=True) | 1635         env=env, silent_ok=True) | 
| 1632 | 1636 | 
| 1633     # The CL could be only file deletion or not. So accept silent diff for both | 1637     # The CL could be only file deletion or not. So accept silent diff for both | 
| 1634     # commands then check for an empty diff manually. | 1638     # commands then check for an empty diff manually. | 
| 1635     if not diff: | 1639     if not diff: | 
| 1636       ErrorExit("No output from %s" % (cmd + extra_args)) | 1640       ErrorExit("No output from %s" % (cmd + extra_args)) | 
| 1637     return diff | 1641     return diff | 
| 1638 | 1642 | 
| (...skipping 440 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 2079         diffData.base_rev = fstat["headRev"] # Re-adding a deleted file | 2083         diffData.base_rev = fstat["headRev"] # Re-adding a deleted file | 
| 2080       else: | 2084       else: | 
| 2081         diffData.base_rev = "0" # Brand new file | 2085         diffData.base_rev = "0" # Brand new file | 
| 2082       diffData.working_copy = False | 2086       diffData.working_copy = False | 
| 2083       rel_path = self.GetLocalFilename(diffData.filename) | 2087       rel_path = self.GetLocalFilename(diffData.filename) | 
| 2084       diffData.file_body = open(rel_path, 'r').read() | 2088       diffData.file_body = open(rel_path, 'r').read() | 
| 2085       # Replicate svn's list of changed lines | 2089       # Replicate svn's list of changed lines | 
| 2086       line_count = len(diffData.file_body.splitlines()) | 2090       line_count = len(diffData.file_body.splitlines()) | 
| 2087       diffData.change_summary = "@@ -0,0 +1" | 2091       diffData.change_summary = "@@ -0,0 +1" | 
| 2088       if line_count > 1: | 2092       if line_count > 1: | 
| 2089           diffData.change_summary += ",%d" % line_count | 2093         diffData.change_summary += ",%d" % line_count | 
| 2090       diffData.change_summary += " @@" | 2094       diffData.change_summary += " @@" | 
| 2091       diffData.prefix = "+" | 2095       diffData.prefix = "+" | 
| 2092       return diffData | 2096       return diffData | 
| 2093 | 2097 | 
| 2094     def GenerateDeleteDiff(diffData): | 2098     def GenerateDeleteDiff(diffData): | 
| 2095       diffData.base_rev = self.GetBaseRevision(diffData.filename) | 2099       diffData.base_rev = self.GetBaseRevision(diffData.filename) | 
| 2096       is_base_binary = self.IsBaseBinary(diffData.filename) | 2100       is_base_binary = self.IsBaseBinary(diffData.filename) | 
| 2097       # For deletes, base_filename == filename | 2101       # For deletes, base_filename == filename | 
| 2098       diffData.file_body = self.GetFileContent(diffData.base_filename, | 2102       diffData.file_body = self.GetFileContent(diffData.base_filename, | 
| 2099           None, | 2103           None, | 
| (...skipping 606 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 2706     os.environ['LC_ALL'] = 'C' | 2710     os.environ['LC_ALL'] = 'C' | 
| 2707     RealMain(sys.argv) | 2711     RealMain(sys.argv) | 
| 2708   except KeyboardInterrupt: | 2712   except KeyboardInterrupt: | 
| 2709     print | 2713     print | 
| 2710     StatusUpdate("Interrupted.") | 2714     StatusUpdate("Interrupted.") | 
| 2711     sys.exit(1) | 2715     sys.exit(1) | 
| 2712 | 2716 | 
| 2713 | 2717 | 
| 2714 if __name__ == "__main__": | 2718 if __name__ == "__main__": | 
| 2715   main() | 2719   main() | 
| OLD | NEW | 
|---|