| OLD | NEW |
| 1 #!/usr/bin/python | 1 #!/usr/bin/python |
| 2 # Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 # | 5 # |
| 6 # Wrapper script around Rietveld's upload.py that groups files into | 6 # Wrapper script around Rietveld's upload.py that groups files into |
| 7 # changelists. | 7 # changelists. |
| 8 | 8 |
| 9 import getpass | 9 import getpass |
| 10 import os | 10 import os |
| (...skipping 21 matching lines...) Expand all Loading... |
| 32 # we store information about changelists. | 32 # we store information about changelists. |
| 33 repository_root = "" | 33 repository_root = "" |
| 34 gcl_info_dir = "" | 34 gcl_info_dir = "" |
| 35 | 35 |
| 36 # Filename where we store repository specific information for gcl. | 36 # Filename where we store repository specific information for gcl. |
| 37 CODEREVIEW_SETTINGS_FILE = "codereview.settings" | 37 CODEREVIEW_SETTINGS_FILE = "codereview.settings" |
| 38 | 38 |
| 39 # Warning message when the change appears to be missing tests. | 39 # Warning message when the change appears to be missing tests. |
| 40 MISSING_TEST_MSG = "Change contains new or modified methods, but no new tests!" | 40 MISSING_TEST_MSG = "Change contains new or modified methods, but no new tests!" |
| 41 | 41 |
| 42 # Global cache of files cached in GetInfoDir(). | 42 # Caches whether we read the codereview.settings file yet or not. |
| 43 FILES_CACHE = {} | 43 read_gcl_info = False |
| 44 | 44 |
| 45 | 45 |
| 46 def IsSVNMoved(filename): | 46 def IsSVNMoved(filename): |
| 47 """Determine if a file has been added through svn mv""" | 47 """Determine if a file has been added through svn mv""" |
| 48 info = GetSVNFileInfo(filename) | 48 info = GetSVNFileInfo(filename) |
| 49 return (info.get('Copied From URL') and | 49 return (info.get('Copied From URL') and |
| 50 info.get('Copied From Rev') and | 50 info.get('Copied From Rev') and |
| 51 info.get('Schedule') == 'add') | 51 info.get('Schedule') == 'add') |
| 52 | 52 |
| 53 | 53 |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 104 | 104 |
| 105 | 105 |
| 106 def GetInfoDir(): | 106 def GetInfoDir(): |
| 107 """Returns the directory where gcl info files are stored.""" | 107 """Returns the directory where gcl info files are stored.""" |
| 108 global gcl_info_dir | 108 global gcl_info_dir |
| 109 if not gcl_info_dir: | 109 if not gcl_info_dir: |
| 110 gcl_info_dir = os.path.join(GetRepositoryRoot(), '.svn', 'gcl_info') | 110 gcl_info_dir = os.path.join(GetRepositoryRoot(), '.svn', 'gcl_info') |
| 111 return gcl_info_dir | 111 return gcl_info_dir |
| 112 | 112 |
| 113 | 113 |
| 114 def GetCachedFile(filename, max_age=60*60*24*3, use_root=False): | |
| 115 """Retrieves a file from the repository and caches it in GetInfoDir() for | |
| 116 max_age seconds. | |
| 117 | |
| 118 use_root: If False, look up the arborescence for the first match, otherwise go | |
| 119 directory to the root repository. | |
| 120 """ | |
| 121 global FILES_CACHE | |
| 122 if filename not in FILES_CACHE: | |
| 123 # Don't try to look up twice. | |
| 124 FILES_CACHE[filename] = None | |
| 125 # First we check if we have a cached version. | |
| 126 cached_file = os.path.join(GetInfoDir(), filename) | |
| 127 if (not os.path.exists(cached_file) or | |
| 128 os.stat(cached_file).st_mtime > max_age): | |
| 129 dir_info = GetSVNFileInfo(".") | |
| 130 repo_root = dir_info["Repository Root"] | |
| 131 if use_root: | |
| 132 url_path = repo_root | |
| 133 else: | |
| 134 url_path = dir_info["URL"] | |
| 135 content = "" | |
| 136 while True: | |
| 137 # Look for the codereview.settings file at the current level. | |
| 138 svn_path = url_path + "/" + filename | |
| 139 content, rc = RunShellWithReturnCode(["svn", "cat", svn_path]) | |
| 140 if not rc: | |
| 141 # Exit the loop if the file was found. Override content. | |
| 142 break | |
| 143 # Make sure to mark settings as empty if not found. | |
| 144 content = "" | |
| 145 if url_path == repo_root: | |
| 146 # Reached the root. Abandoning search. | |
| 147 break | |
| 148 # Go up one level to try again. | |
| 149 url_path = os.path.dirname(url_path) | |
| 150 # Write a cached version even if there isn't a file, so we don't try to | |
| 151 # fetch it each time. | |
| 152 WriteFile(cached_file, content) | |
| 153 else: | |
| 154 content = ReadFile(cached_settings_file) | |
| 155 FILES_CACHE[filename] = content | |
| 156 return FILES_CACHE[filename] | |
| 157 | |
| 158 | |
| 159 def GetCodeReviewSetting(key): | 114 def GetCodeReviewSetting(key): |
| 160 """Returns a value for the given key for this repository.""" | 115 """Returns a value for the given key for this repository.""" |
| 161 # Use '__just_initialized' as a flag to determine if the settings were | 116 global read_gcl_info |
| 162 # already initialized. | 117 if not read_gcl_info: |
| 163 if '__just_initialized' not in CODEREVIEW_SETTINGS: | 118 read_gcl_info = True |
| 164 for line in GetCachedFile(CODEREVIEW_SETTINGS_FILE).splitlines(): | 119 # First we check if we have a cached version. |
| 120 cached_settings_file = os.path.join(GetInfoDir(), CODEREVIEW_SETTINGS_FILE) |
| 121 if (not os.path.exists(cached_settings_file) or |
| 122 os.stat(cached_settings_file).st_mtime > 60*60*24*3): |
| 123 dir_info = GetSVNFileInfo(".") |
| 124 repo_root = dir_info["Repository Root"] |
| 125 url_path = dir_info["URL"] |
| 126 settings = "" |
| 127 while True: |
| 128 # Look for the codereview.settings file at the current level. |
| 129 svn_path = url_path + "/" + CODEREVIEW_SETTINGS_FILE |
| 130 settings, rc = RunShellWithReturnCode(["svn", "cat", svn_path]) |
| 131 if not rc: |
| 132 # Exit the loop if the file was found. |
| 133 break |
| 134 # Make sure to mark settings as empty if not found. |
| 135 settings = "" |
| 136 if url_path == repo_root: |
| 137 # Reached the root. Abandoning search. |
| 138 break; |
| 139 # Go up one level to try again. |
| 140 url_path = os.path.dirname(url_path) |
| 141 |
| 142 # Write a cached version even if there isn't a file, so we don't try to |
| 143 # fetch it each time. |
| 144 WriteFile(cached_settings_file, settings) |
| 145 |
| 146 output = ReadFile(cached_settings_file) |
| 147 for line in output.splitlines(): |
| 165 if not line or line.startswith("#"): | 148 if not line or line.startswith("#"): |
| 166 continue | 149 continue |
| 167 k, v = line.split(": ", 1) | 150 k, v = line.split(": ", 1) |
| 168 CODEREVIEW_SETTINGS[k] = v | 151 CODEREVIEW_SETTINGS[k] = v |
| 169 CODEREVIEW_SETTINGS.setdefault('__just_initialized', None) | |
| 170 return CODEREVIEW_SETTINGS.get(key, "") | 152 return CODEREVIEW_SETTINGS.get(key, "") |
| 171 | 153 |
| 172 | 154 |
| 173 def IsTreeOpen(): | 155 def IsTreeOpen(): |
| 174 """Fetches the tree status and returns either True or False.""" | 156 """Fetches the tree status and returns either True or False.""" |
| 175 url = GetCodeReviewSetting('STATUS') | 157 url = GetCodeReviewSetting('STATUS') |
| 176 status = "" | 158 status = "" |
| 177 if url: | 159 if url: |
| 178 status = urllib2.urlopen(url).read() | 160 status = urllib2.urlopen(url).read() |
| 179 return status.find('0') == -1 | 161 return status.find('0') == -1 |
| (...skipping 414 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 594 Basic commands: | 576 Basic commands: |
| 595 ----------------------------------------- | 577 ----------------------------------------- |
| 596 gcl change change_name | 578 gcl change change_name |
| 597 Add/remove files to a changelist. Only scans the current directory and | 579 Add/remove files to a changelist. Only scans the current directory and |
| 598 subdirectories. | 580 subdirectories. |
| 599 | 581 |
| 600 gcl upload change_name [-r reviewer1@gmail.com,reviewer2@gmail.com,...] | 582 gcl upload change_name [-r reviewer1@gmail.com,reviewer2@gmail.com,...] |
| 601 [--send_mail] [--no_try] [--no_presubmit] | 583 [--send_mail] [--no_try] [--no_presubmit] |
| 602 Uploads the changelist to the server for review. | 584 Uploads the changelist to the server for review. |
| 603 | 585 |
| 604 gcl commit change_name [--no_presubmit] [--force] | 586 gcl commit change_name [--force] |
| 605 Commits the changelist to the repository. | 587 Commits the changelist to the repository. |
| 606 | 588 |
| 607 gcl lint change_name | 589 gcl lint change_name |
| 608 Check all the files in the changelist for possible style violations. | 590 Check all the files in the changelist for possible style violations. |
| 609 | 591 |
| 610 Advanced commands: | 592 Advanced commands: |
| 611 ----------------------------------------- | 593 ----------------------------------------- |
| 612 gcl delete change_name | 594 gcl delete change_name |
| 613 Deletes a changelist. | 595 Deletes a changelist. |
| 614 | 596 |
| (...skipping 388 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1003 | 985 |
| 1004 | 986 |
| 1005 def DoPresubmitChecks(change_info, committing): | 987 def DoPresubmitChecks(change_info, committing): |
| 1006 """Imports presubmit, then calls presubmit.DoPresubmitChecks.""" | 988 """Imports presubmit, then calls presubmit.DoPresubmitChecks.""" |
| 1007 # Need to import here to avoid circular dependency. | 989 # Need to import here to avoid circular dependency. |
| 1008 import presubmit | 990 import presubmit |
| 1009 result = presubmit.DoPresubmitChecks(change_info, | 991 result = presubmit.DoPresubmitChecks(change_info, |
| 1010 committing, | 992 committing, |
| 1011 verbose=False, | 993 verbose=False, |
| 1012 output_stream=sys.stdout, | 994 output_stream=sys.stdout, |
| 1013 input_stream=sys.stdin, | 995 input_stream=sys.stdin) |
| 1014 default_presubmit= | |
| 1015 GetCachedFile('PRESUBMIT.py', | |
| 1016 use_root=True)) | |
| 1017 if not result: | 996 if not result: |
| 1018 print "\nPresubmit errors, can't continue (use --no_presubmit to bypass)" | 997 print "\nPresubmit errors, can't continue (use --no_presubmit to bypass)" |
| 1019 return result | 998 return result |
| 1020 | 999 |
| 1021 | 1000 |
| 1022 def Changes(): | 1001 def Changes(): |
| 1023 """Print all the changelists and their files.""" | 1002 """Print all the changelists and their files.""" |
| 1024 for cl in GetCLs(): | 1003 for cl in GetCLs(): |
| 1025 change_info = LoadChangelistInfo(cl, True, True) | 1004 change_info = LoadChangelistInfo(cl, True, True) |
| 1026 print "\n--- Changelist " + change_info.name + ":" | 1005 print "\n--- Changelist " + change_info.name + ":" |
| (...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1113 # the files. This allows commands such as 'gcl diff xxx' to work. | 1092 # the files. This allows commands such as 'gcl diff xxx' to work. |
| 1114 args =["svn", command] | 1093 args =["svn", command] |
| 1115 root = GetRepositoryRoot() | 1094 root = GetRepositoryRoot() |
| 1116 args.extend([os.path.join(root, x) for x in change_info.FileList()]) | 1095 args.extend([os.path.join(root, x) for x in change_info.FileList()]) |
| 1117 RunShell(args, True) | 1096 RunShell(args, True) |
| 1118 return 0 | 1097 return 0 |
| 1119 | 1098 |
| 1120 | 1099 |
| 1121 if __name__ == "__main__": | 1100 if __name__ == "__main__": |
| 1122 sys.exit(main()) | 1101 sys.exit(main()) |
| OLD | NEW |