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 247 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
258 | 258 |
259 class ChangeInfo(object): | 259 class ChangeInfo(object): |
260 """Holds information about a changelist. | 260 """Holds information about a changelist. |
261 | 261 |
262 name: change name. | 262 name: change name. |
263 issue: the Rietveld issue number or 0 if it hasn't been uploaded yet. | 263 issue: the Rietveld issue number or 0 if it hasn't been uploaded yet. |
264 patchset: the Rietveld latest patchset number or 0. | 264 patchset: the Rietveld latest patchset number or 0. |
265 description: the description. | 265 description: the description. |
266 files: a list of 2 tuple containing (status, filename) of changed files, | 266 files: a list of 2 tuple containing (status, filename) of changed files, |
267 with paths being relative to the top repository directory. | 267 with paths being relative to the top repository directory. |
| 268 local_root: Local root directory |
268 """ | 269 """ |
269 | 270 |
270 _SEPARATOR = "\n-----\n" | 271 _SEPARATOR = "\n-----\n" |
271 # The info files have the following format: | 272 # The info files have the following format: |
272 # issue_id, patchset\n (, patchset is optional) | 273 # issue_id, patchset\n (, patchset is optional) |
273 # _SEPARATOR\n | 274 # _SEPARATOR\n |
274 # filepath1\n | 275 # filepath1\n |
275 # filepath2\n | 276 # filepath2\n |
276 # . | 277 # . |
277 # . | 278 # . |
278 # filepathn\n | 279 # filepathn\n |
279 # _SEPARATOR\n | 280 # _SEPARATOR\n |
280 # description | 281 # description |
281 | 282 |
282 def __init__(self, name, issue, patchset, description, files): | 283 def __init__(self, name, issue, patchset, description, files, local_root): |
283 self.name = name | 284 self.name = name |
284 self.issue = int(issue) | 285 self.issue = int(issue) |
285 self.patchset = int(patchset) | 286 self.patchset = int(patchset) |
286 self.description = description | 287 self.description = description |
287 if files is None: | 288 if files is None: |
288 files = [] | 289 files = [] |
289 self._files = files | 290 self._files = files |
290 self.patch = None | 291 self.patch = None |
291 self._local_root = GetRepositoryRoot() | 292 self._local_root = local_root |
292 | 293 |
293 def GetFileNames(self): | 294 def GetFileNames(self): |
294 """Returns the list of file names included in this change.""" | 295 """Returns the list of file names included in this change.""" |
295 return [file[1] for file in self._files] | 296 return [file[1] for file in self._files] |
296 | 297 |
297 def GetFiles(self): | 298 def GetFiles(self): |
298 """Returns the list of files included in this change with their status.""" | 299 """Returns the list of files included in this change with their status.""" |
299 return self._files | 300 return self._files |
300 | 301 |
301 def GetLocalRoot(self): | 302 def GetLocalRoot(self): |
(...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
410 (line == "{" or line.startswith(" ") or line.startswith("\t"))): | 411 (line == "{" or line.startswith(" ") or line.startswith("\t"))): |
411 definition += line | 412 definition += line |
412 | 413 |
413 # A flush-left line starts a new possible function definition. | 414 # A flush-left line starts a new possible function definition. |
414 elif not line.startswith(" ") and not line.startswith("\t"): | 415 elif not line.startswith(" ") and not line.startswith("\t"): |
415 definition = line | 416 definition = line |
416 | 417 |
417 return False | 418 return False |
418 | 419 |
419 @staticmethod | 420 @staticmethod |
420 def Load(changename, fail_on_not_found=True, update_status=False): | 421 def Load(changename, local_root, fail_on_not_found, update_status): |
421 """Gets information about a changelist. | 422 """Gets information about a changelist. |
422 | 423 |
423 Args: | 424 Args: |
424 fail_on_not_found: if True, this function will quit the program if the | 425 fail_on_not_found: if True, this function will quit the program if the |
425 changelist doesn't exist. | 426 changelist doesn't exist. |
426 update_status: if True, the svn status will be updated for all the files | 427 update_status: if True, the svn status will be updated for all the files |
427 and unchanged files will be removed. | 428 and unchanged files will be removed. |
428 | 429 |
429 Returns: a ChangeInfo object. | 430 Returns: a ChangeInfo object. |
430 """ | 431 """ |
431 info_file = GetChangelistInfoFile(changename) | 432 info_file = GetChangelistInfoFile(changename) |
432 if not os.path.exists(info_file): | 433 if not os.path.exists(info_file): |
433 if fail_on_not_found: | 434 if fail_on_not_found: |
434 ErrorExit("Changelist " + changename + " not found.") | 435 ErrorExit("Changelist " + changename + " not found.") |
435 return ChangeInfo(changename, 0, 0, '', None) | 436 return ChangeInfo(changename, 0, 0, '', None, local_root) |
436 split_data = ReadFile(info_file).split(ChangeInfo._SEPARATOR, 2) | 437 split_data = ReadFile(info_file).split(ChangeInfo._SEPARATOR, 2) |
437 if len(split_data) != 3: | 438 if len(split_data) != 3: |
438 ErrorExit("Changelist file %s is corrupt" % info_file) | 439 ErrorExit("Changelist file %s is corrupt" % info_file) |
439 items = split_data[0].split(',') | 440 items = split_data[0].split(',') |
440 issue = 0 | 441 issue = 0 |
441 patchset = 0 | 442 patchset = 0 |
442 if items[0]: | 443 if items[0]: |
443 issue = int(items[0]) | 444 issue = int(items[0]) |
444 if len(items) > 1: | 445 if len(items) > 1: |
445 patchset = int(items[1]) | 446 patchset = int(items[1]) |
446 files = [] | 447 files = [] |
447 for line in split_data[1].splitlines(): | 448 for line in split_data[1].splitlines(): |
448 status = line[:7] | 449 status = line[:7] |
449 file = line[7:] | 450 file = line[7:] |
450 files.append((status, file)) | 451 files.append((status, file)) |
451 description = split_data[2] | 452 description = split_data[2] |
452 save = False | 453 save = False |
453 if update_status: | 454 if update_status: |
454 for file in files: | 455 for file in files: |
455 filename = os.path.join(GetRepositoryRoot(), file[1]) | 456 filename = os.path.join(local_root, file[1]) |
456 status_result = gclient.CaptureSVNStatus(filename) | 457 status_result = gclient.CaptureSVNStatus(filename) |
457 if not status_result or not status_result[0][0]: | 458 if not status_result or not status_result[0][0]: |
458 # File has been reverted. | 459 # File has been reverted. |
459 save = True | 460 save = True |
460 files.remove(file) | 461 files.remove(file) |
461 continue | 462 continue |
462 status = status_result[0][0] | 463 status = status_result[0][0] |
463 if status != file[0]: | 464 if status != file[0]: |
464 save = True | 465 save = True |
465 files[files.index(file)] = (status, file[1]) | 466 files[files.index(file)] = (status, file[1]) |
466 change_info = ChangeInfo(changename, issue, patchset, description, files) | 467 change_info = ChangeInfo(changename, issue, patchset, description, files, |
| 468 local_root) |
467 if save: | 469 if save: |
468 change_info.Save() | 470 change_info.Save() |
469 return change_info | 471 return change_info |
470 | 472 |
471 | 473 |
472 def GetChangelistInfoFile(changename): | 474 def GetChangelistInfoFile(changename): |
473 """Returns the file that stores information about a changelist.""" | 475 """Returns the file that stores information about a changelist.""" |
474 if not changename or re.search(r'[^\w-]', changename): | 476 if not changename or re.search(r'[^\w-]', changename): |
475 ErrorExit("Invalid changelist name: " + changename) | 477 ErrorExit("Invalid changelist name: " + changename) |
476 return os.path.join(GetChangesDir(), changename) | 478 return os.path.join(GetChangesDir(), changename) |
477 | 479 |
478 | 480 |
479 def LoadChangelistInfoForMultiple(changenames, fail_on_not_found=True, | 481 def LoadChangelistInfoForMultiple(changenames, local_root, fail_on_not_found, |
480 update_status=False): | 482 update_status): |
481 """Loads many changes and merge their files list into one pseudo change. | 483 """Loads many changes and merge their files list into one pseudo change. |
482 | 484 |
483 This is mainly usefull to concatenate many changes into one for a 'gcl try'. | 485 This is mainly usefull to concatenate many changes into one for a 'gcl try'. |
484 """ | 486 """ |
485 changes = changenames.split(',') | 487 changes = changenames.split(',') |
486 aggregate_change_info = ChangeInfo(changenames, 0, 0, '', None) | 488 aggregate_change_info = ChangeInfo(changenames, 0, 0, '', None, local_root) |
487 for change in changes: | 489 for change in changes: |
488 aggregate_change_info._files += ChangeInfo.Load(change, fail_on_not_found, | 490 aggregate_change_info._files += ChangeInfo.Load(change, |
| 491 local_root, |
| 492 fail_on_not_found, |
489 update_status).GetFiles() | 493 update_status).GetFiles() |
490 return aggregate_change_info | 494 return aggregate_change_info |
491 | 495 |
492 | 496 |
493 def GetCLs(): | 497 def GetCLs(): |
494 """Returns a list of all the changelists in this repository.""" | 498 """Returns a list of all the changelists in this repository.""" |
495 cls = os.listdir(GetChangesDir()) | 499 cls = os.listdir(GetChangesDir()) |
496 if CODEREVIEW_SETTINGS_FILE in cls: | 500 if CODEREVIEW_SETTINGS_FILE in cls: |
497 cls.remove(CODEREVIEW_SETTINGS_FILE) | 501 cls.remove(CODEREVIEW_SETTINGS_FILE) |
498 return cls | 502 return cls |
(...skipping 22 matching lines...) Expand all Loading... |
521 """ | 525 """ |
522 files = {} | 526 files = {} |
523 | 527 |
524 # Since the files are normalized to the root folder of the repositary, figure | 528 # Since the files are normalized to the root folder of the repositary, figure |
525 # out what we need to add to the paths. | 529 # out what we need to add to the paths. |
526 dir_prefix = os.getcwd()[len(GetRepositoryRoot()):].strip(os.sep) | 530 dir_prefix = os.getcwd()[len(GetRepositoryRoot()):].strip(os.sep) |
527 | 531 |
528 # Get a list of all files in changelists. | 532 # Get a list of all files in changelists. |
529 files_in_cl = {} | 533 files_in_cl = {} |
530 for cl in GetCLs(): | 534 for cl in GetCLs(): |
531 change_info = ChangeInfo.Load(cl) | 535 change_info = ChangeInfo.Load(cl, GetRepositoryRoot(), |
| 536 fail_on_not_found=True, update_status=False) |
532 for status, filename in change_info.GetFiles(): | 537 for status, filename in change_info.GetFiles(): |
533 files_in_cl[filename] = change_info.name | 538 files_in_cl[filename] = change_info.name |
534 | 539 |
535 # Get all the modified files. | 540 # Get all the modified files. |
536 status_result = gclient.CaptureSVNStatus(None) | 541 status_result = gclient.CaptureSVNStatus(None) |
537 for line in status_result: | 542 for line in status_result: |
538 status = line[0] | 543 status = line[0] |
539 filename = line[1] | 544 filename = line[1] |
540 if status[0] == "?": | 545 if status[0] == "?": |
541 continue | 546 continue |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
590 | 595 |
591 | 596 |
592 def Opened(): | 597 def Opened(): |
593 """Prints a list of modified files in the current directory down.""" | 598 """Prints a list of modified files in the current directory down.""" |
594 files = GetModifiedFiles() | 599 files = GetModifiedFiles() |
595 cl_keys = files.keys() | 600 cl_keys = files.keys() |
596 cl_keys.sort() | 601 cl_keys.sort() |
597 for cl_name in cl_keys: | 602 for cl_name in cl_keys: |
598 if cl_name: | 603 if cl_name: |
599 note = "" | 604 note = "" |
600 if len(ChangeInfo.Load(cl_name).GetFiles()) != len(files[cl_name]): | 605 change_info = ChangeInfo.Load(cl_name, GetRepositoryRoot(), |
| 606 fail_on_not_found=True, update_status=False) |
| 607 if len(change_info.GetFiles()) != len(files[cl_name]): |
601 note = " (Note: this changelist contains files outside this directory)" | 608 note = " (Note: this changelist contains files outside this directory)" |
602 print "\n--- Changelist " + cl_name + note + ":" | 609 print "\n--- Changelist " + cl_name + note + ":" |
603 for file in files[cl_name]: | 610 for file in files[cl_name]: |
604 print "".join(file) | 611 print "".join(file) |
605 | 612 |
606 | 613 |
607 def Help(argv=None): | 614 def Help(argv=None): |
608 if argv: | 615 if argv: |
609 if argv[0] == 'try': | 616 if argv[0] == 'try': |
610 TryChange(None, ['--help'], swallow_exception=False) | 617 TryChange(None, ['--help'], swallow_exception=False) |
(...skipping 454 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1065 default_presubmit=root_presubmit, | 1072 default_presubmit=root_presubmit, |
1066 may_prompt=may_prompt) | 1073 may_prompt=may_prompt) |
1067 if not result and may_prompt: | 1074 if not result and may_prompt: |
1068 print "\nPresubmit errors, can't continue (use --no_presubmit to bypass)" | 1075 print "\nPresubmit errors, can't continue (use --no_presubmit to bypass)" |
1069 return result | 1076 return result |
1070 | 1077 |
1071 | 1078 |
1072 def Changes(): | 1079 def Changes(): |
1073 """Print all the changelists and their files.""" | 1080 """Print all the changelists and their files.""" |
1074 for cl in GetCLs(): | 1081 for cl in GetCLs(): |
1075 change_info = ChangeInfo.Load(cl, True, True) | 1082 change_info = ChangeInfo.Load(cl, GetRepositoryRoot(), True, True) |
1076 print "\n--- Changelist " + change_info.name + ":" | 1083 print "\n--- Changelist " + change_info.name + ":" |
1077 for file in change_info.GetFiles(): | 1084 for file in change_info.GetFiles(): |
1078 print "".join(file) | 1085 print "".join(file) |
1079 | 1086 |
1080 | 1087 |
1081 def main(argv=None): | 1088 def main(argv=None): |
1082 if argv is None: | 1089 if argv is None: |
1083 argv = sys.argv | 1090 argv = sys.argv |
1084 | 1091 |
1085 if len(argv) == 1: | 1092 if len(argv) == 1: |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1139 else: | 1146 else: |
1140 ErrorExit("Need a changelist name.") | 1147 ErrorExit("Need a changelist name.") |
1141 else: | 1148 else: |
1142 changename = argv[2] | 1149 changename = argv[2] |
1143 | 1150 |
1144 # When the command is 'try' and --patchset is used, the patch to try | 1151 # When the command is 'try' and --patchset is used, the patch to try |
1145 # is on the Rietveld server. 'change' creates a change so it's fine if the | 1152 # is on the Rietveld server. 'change' creates a change so it's fine if the |
1146 # change didn't exist. All other commands require an existing change. | 1153 # change didn't exist. All other commands require an existing change. |
1147 fail_on_not_found = command != "try" and command != "change" | 1154 fail_on_not_found = command != "try" and command != "change" |
1148 if command == "try" and changename.find(',') != -1: | 1155 if command == "try" and changename.find(',') != -1: |
1149 change_info = LoadChangelistInfoForMultiple(changename, True, True) | 1156 change_info = LoadChangelistInfoForMultiple(changename, GetRepositoryRoot(), |
| 1157 True, True) |
1150 else: | 1158 else: |
1151 change_info = ChangeInfo.Load(changename, fail_on_not_found, True) | 1159 change_info = ChangeInfo.Load(changename, GetRepositoryRoot(), |
| 1160 fail_on_not_found, True) |
1152 | 1161 |
1153 if command == "change": | 1162 if command == "change": |
1154 if (len(argv) == 4): | 1163 if (len(argv) == 4): |
1155 filename = argv[3] | 1164 filename = argv[3] |
1156 f = open(filename, 'rU') | 1165 f = open(filename, 'rU') |
1157 override_description = f.read() | 1166 override_description = f.read() |
1158 f.close() | 1167 f.close() |
1159 else: | 1168 else: |
1160 override_description = None | 1169 override_description = None |
1161 Change(change_info, override_description) | 1170 Change(change_info, override_description) |
(...skipping 21 matching lines...) Expand all Loading... |
1183 # the files. This allows commands such as 'gcl diff xxx' to work. | 1192 # the files. This allows commands such as 'gcl diff xxx' to work. |
1184 args =["svn", command] | 1193 args =["svn", command] |
1185 root = GetRepositoryRoot() | 1194 root = GetRepositoryRoot() |
1186 args.extend([os.path.join(root, x) for x in change_info.GetFileNames()]) | 1195 args.extend([os.path.join(root, x) for x in change_info.GetFileNames()]) |
1187 RunShell(args, True) | 1196 RunShell(args, True) |
1188 return 0 | 1197 return 0 |
1189 | 1198 |
1190 | 1199 |
1191 if __name__ == "__main__": | 1200 if __name__ == "__main__": |
1192 sys.exit(main()) | 1201 sys.exit(main()) |
OLD | NEW |