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 11 matching lines...) Expand all Loading... |
22 __version__ = '1.0' | 22 __version__ = '1.0' |
23 | 23 |
24 | 24 |
25 CODEREVIEW_SETTINGS = { | 25 CODEREVIEW_SETTINGS = { |
26 # Default values. | 26 # Default values. |
27 "CODE_REVIEW_SERVER": "codereview.chromium.org", | 27 "CODE_REVIEW_SERVER": "codereview.chromium.org", |
28 "CC_LIST": "chromium-reviews@googlegroups.com", | 28 "CC_LIST": "chromium-reviews@googlegroups.com", |
29 "VIEW_VC": "http://src.chromium.org/viewvc/chrome?view=rev&revision=", | 29 "VIEW_VC": "http://src.chromium.org/viewvc/chrome?view=rev&revision=", |
30 } | 30 } |
31 | 31 |
32 # Use a shell for subcommands on Windows to get a PATH search, and because svn | |
33 # may be a batch file. | |
34 use_shell = sys.platform.startswith("win") | |
35 | |
36 # globals that store the root of the current repository and the directory where | 32 # globals that store the root of the current repository and the directory where |
37 # we store information about changelists. | 33 # we store information about changelists. |
38 repository_root = "" | 34 repository_root = "" |
39 gcl_info_dir = "" | 35 gcl_info_dir = "" |
40 | 36 |
41 # Filename where we store repository specific information for gcl. | 37 # Filename where we store repository specific information for gcl. |
42 CODEREVIEW_SETTINGS_FILE = "codereview.settings" | 38 CODEREVIEW_SETTINGS_FILE = "codereview.settings" |
43 | 39 |
44 # Warning message when the change appears to be missing tests. | 40 # Warning message when the change appears to be missing tests. |
45 MISSING_TEST_MSG = "Change contains new or modified methods, but no new tests!" | 41 MISSING_TEST_MSG = "Change contains new or modified methods, but no new tests!" |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
77 def IsSVNMoved(filename): | 73 def IsSVNMoved(filename): |
78 """Determine if a file has been added through svn mv""" | 74 """Determine if a file has been added through svn mv""" |
79 info = GetSVNFileInfo(filename) | 75 info = GetSVNFileInfo(filename) |
80 return (info.get('Copied From URL') and | 76 return (info.get('Copied From URL') and |
81 info.get('Copied From Rev') and | 77 info.get('Copied From Rev') and |
82 info.get('Schedule') == 'add') | 78 info.get('Schedule') == 'add') |
83 | 79 |
84 | 80 |
85 def GetSVNFileInfo(file): | 81 def GetSVNFileInfo(file): |
86 """Returns a dictionary from the svn info output for the given file.""" | 82 """Returns a dictionary from the svn info output for the given file.""" |
87 dom = ParseXML(RunShell(["svn", "info", "--xml", file])) | 83 output = RunShell(["svn", "info", "--xml", file]) |
| 84 dom = ParseXML(output) |
88 result = {} | 85 result = {} |
89 if dom: | 86 if dom: |
90 # /info/entry/ | 87 # /info/entry/ |
91 # url | 88 # url |
92 # reposityory/(root|uuid) | 89 # reposityory/(root|uuid) |
93 # wc-info/(schedule|depth) | 90 # wc-info/(schedule|depth) |
94 # commit/(author|date) | 91 # commit/(author|date) |
95 result['Node Kind'] = GetNodeNamedAttributeText(dom, 'entry', 'kind') | 92 result['Node Kind'] = GetNodeNamedAttributeText(dom, 'entry', 'kind') |
96 result['Repository Root'] = GetNamedNodeText(dom, 'root') | 93 result['Repository Root'] = GetNamedNodeText(dom, 'root') |
97 result['Schedule'] = GetNamedNodeText(dom, 'schedule') | 94 result['Schedule'] = GetNamedNodeText(dom, 'schedule') |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
140 'conflicted': 'C', | 137 'conflicted': 'C', |
141 'deleted': 'D', | 138 'deleted': 'D', |
142 'ignored': 'I', | 139 'ignored': 'I', |
143 'missing': '!', | 140 'missing': '!', |
144 'modified': 'M', | 141 'modified': 'M', |
145 'normal': ' ', | 142 'normal': ' ', |
146 'replaced': 'R', | 143 'replaced': 'R', |
147 'unversioned': '?', | 144 'unversioned': '?', |
148 # TODO(maruel): Find the corresponding strings for X, ~ | 145 # TODO(maruel): Find the corresponding strings for X, ~ |
149 } | 146 } |
150 dom = ParseXML(RunShell(command)) | 147 output = RunShell(command) |
| 148 dom = ParseXML(output) |
151 results = [] | 149 results = [] |
152 if dom: | 150 if dom: |
153 # /status/target/entry/(wc-status|commit|author|date) | 151 # /status/target/entry/(wc-status|commit|author|date) |
154 for target in dom.getElementsByTagName('target'): | 152 for target in dom.getElementsByTagName('target'): |
155 base_path = target.getAttribute('path') | 153 base_path = target.getAttribute('path') |
156 for entry in target.getElementsByTagName('entry'): | 154 for entry in target.getElementsByTagName('entry'): |
157 file = entry.getAttribute('path') | 155 file = entry.getAttribute('path') |
158 wc_status = entry.getElementsByTagName('wc-status') | 156 wc_status = entry.getElementsByTagName('wc-status') |
159 assert len(wc_status) == 1 | 157 assert len(wc_status) == 1 |
160 # Emulate svn 1.5 status ouput... | 158 # Emulate svn 1.5 status ouput... |
(...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
279 | 277 |
280 def ErrorExit(msg, exit=True): | 278 def ErrorExit(msg, exit=True): |
281 """Print an error message to stderr and optionally exit.""" | 279 """Print an error message to stderr and optionally exit.""" |
282 print >>sys.stderr, msg | 280 print >>sys.stderr, msg |
283 if exit: | 281 if exit: |
284 sys.exit(1) | 282 sys.exit(1) |
285 | 283 |
286 | 284 |
287 def RunShellWithReturnCode(command, print_output=False): | 285 def RunShellWithReturnCode(command, print_output=False): |
288 """Executes a command and returns the output and the return code.""" | 286 """Executes a command and returns the output and the return code.""" |
| 287 # Use a shell for subcommands on Windows to get a PATH search, and because svn |
| 288 # may be a batch file. |
| 289 use_shell = sys.platform.startswith("win") |
289 p = subprocess.Popen(command, stdout=subprocess.PIPE, | 290 p = subprocess.Popen(command, stdout=subprocess.PIPE, |
290 stderr=subprocess.STDOUT, shell=use_shell, | 291 stderr=subprocess.STDOUT, shell=use_shell, |
291 universal_newlines=True) | 292 universal_newlines=True) |
292 if print_output: | 293 if print_output: |
293 output_array = [] | 294 output_array = [] |
294 while True: | 295 while True: |
295 line = p.stdout.readline() | 296 line = p.stdout.readline() |
296 if not line: | 297 if not line: |
297 break | 298 break |
298 if print_output: | 299 if print_output: |
(...skipping 20 matching lines...) Expand all Loading... |
319 return result | 320 return result |
320 | 321 |
321 | 322 |
322 def WriteFile(filename, contents): | 323 def WriteFile(filename, contents): |
323 """Overwrites the file with the given contents.""" | 324 """Overwrites the file with the given contents.""" |
324 file = open(filename, 'w') | 325 file = open(filename, 'w') |
325 file.write(contents) | 326 file.write(contents) |
326 file.close() | 327 file.close() |
327 | 328 |
328 | 329 |
329 class ChangeInfo: | 330 class ChangeInfo(object): |
330 """Holds information about a changelist. | 331 """Holds information about a changelist. |
331 | 332 |
332 issue: the Rietveld issue number, of "" if it hasn't been uploaded yet. | 333 issue: the Rietveld issue number, of "" if it hasn't been uploaded yet. |
333 description: the description. | 334 description: the description. |
334 files: a list of 2 tuple containing (status, filename) of changed files, | 335 files: a list of 2 tuple containing (status, filename) of changed files, |
335 with paths being relative to the top repository directory. | 336 with paths being relative to the top repository directory. |
336 """ | 337 """ |
337 def __init__(self, name="", issue="", description="", files=[]): | 338 def __init__(self, name="", issue="", description="", files=None): |
338 self.name = name | 339 self.name = name |
339 self.issue = issue | 340 self.issue = issue |
340 self.description = description | 341 self.description = description |
| 342 if files is None: |
| 343 files = [] |
341 self.files = files | 344 self.files = files |
342 self.patch = None | 345 self.patch = None |
343 | 346 |
344 def FileList(self): | 347 def FileList(self): |
345 """Returns a list of files.""" | 348 """Returns a list of files.""" |
346 return [file[1] for file in self.files] | 349 return [file[1] for file in self.files] |
347 | 350 |
348 def _NonDeletedFileList(self): | 351 def _NonDeletedFileList(self): |
349 """Returns a list of files in this change, not including deleted files.""" | 352 """Returns a list of files in this change, not including deleted files.""" |
350 return [file[1] for file in self.files if not file[0].startswith("D")] | 353 return [file[1] for file in self.files if not file[0].startswith("D")] |
(...skipping 833 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1184 # the files. This allows commands such as 'gcl diff xxx' to work. | 1187 # the files. This allows commands such as 'gcl diff xxx' to work. |
1185 args =["svn", command] | 1188 args =["svn", command] |
1186 root = GetRepositoryRoot() | 1189 root = GetRepositoryRoot() |
1187 args.extend([os.path.join(root, x) for x in change_info.FileList()]) | 1190 args.extend([os.path.join(root, x) for x in change_info.FileList()]) |
1188 RunShell(args, True) | 1191 RunShell(args, True) |
1189 return 0 | 1192 return 0 |
1190 | 1193 |
1191 | 1194 |
1192 if __name__ == "__main__": | 1195 if __name__ == "__main__": |
1193 sys.exit(main()) | 1196 sys.exit(main()) |
OLD | NEW |