| 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 926 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 937 new_content: For text files, this is empty. For binary files, this is | 937 new_content: For text files, this is empty. For binary files, this is |
| 938 the contents of the new file, since the diff output won't contain | 938 the contents of the new file, since the diff output won't contain |
| 939 information to reconstruct the current file. | 939 information to reconstruct the current file. |
| 940 is_binary: True iff the file is binary. | 940 is_binary: True iff the file is binary. |
| 941 status: The status of the file. | 941 status: The status of the file. |
| 942 """ | 942 """ |
| 943 | 943 |
| 944 raise NotImplementedError( | 944 raise NotImplementedError( |
| 945 "abstract method -- subclass %s must override" % self.__class__) | 945 "abstract method -- subclass %s must override" % self.__class__) |
| 946 | 946 |
| 947 | |
| 948 def GetBaseFiles(self, diff): | 947 def GetBaseFiles(self, diff): |
| 949 """Helper that calls GetBase file for each file in the patch. | 948 """Helper that calls GetBase file for each file in the patch. |
| 950 | 949 |
| 951 Returns: | 950 Returns: |
| 952 A dictionary that maps from filename to GetBaseFile's tuple. Filenames | 951 A dictionary that maps from filename to GetBaseFile's tuple. Filenames |
| 953 are retrieved based on lines that start with "Index:" or | 952 are retrieved based on lines that start with "Index:" or |
| 954 "Property changes on:". | 953 "Property changes on:". |
| 955 """ | 954 """ |
| 956 files = {} | 955 files = {} |
| 957 for line in diff.splitlines(True): | 956 for line in diff.splitlines(True): |
| 958 if line.startswith('Index:') or line.startswith('Property changes on:'): | 957 if line.startswith('Index:') or line.startswith('Property changes on:'): |
| 959 unused, filename = line.split(':', 1) | 958 unused, filename = line.split(':', 1) |
| 960 # On Windows if a file has property changes its filename uses '\' | 959 # On Windows if a file has property changes its filename uses '\' |
| 961 # instead of '/'. | 960 # instead of '/'. |
| 962 filename = filename.strip().replace('\\', '/') | 961 filename = filename.strip().replace('\\', '/') |
| 963 files[filename] = self.GetBaseFile(filename) | 962 files[filename] = self.GetBaseFile(filename) |
| 964 return files | 963 return files |
| 965 | 964 |
| 966 | |
| 967 def UploadBaseFiles(self, issue, rpc_server, patch_list, patchset, options, | 965 def UploadBaseFiles(self, issue, rpc_server, patch_list, patchset, options, |
| 968 files): | 966 files): |
| 969 """Uploads the base files (and if necessary, the current ones as well).""" | 967 """Uploads the base files (and if necessary, the current ones as well).""" |
| 970 | 968 |
| 971 def UploadFile(filename, file_id, content, is_binary, status, is_base): | 969 def UploadFile(filename, file_id, content, is_binary, status, is_base): |
| 972 """Uploads a file to the server.""" | 970 """Uploads a file to the server.""" |
| 973 file_too_large = False | 971 file_too_large = False |
| 974 if is_base: | 972 if is_base: |
| 975 type = "base" | 973 type = "base" |
| 976 else: | 974 else: |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1026 file_id, base_content, is_binary, status, True)) | 1024 file_id, base_content, is_binary, status, True)) |
| 1027 threads.append(t) | 1025 threads.append(t) |
| 1028 if new_content != None: | 1026 if new_content != None: |
| 1029 t = thread_pool.apply_async(UploadFile, args=(filename, | 1027 t = thread_pool.apply_async(UploadFile, args=(filename, |
| 1030 file_id, new_content, is_binary, status, False)) | 1028 file_id, new_content, is_binary, status, False)) |
| 1031 threads.append(t) | 1029 threads.append(t) |
| 1032 | 1030 |
| 1033 for t in threads: | 1031 for t in threads: |
| 1034 print t.get(timeout=60) | 1032 print t.get(timeout=60) |
| 1035 | 1033 |
| 1036 | |
| 1037 def IsImage(self, filename): | 1034 def IsImage(self, filename): |
| 1038 """Returns true if the filename has an image extension.""" | 1035 """Returns true if the filename has an image extension.""" |
| 1039 mimetype = mimetypes.guess_type(filename)[0] | 1036 mimetype = mimetypes.guess_type(filename)[0] |
| 1040 if not mimetype: | 1037 if not mimetype: |
| 1041 return False | 1038 return False |
| 1042 return (mimetype.startswith("image/") and | 1039 return (mimetype.startswith("image/") and |
| 1043 not mimetype.startswith("image/svg")) | 1040 not mimetype.startswith("image/svg")) |
| 1044 | 1041 |
| 1045 def IsBinaryData(self, data): | 1042 def IsBinaryData(self, data): |
| 1046 """Returns true if data contains a null byte.""" | 1043 """Returns true if data contains a null byte.""" |
| 1047 # Derived from how Mercurial's heuristic, see | 1044 # Derived from how Mercurial's heuristic, see |
| 1048 # http://selenic.com/hg/file/848a6658069e/mercurial/util.py#l229 | 1045 # http://selenic.com/hg/file/848a6658069e/mercurial/util.py#l229 |
| 1049 return bool(data and "\0" in data) | 1046 return bool(data and "\0" in data) |
| 1050 | 1047 |
| 1048 def GetMostRecentCommitSummary(self): |
| 1049 """Returns a one line summary of the current commit.""" |
| 1050 return "" |
| 1051 |
| 1051 | 1052 |
| 1052 class SubversionVCS(VersionControlSystem): | 1053 class SubversionVCS(VersionControlSystem): |
| 1053 """Implementation of the VersionControlSystem interface for Subversion.""" | 1054 """Implementation of the VersionControlSystem interface for Subversion.""" |
| 1054 | 1055 |
| 1055 def __init__(self, options): | 1056 def __init__(self, options): |
| 1056 super(SubversionVCS, self).__init__(options) | 1057 super(SubversionVCS, self).__init__(options) |
| 1057 if self.options.revision: | 1058 if self.options.revision: |
| 1058 match = re.match(r"(\d+)(:(\d+))?", self.options.revision) | 1059 match = re.match(r"(\d+)(:(\d+))?", self.options.revision) |
| 1059 if not match: | 1060 if not match: |
| 1060 ErrorExit("Invalid Subversion revision %s." % self.options.revision) | 1061 ErrorExit("Invalid Subversion revision %s." % self.options.revision) |
| (...skipping 443 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1504 | 1505 |
| 1505 # Only include the "after" file if it's an image; otherwise it | 1506 # Only include the "after" file if it's an image; otherwise it |
| 1506 # it is reconstructed from the diff. | 1507 # it is reconstructed from the diff. |
| 1507 if hash_after: | 1508 if hash_after: |
| 1508 new_content = self.GetFileContent(hash_after) | 1509 new_content = self.GetFileContent(hash_after) |
| 1509 is_binary = is_binary or self.IsBinaryData(new_content) | 1510 is_binary = is_binary or self.IsBinaryData(new_content) |
| 1510 if not is_binary: | 1511 if not is_binary: |
| 1511 new_content = None | 1512 new_content = None |
| 1512 return (base_content, new_content, is_binary, status) | 1513 return (base_content, new_content, is_binary, status) |
| 1513 | 1514 |
| 1515 def GetMostRecentCommitSummary(self): |
| 1516 return RunShell(["git", "log", "-1", "--format=%s"], silent_ok=True).strip() |
| 1517 |
| 1514 | 1518 |
| 1515 class CVSVCS(VersionControlSystem): | 1519 class CVSVCS(VersionControlSystem): |
| 1516 """Implementation of the VersionControlSystem interface for CVS.""" | 1520 """Implementation of the VersionControlSystem interface for CVS.""" |
| 1517 | 1521 |
| 1518 def __init__(self, options): | 1522 def __init__(self, options): |
| 1519 super(CVSVCS, self).__init__(options) | 1523 super(CVSVCS, self).__init__(options) |
| 1520 | 1524 |
| 1521 def GetGUID(self): | 1525 def GetGUID(self): |
| 1522 """For now we don't know how to get repository ID for CVS""" | 1526 """For now we don't know how to get repository ID for CVS""" |
| 1523 return | 1527 return |
| (...skipping 904 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2428 | 2432 |
| 2429 # Process --message, --title and --file. | 2433 # Process --message, --title and --file. |
| 2430 message = options.message or "" | 2434 message = options.message or "" |
| 2431 title = options.title or "" | 2435 title = options.title or "" |
| 2432 if options.file: | 2436 if options.file: |
| 2433 if options.message: | 2437 if options.message: |
| 2434 ErrorExit("Can't specify both message and message file options") | 2438 ErrorExit("Can't specify both message and message file options") |
| 2435 file = open(options.file, 'r') | 2439 file = open(options.file, 'r') |
| 2436 message = file.read() | 2440 message = file.read() |
| 2437 file.close() | 2441 file.close() |
| 2438 if options.issue: | 2442 title = title or message.split('\n', 1)[0].strip() |
| 2439 prompt = "Title describing this patch set: " | 2443 if not title: |
| 2440 else: | 2444 if options.issue: |
| 2441 prompt = "New issue subject: " | 2445 prompt = "Title describing this patch set" |
| 2442 title = ( | 2446 else: |
| 2443 title or message.split('\n', 1)[0].strip() or raw_input(prompt).strip()) | 2447 prompt = "New issue subject" |
| 2448 title_default = vcs.GetMostRecentCommitSummary() |
| 2449 if title_default: |
| 2450 prompt += " [%s]" % title_default |
| 2451 title = raw_input(prompt + ": ").strip() or title_default |
| 2444 if not title and not options.issue: | 2452 if not title and not options.issue: |
| 2445 ErrorExit("A non-empty title is required for a new issue") | 2453 ErrorExit("A non-empty title is required for a new issue") |
| 2446 # For existing issues, it's fine to give a patchset an empty name. Rietveld | 2454 # For existing issues, it's fine to give a patchset an empty name. Rietveld |
| 2447 # doesn't accept that so use a whitespace. | 2455 # doesn't accept that so use a whitespace. |
| 2448 title = title or " " | 2456 title = title or " " |
| 2449 if len(title) > 100: | 2457 if len(title) > 100: |
| 2450 title = title[:99] + '…' | 2458 title = title[:99] + '…' |
| 2451 if title and not options.issue: | 2459 if title and not options.issue: |
| 2452 message = message or title | 2460 message = message or title |
| 2453 | 2461 |
| (...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2535 print | 2543 print |
| 2536 StatusUpdate("Interrupted.") | 2544 StatusUpdate("Interrupted.") |
| 2537 sys.exit(1) | 2545 sys.exit(1) |
| 2538 except auth.AuthenticationError as e: | 2546 except auth.AuthenticationError as e: |
| 2539 print >> sys.stderr, e | 2547 print >> sys.stderr, e |
| 2540 sys.exit(1) | 2548 sys.exit(1) |
| 2541 | 2549 |
| 2542 | 2550 |
| 2543 if __name__ == "__main__": | 2551 if __name__ == "__main__": |
| 2544 main() | 2552 main() |
| OLD | NEW |