OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # | 2 # |
3 # Copyright 2007 Google Inc. | 3 # Copyright 2007 Google Inc. |
4 # | 4 # |
5 # Licensed under the Apache License, Version 2.0 (the "License"); | 5 # Licensed under the Apache License, Version 2.0 (the "License"); |
6 # you may not use this file except in compliance with the License. | 6 # you may not use this file except in compliance with the License. |
7 # You may obtain a copy of the License at | 7 # You may obtain a copy of the License at |
8 # | 8 # |
9 # http://www.apache.org/licenses/LICENSE-2.0 | 9 # http://www.apache.org/licenses/LICENSE-2.0 |
10 # | 10 # |
(...skipping 17 matching lines...) Expand all Loading... |
28 CVS | 28 CVS |
29 | 29 |
30 It is important for Git/Mercurial users to specify a tree/node/branch to diff | 30 It is important for Git/Mercurial users to specify a tree/node/branch to diff |
31 against by using the '--rev' option. | 31 against by using the '--rev' option. |
32 """ | 32 """ |
33 # This code is derived from appcfg.py in the App Engine SDK (open source), | 33 # This code is derived from appcfg.py in the App Engine SDK (open source), |
34 # and from ASPN recipe #146306. | 34 # and from ASPN recipe #146306. |
35 | 35 |
36 import ConfigParser | 36 import ConfigParser |
37 import cookielib | 37 import cookielib |
| 38 import errno |
38 import fnmatch | 39 import fnmatch |
39 import getpass | 40 import getpass |
40 import logging | 41 import logging |
41 import marshal | 42 import marshal |
42 import mimetypes | 43 import mimetypes |
43 import optparse | 44 import optparse |
44 import os | 45 import os |
45 import re | 46 import re |
46 import socket | 47 import socket |
47 import subprocess | 48 import subprocess |
(...skipping 619 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
667 | 668 |
668 | 669 |
669 def GetContentType(filename): | 670 def GetContentType(filename): |
670 """Helper to guess the content-type from the filename.""" | 671 """Helper to guess the content-type from the filename.""" |
671 return mimetypes.guess_type(filename)[0] or 'application/octet-stream' | 672 return mimetypes.guess_type(filename)[0] or 'application/octet-stream' |
672 | 673 |
673 | 674 |
674 # Use a shell for subcommands on Windows to get a PATH search. | 675 # Use a shell for subcommands on Windows to get a PATH search. |
675 use_shell = sys.platform.startswith("win") | 676 use_shell = sys.platform.startswith("win") |
676 | 677 |
677 def RunShellWithReturnCode(command, print_output=False, | 678 def RunShellWithReturnCodeAndStderr(command, print_output=False, |
678 universal_newlines=True, | 679 universal_newlines=True, |
679 env=os.environ): | 680 env=os.environ): |
680 """Executes a command and returns the output from stdout and the return code. | 681 """Executes a command and returns the output from stdout, stderr and the retur
n code. |
681 | 682 |
682 Args: | 683 Args: |
683 command: Command to execute. | 684 command: Command to execute. |
684 print_output: If True, the output is printed to stdout. | 685 print_output: If True, the output is printed to stdout. |
685 If False, both stdout and stderr are ignored. | 686 If False, both stdout and stderr are ignored. |
686 universal_newlines: Use universal_newlines flag (default: True). | 687 universal_newlines: Use universal_newlines flag (default: True). |
687 | 688 |
688 Returns: | 689 Returns: |
689 Tuple (output, return code) | 690 Tuple (stdout, stderr, return code) |
690 """ | 691 """ |
691 logging.info("Running %s", command) | 692 logging.info("Running %s", command) |
| 693 env = env.copy() |
| 694 env['LC_MESSAGES'] = 'C' |
692 p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, | 695 p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, |
693 shell=use_shell, universal_newlines=universal_newlines, | 696 shell=use_shell, universal_newlines=universal_newlines, |
694 env=env) | 697 env=env) |
695 if print_output: | 698 if print_output: |
696 output_array = [] | 699 output_array = [] |
697 while True: | 700 while True: |
698 line = p.stdout.readline() | 701 line = p.stdout.readline() |
699 if not line: | 702 if not line: |
700 break | 703 break |
701 print line.strip("\n") | 704 print line.strip("\n") |
702 output_array.append(line) | 705 output_array.append(line) |
703 output = "".join(output_array) | 706 output = "".join(output_array) |
704 else: | 707 else: |
705 output = p.stdout.read() | 708 output = p.stdout.read() |
706 p.wait() | 709 p.wait() |
707 errout = p.stderr.read() | 710 errout = p.stderr.read() |
708 if print_output and errout: | 711 if print_output and errout: |
709 print >>sys.stderr, errout | 712 print >>sys.stderr, errout |
710 p.stdout.close() | 713 p.stdout.close() |
711 p.stderr.close() | 714 p.stderr.close() |
712 return output, p.returncode | 715 return output, errout, p.returncode |
713 | 716 |
| 717 def RunShellWithReturnCode(command, print_output=False, |
| 718 universal_newlines=True, |
| 719 env=os.environ): |
| 720 """Executes a command and returns the output from stdout and the return code."
"" |
| 721 out, err, retcode = RunShellWithReturnCodeAndStderr(command, print_output, |
| 722 universal_newlines, env) |
| 723 return out, retcode |
714 | 724 |
715 def RunShell(command, silent_ok=False, universal_newlines=True, | 725 def RunShell(command, silent_ok=False, universal_newlines=True, |
716 print_output=False, env=os.environ): | 726 print_output=False, env=os.environ): |
717 data, retcode = RunShellWithReturnCode(command, print_output, | 727 data, retcode = RunShellWithReturnCode(command, print_output, |
718 universal_newlines, env) | 728 universal_newlines, env) |
719 if retcode: | 729 if retcode: |
720 ErrorExit("Got error status from %s:\n%s" % (command, data)) | 730 ErrorExit("Got error status from %s:\n%s" % (command, data)) |
721 if not silent_ok and not data: | 731 if not silent_ok and not data: |
722 ErrorExit("No output from %s" % command) | 732 ErrorExit("No output from %s" % command) |
723 return data | 733 return data |
(...skipping 281 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1005 status = status_lines[2] | 1015 status = status_lines[2] |
1006 else: | 1016 else: |
1007 status = status_lines[0] | 1017 status = status_lines[0] |
1008 # If we have a revision to diff against we need to run "svn list" | 1018 # If we have a revision to diff against we need to run "svn list" |
1009 # for the old and the new revision and compare the results to get | 1019 # for the old and the new revision and compare the results to get |
1010 # the correct status for a file. | 1020 # the correct status for a file. |
1011 else: | 1021 else: |
1012 dirname, relfilename = os.path.split(filename) | 1022 dirname, relfilename = os.path.split(filename) |
1013 if dirname not in self.svnls_cache: | 1023 if dirname not in self.svnls_cache: |
1014 cmd = ["svn", "list", "-r", self.rev_start, dirname or "."] | 1024 cmd = ["svn", "list", "-r", self.rev_start, dirname or "."] |
1015 out, returncode = RunShellWithReturnCode(cmd) | 1025 out, err, returncode = RunShellWithReturnCodeAndStderr(cmd) |
1016 if returncode: | 1026 if returncode: |
1017 ErrorExit("Failed to get status for %s." % filename) | 1027 # Directory might not yet exist at start revison |
1018 old_files = out.splitlines() | 1028 # svn: Unable to find repository location for 'abc' in revision nnn |
| 1029 if re.match('^svn: Unable to find repository location for .+ in revisi
on \d+', err): |
| 1030 old_files = () |
| 1031 else: |
| 1032 ErrorExit("Failed to get status for %s:\n%s" % (filename, err)) |
| 1033 else: |
| 1034 old_files = out.splitlines() |
1019 args = ["svn", "list"] | 1035 args = ["svn", "list"] |
1020 if self.rev_end: | 1036 if self.rev_end: |
1021 args += ["-r", self.rev_end] | 1037 args += ["-r", self.rev_end] |
1022 cmd = args + [dirname or "."] | 1038 cmd = args + [dirname or "."] |
1023 out, returncode = RunShellWithReturnCode(cmd) | 1039 out, returncode = RunShellWithReturnCode(cmd) |
1024 if returncode: | 1040 if returncode: |
1025 ErrorExit("Failed to run command %s" % cmd) | 1041 ErrorExit("Failed to run command %s" % cmd) |
1026 self.svnls_cache[dirname] = (old_files, out.splitlines()) | 1042 self.svnls_cache[dirname] = (old_files, out.splitlines()) |
1027 old_files, new_files = self.svnls_cache[dirname] | 1043 old_files, new_files = self.svnls_cache[dirname] |
1028 if relfilename in old_files and relfilename not in new_files: | 1044 if relfilename in old_files and relfilename not in new_files: |
(...skipping 178 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1207 if ":" in self.options.revision: | 1223 if ":" in self.options.revision: |
1208 extra_args = self.options.revision.split(":", 1) + extra_args | 1224 extra_args = self.options.revision.split(":", 1) + extra_args |
1209 else: | 1225 else: |
1210 extra_args = [self.options.revision] + extra_args | 1226 extra_args = [self.options.revision] + extra_args |
1211 | 1227 |
1212 # --no-ext-diff is broken in some versions of Git, so try to work around | 1228 # --no-ext-diff is broken in some versions of Git, so try to work around |
1213 # this by overriding the environment (but there is still a problem if the | 1229 # this by overriding the environment (but there is still a problem if the |
1214 # git config key "diff.external" is used). | 1230 # git config key "diff.external" is used). |
1215 env = os.environ.copy() | 1231 env = os.environ.copy() |
1216 if 'GIT_EXTERNAL_DIFF' in env: del env['GIT_EXTERNAL_DIFF'] | 1232 if 'GIT_EXTERNAL_DIFF' in env: del env['GIT_EXTERNAL_DIFF'] |
1217 # -M/-C will not print the diff for the deleted file when a file is renamed. | 1233 return RunShell(["git", "diff", "--no-ext-diff", "--full-index", "-M"] |
1218 # This is confusing because the original file will not be shown on the | 1234 + extra_args, env=env) |
1219 # review when a file is renamed. So first get the diff of all deleted files, | |
1220 # then the diff of everything except deleted files with rename and copy | |
1221 # support enabled. | |
1222 cmd = ["git", "diff", "--no-ext-diff", "--full-index"] | |
1223 diff = RunShell(cmd + ["--diff-filter=D"] + extra_args, env=env, | |
1224 silent_ok=True) | |
1225 diff += RunShell(cmd + ["-C", "--diff-filter=ACMRT"] + extra_args, env=env, | |
1226 silent_ok=True) | |
1227 if not diff: | |
1228 ErrorExit("No output from %s" % (cmd + extra_args)) | |
1229 return diff | |
1230 | 1235 |
1231 def GetUnknownFiles(self): | 1236 def GetUnknownFiles(self): |
1232 status = RunShell(["git", "ls-files", "--exclude-standard", "--others"], | 1237 status = RunShell(["git", "ls-files", "--exclude-standard", "--others"], |
1233 silent_ok=True) | 1238 silent_ok=True) |
1234 return status.splitlines() | 1239 return status.splitlines() |
1235 | 1240 |
1236 def GetFileContent(self, file_hash, is_binary): | 1241 def GetFileContent(self, file_hash, is_binary): |
1237 """Returns the content of a file identified by its git hash.""" | 1242 """Returns the content of a file identified by its git hash.""" |
1238 data, retcode = RunShellWithReturnCode(["git", "show", file_hash], | 1243 data, retcode = RunShellWithReturnCode(["git", "show", file_hash], |
1239 universal_newlines=not is_binary) | 1244 universal_newlines=not is_binary) |
(...skipping 602 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1842 VCS_CVS, or VCS_UNKNOWN. | 1847 VCS_CVS, or VCS_UNKNOWN. |
1843 Since local perforce repositories can't be easily detected, this method | 1848 Since local perforce repositories can't be easily detected, this method |
1844 will only guess VCS_PERFORCE if any perforce options have been specified. | 1849 will only guess VCS_PERFORCE if any perforce options have been specified. |
1845 output is a string containing any interesting output from the vcs | 1850 output is a string containing any interesting output from the vcs |
1846 detection routine, or None if there is nothing interesting. | 1851 detection routine, or None if there is nothing interesting. |
1847 """ | 1852 """ |
1848 for attribute, value in options.__dict__.iteritems(): | 1853 for attribute, value in options.__dict__.iteritems(): |
1849 if attribute.startswith("p4") and value != None: | 1854 if attribute.startswith("p4") and value != None: |
1850 return (VCS_PERFORCE, None) | 1855 return (VCS_PERFORCE, None) |
1851 | 1856 |
| 1857 def RunDetectCommand(vcs_type, command): |
| 1858 """Helper to detect VCS by executing command. |
| 1859 |
| 1860 Returns: |
| 1861 A pair (vcs, output) or None. Throws exception on error. |
| 1862 """ |
| 1863 try: |
| 1864 out, returncode = RunShellWithReturnCode(command) |
| 1865 if returncode == 0: |
| 1866 return (vcs_type, out.strip()) |
| 1867 except OSError, (errcode, message): |
| 1868 if errcode != errno.ENOENT: # command not found code |
| 1869 raise |
| 1870 |
1852 # Mercurial has a command to get the base directory of a repository | 1871 # Mercurial has a command to get the base directory of a repository |
1853 # Try running it, but don't die if we don't have hg installed. | 1872 # Try running it, but don't die if we don't have hg installed. |
1854 # NOTE: we try Mercurial first as it can sit on top of an SVN working copy. | 1873 # NOTE: we try Mercurial first as it can sit on top of an SVN working copy. |
1855 try: | 1874 res = RunDetectCommand(VCS_MERCURIAL, ["hg", "root"]) |
1856 out, returncode = RunShellWithReturnCode(["hg", "root"]) | 1875 if res != None: |
1857 if returncode == 0: | 1876 return res |
1858 return (VCS_MERCURIAL, out.strip()) | |
1859 except OSError, (errno, message): | |
1860 if errno != 2: # ENOENT -- they don't have hg installed. | |
1861 raise | |
1862 | 1877 |
1863 # Subversion has a .svn in all working directories. | 1878 # Subversion has a .svn in all working directories. |
1864 if os.path.isdir('.svn'): | 1879 if os.path.isdir('.svn'): |
1865 logging.info("Guessed VCS = Subversion") | 1880 logging.info("Guessed VCS = Subversion") |
1866 return (VCS_SUBVERSION, None) | 1881 return (VCS_SUBVERSION, None) |
1867 | 1882 |
1868 # Git has a command to test if you're in a git tree. | 1883 # Git has a command to test if you're in a git tree. |
1869 # Try running it, but don't die if we don't have git installed. | 1884 # Try running it, but don't die if we don't have git installed. |
1870 try: | 1885 res = RunDetectCommand(VCS_GIT, ["git", "rev-parse", |
1871 out, returncode = RunShellWithReturnCode(["git", "rev-parse", | 1886 "--is-inside-work-tree"]) |
1872 "--is-inside-work-tree"]) | 1887 if res != None: |
1873 if returncode == 0: | 1888 return res |
1874 return (VCS_GIT, None) | |
1875 except OSError, (errno, message): | |
1876 if errno != 2: # ENOENT -- they don't have git installed. | |
1877 raise | |
1878 | 1889 |
1879 # detect CVS repos use `cvs status && $? == 0` rules | 1890 # detect CVS repos use `cvs status && $? == 0` rules |
1880 try: | 1891 res = RunDetectCommand(VCS_CVS, ["cvs", "status"]) |
1881 out, returncode = RunShellWithReturnCode(["cvs", "status"]) | 1892 if res != None: |
1882 if returncode == 0: | 1893 return res |
1883 return (VCS_CVS, None) | |
1884 except OSError, (errno, message): | |
1885 if errno != 2: | |
1886 raise | |
1887 | 1894 |
1888 return (VCS_UNKNOWN, None) | 1895 return (VCS_UNKNOWN, None) |
1889 | 1896 |
1890 | 1897 |
1891 def GuessVCS(options): | 1898 def GuessVCS(options): |
1892 """Helper to guess the version control system. | 1899 """Helper to guess the version control system. |
1893 | 1900 |
1894 This verifies any user-specified VersionControlSystem (by command line | 1901 This verifies any user-specified VersionControlSystem (by command line |
1895 or environment variable). If the user didn't specify one, this examines | 1902 or environment variable). If the user didn't specify one, this examines |
1896 the current directory, guesses which VersionControlSystem we're using, | 1903 the current directory, guesses which VersionControlSystem we're using, |
(...skipping 303 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2200 | 2207 |
2201 if not options.download_base: | 2208 if not options.download_base: |
2202 vcs.UploadBaseFiles(issue, rpc_server, patches, patchset, options, files) | 2209 vcs.UploadBaseFiles(issue, rpc_server, patches, patchset, options, files) |
2203 if options.send_mail: | 2210 if options.send_mail: |
2204 rpc_server.Send("/" + issue + "/mail", payload="") | 2211 rpc_server.Send("/" + issue + "/mail", payload="") |
2205 return issue, patchset | 2212 return issue, patchset |
2206 | 2213 |
2207 | 2214 |
2208 def main(): | 2215 def main(): |
2209 try: | 2216 try: |
| 2217 logging.basicConfig(format=("%(asctime).19s %(levelname)s %(filename)s:" |
| 2218 "%(lineno)s %(message)s ")) |
| 2219 os.environ['LC_ALL'] = 'C' |
2210 RealMain(sys.argv) | 2220 RealMain(sys.argv) |
2211 except KeyboardInterrupt: | 2221 except KeyboardInterrupt: |
2212 print | 2222 print |
2213 StatusUpdate("Interrupted.") | 2223 StatusUpdate("Interrupted.") |
2214 sys.exit(1) | 2224 sys.exit(1) |
2215 | 2225 |
2216 | 2226 |
2217 if __name__ == "__main__": | 2227 if __name__ == "__main__": |
2218 main() | 2228 main() |
OLD | NEW |