| 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 |