Chromium Code Reviews| 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 252 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 263 # issue_id, patchset\n (, patchset is optional) | 263 # issue_id, patchset\n (, patchset is optional) |
| 264 # _SEPARATOR\n | 264 # _SEPARATOR\n |
| 265 # filepath1\n | 265 # filepath1\n |
| 266 # filepath2\n | 266 # filepath2\n |
| 267 # . | 267 # . |
| 268 # . | 268 # . |
| 269 # filepathn\n | 269 # filepathn\n |
| 270 # _SEPARATOR\n | 270 # _SEPARATOR\n |
| 271 # description | 271 # description |
| 272 | 272 |
| 273 def __init__(self, name, issue, patchset, description, files, local_root): | 273 def __init__(self, name, issue, patchset, description, files, local_root, |
| 274 needs_upload=False): | |
| 274 self.name = name | 275 self.name = name |
| 275 self.issue = int(issue) | 276 self.issue = int(issue) |
| 276 self.patchset = int(patchset) | 277 self.patchset = int(patchset) |
| 277 self.description = description | 278 self.description = description |
| 278 if files is None: | 279 if files is None: |
| 279 files = [] | 280 files = [] |
| 280 self._files = files | 281 self._files = files |
| 281 self.patch = None | 282 self.patch = None |
| 282 self._local_root = local_root | 283 self._local_root = local_root |
| 284 self.needs_upload = needs_upload | |
| 285 | |
| 286 def NeedsUpload(self): | |
| 287 return self.needs_upload | |
| 283 | 288 |
| 284 def GetFileNames(self): | 289 def GetFileNames(self): |
| 285 """Returns the list of file names included in this change.""" | 290 """Returns the list of file names included in this change.""" |
| 286 return [f[1] for f in self._files] | 291 return [f[1] for f in self._files] |
| 287 | 292 |
| 288 def GetFiles(self): | 293 def GetFiles(self): |
| 289 """Returns the list of files included in this change with their status.""" | 294 """Returns the list of files included in this change with their status.""" |
| 290 return self._files | 295 return self._files |
| 291 | 296 |
| 292 def GetLocalRoot(self): | 297 def GetLocalRoot(self): |
| 293 """Returns the local repository checkout root directory.""" | 298 """Returns the local repository checkout root directory.""" |
| 294 return self._local_root | 299 return self._local_root |
| 295 | 300 |
| 296 def Exists(self): | 301 def Exists(self): |
| 297 """Returns True if this change already exists (i.e., is not new).""" | 302 """Returns True if this change already exists (i.e., is not new).""" |
| 298 return (self.issue or self.description or self._files) | 303 return (self.issue or self.description or self._files) |
| 299 | 304 |
| 300 def _NonDeletedFileList(self): | 305 def _NonDeletedFileList(self): |
| 301 """Returns a list of files in this change, not including deleted files.""" | 306 """Returns a list of files in this change, not including deleted files.""" |
| 302 return [f[1] for f in self.GetFiles() | 307 return [f[1] for f in self.GetFiles() |
| 303 if not f[0].startswith("D")] | 308 if not f[0].startswith("D")] |
| 304 | 309 |
| 305 def _AddedFileList(self): | 310 def _AddedFileList(self): |
| 306 """Returns a list of files added in this change.""" | 311 """Returns a list of files added in this change.""" |
| 307 return [f[1] for f in self.GetFiles() if f[0].startswith("A")] | 312 return [f[1] for f in self.GetFiles() if f[0].startswith("A")] |
| 308 | 313 |
| 309 def Save(self): | 314 def Save(self): |
| 310 """Writes the changelist information to disk.""" | 315 """Writes the changelist information to disk.""" |
| 316 if self.NeedsUpload(): | |
| 317 needs_upload = "dirty" | |
| 318 else: | |
| 319 needs_upload = "clean" | |
| 311 data = ChangeInfo._SEPARATOR.join([ | 320 data = ChangeInfo._SEPARATOR.join([ |
|
M-A Ruel
2009/11/22 18:06:10
We should have used json or pickle right at the st
| |
| 312 "%d, %d" % (self.issue, self.patchset), | 321 "%d, %d, %s" % (self.issue, self.patchset, needs_upload), |
| 313 "\n".join([f[0] + f[1] for f in self.GetFiles()]), | 322 "\n".join([f[0] + f[1] for f in self.GetFiles()]), |
| 314 self.description]) | 323 self.description]) |
| 315 WriteFile(GetChangelistInfoFile(self.name), data) | 324 WriteFile(GetChangelistInfoFile(self.name), data) |
| 316 | 325 |
| 317 def Delete(self): | 326 def Delete(self): |
| 318 """Removes the changelist information from disk.""" | 327 """Removes the changelist information from disk.""" |
| 319 os.remove(GetChangelistInfoFile(self.name)) | 328 os.remove(GetChangelistInfoFile(self.name)) |
| 320 | 329 |
| 321 def CloseIssue(self): | 330 def CloseIssue(self): |
| 322 """Closes the Rietveld issue for this changelist.""" | 331 """Closes the Rietveld issue for this changelist.""" |
| (...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 420 changelist doesn't exist. | 429 changelist doesn't exist. |
| 421 update_status: if True, the svn status will be updated for all the files | 430 update_status: if True, the svn status will be updated for all the files |
| 422 and unchanged files will be removed. | 431 and unchanged files will be removed. |
| 423 | 432 |
| 424 Returns: a ChangeInfo object. | 433 Returns: a ChangeInfo object. |
| 425 """ | 434 """ |
| 426 info_file = GetChangelistInfoFile(changename) | 435 info_file = GetChangelistInfoFile(changename) |
| 427 if not os.path.exists(info_file): | 436 if not os.path.exists(info_file): |
| 428 if fail_on_not_found: | 437 if fail_on_not_found: |
| 429 ErrorExit("Changelist " + changename + " not found.") | 438 ErrorExit("Changelist " + changename + " not found.") |
| 430 return ChangeInfo(changename, 0, 0, '', None, local_root) | 439 return ChangeInfo(changename, 0, 0, '', None, local_root, |
| 440 needs_upload=False) | |
| 431 split_data = ReadFile(info_file).split(ChangeInfo._SEPARATOR, 2) | 441 split_data = ReadFile(info_file).split(ChangeInfo._SEPARATOR, 2) |
| 432 if len(split_data) != 3: | 442 if len(split_data) != 3: |
| 433 ErrorExit("Changelist file %s is corrupt" % info_file) | 443 ErrorExit("Changelist file %s is corrupt" % info_file) |
| 434 items = split_data[0].split(',') | 444 items = split_data[0].split(', ') |
| 435 issue = 0 | 445 issue = 0 |
| 436 patchset = 0 | 446 patchset = 0 |
| 447 needs_upload = False | |
| 437 if items[0]: | 448 if items[0]: |
| 438 issue = int(items[0]) | 449 issue = int(items[0]) |
| 439 if len(items) > 1: | 450 if len(items) > 1: |
| 440 patchset = int(items[1]) | 451 patchset = int(items[1]) |
| 452 if len(items) > 2: | |
| 453 needs_upload = (items[2] == "dirty") | |
| 441 files = [] | 454 files = [] |
| 442 for line in split_data[1].splitlines(): | 455 for line in split_data[1].splitlines(): |
| 443 status = line[:7] | 456 status = line[:7] |
| 444 filename = line[7:] | 457 filename = line[7:] |
| 445 files.append((status, filename)) | 458 files.append((status, filename)) |
| 446 description = split_data[2] | 459 description = split_data[2] |
| 447 save = False | 460 save = False |
| 448 if update_status: | 461 if update_status: |
| 449 for item in files: | 462 for item in files: |
| 450 filename = os.path.join(local_root, item[1]) | 463 filename = os.path.join(local_root, item[1]) |
| 451 status_result = SVN.CaptureStatus(filename) | 464 status_result = SVN.CaptureStatus(filename) |
| 452 if not status_result or not status_result[0][0]: | 465 if not status_result or not status_result[0][0]: |
| 453 # File has been reverted. | 466 # File has been reverted. |
| 454 save = True | 467 save = True |
| 455 files.remove(item) | 468 files.remove(item) |
| 456 continue | 469 continue |
| 457 status = status_result[0][0] | 470 status = status_result[0][0] |
| 458 if status != item[0]: | 471 if status != item[0]: |
| 459 save = True | 472 save = True |
| 460 files[files.index(item)] = (status, item[1]) | 473 files[files.index(item)] = (status, item[1]) |
| 461 change_info = ChangeInfo(changename, issue, patchset, description, files, | 474 change_info = ChangeInfo(changename, issue, patchset, description, files, |
| 462 local_root) | 475 local_root, needs_upload) |
| 463 if save: | 476 if save: |
| 464 change_info.Save() | 477 change_info.Save() |
| 465 return change_info | 478 return change_info |
| 466 | 479 |
| 467 | 480 |
| 468 def GetChangelistInfoFile(changename): | 481 def GetChangelistInfoFile(changename): |
| 469 """Returns the file that stores information about a changelist.""" | 482 """Returns the file that stores information about a changelist.""" |
| 470 if not changename or re.search(r'[^\w-]', changename): | 483 if not changename or re.search(r'[^\w-]', changename): |
| 471 ErrorExit("Invalid changelist name: " + changename) | 484 ErrorExit("Invalid changelist name: " + changename) |
| 472 return os.path.join(GetChangesDir(), changename) | 485 return os.path.join(GetChangesDir(), changename) |
| 473 | 486 |
| 474 | 487 |
| 475 def LoadChangelistInfoForMultiple(changenames, local_root, fail_on_not_found, | 488 def LoadChangelistInfoForMultiple(changenames, local_root, fail_on_not_found, |
| 476 update_status): | 489 update_status): |
| 477 """Loads many changes and merge their files list into one pseudo change. | 490 """Loads many changes and merge their files list into one pseudo change. |
| 478 | 491 |
| 479 This is mainly usefull to concatenate many changes into one for a 'gcl try'. | 492 This is mainly usefull to concatenate many changes into one for a 'gcl try'. |
| 480 """ | 493 """ |
| 481 changes = changenames.split(',') | 494 changes = changenames.split(',') |
| 482 aggregate_change_info = ChangeInfo(changenames, 0, 0, '', None, local_root) | 495 aggregate_change_info = ChangeInfo(changenames, 0, 0, '', None, local_root, |
| 496 needs_upload=False) | |
| 483 for change in changes: | 497 for change in changes: |
| 484 aggregate_change_info._files += ChangeInfo.Load(change, | 498 aggregate_change_info._files += ChangeInfo.Load(change, |
| 485 local_root, | 499 local_root, |
| 486 fail_on_not_found, | 500 fail_on_not_found, |
| 487 update_status).GetFiles() | 501 update_status).GetFiles() |
| 488 return aggregate_change_info | 502 return aggregate_change_info |
| 489 | 503 |
| 490 | 504 |
| 491 def GetCLs(): | 505 def GetCLs(): |
| 492 """Returns a list of all the changelists in this repository.""" | 506 """Returns a list of all the changelists in this repository.""" |
| (...skipping 479 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 972 "directory.") | 986 "directory.") |
| 973 | 987 |
| 974 if (len(args) == 1): | 988 if (len(args) == 1): |
| 975 filename = args[0] | 989 filename = args[0] |
| 976 f = open(filename, 'rU') | 990 f = open(filename, 'rU') |
| 977 override_description = f.read() | 991 override_description = f.read() |
| 978 f.close() | 992 f.close() |
| 979 else: | 993 else: |
| 980 override_description = None | 994 override_description = None |
| 981 | 995 |
| 982 if change_info.issue: | 996 if change_info.issue and not change_info.NeedsUpload(): |
| 983 try: | 997 try: |
| 984 description = GetIssueDescription(change_info.issue) | 998 description = GetIssueDescription(change_info.issue) |
| 985 except urllib2.HTTPError, err: | 999 except urllib2.HTTPError, err: |
| 986 if err.code == 404: | 1000 if err.code == 404: |
| 987 # The user deleted the issue in Rietveld, so forget the old issue id. | 1001 # The user deleted the issue in Rietveld, so forget the old issue id. |
| 988 description = change_info.description | 1002 description = change_info.description |
| 989 change_info.issue = 0 | 1003 change_info.issue = 0 |
| 990 change_info.Save() | 1004 change_info.Save() |
| 991 else: | 1005 else: |
| 992 ErrorExit("Error getting the description from Rietveld: " + err) | 1006 ErrorExit("Error getting the description from Rietveld: " + err) |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1029 result = ReadFile(filename) | 1043 result = ReadFile(filename) |
| 1030 os.remove(filename) | 1044 os.remove(filename) |
| 1031 | 1045 |
| 1032 if not result: | 1046 if not result: |
| 1033 return | 1047 return |
| 1034 | 1048 |
| 1035 split_result = result.split(separator1, 1) | 1049 split_result = result.split(separator1, 1) |
| 1036 if len(split_result) != 2: | 1050 if len(split_result) != 2: |
| 1037 ErrorExit("Don't modify the text starting with ---!\n\n" + result) | 1051 ErrorExit("Don't modify the text starting with ---!\n\n" + result) |
| 1038 | 1052 |
| 1053 # Update the CL description if it has changed. | |
| 1039 new_description = split_result[0] | 1054 new_description = split_result[0] |
| 1040 cl_files_text = split_result[1] | 1055 cl_files_text = split_result[1] |
| 1041 if new_description != description or override_description: | 1056 if new_description != description or override_description: |
| 1042 change_info.description = new_description | 1057 change_info.description = new_description |
| 1043 if change_info.issue: | 1058 change_info.needs_upload = True |
| 1044 # Update the Rietveld issue with the new description. | |
| 1045 change_info.UpdateRietveldDescription() | |
| 1046 | 1059 |
| 1047 new_cl_files = [] | 1060 new_cl_files = [] |
| 1048 for line in cl_files_text.splitlines(): | 1061 for line in cl_files_text.splitlines(): |
| 1049 if not len(line): | 1062 if not len(line): |
| 1050 continue | 1063 continue |
| 1051 if line.startswith("---"): | 1064 if line.startswith("---"): |
| 1052 break | 1065 break |
| 1053 status = line[:7] | 1066 status = line[:7] |
| 1054 filename = line[7:] | 1067 filename = line[7:] |
| 1055 new_cl_files.append((status, filename)) | 1068 new_cl_files.append((status, filename)) |
| 1056 | 1069 |
| 1057 if (not len(change_info._files)) and (not change_info.issue) and \ | 1070 if (not len(change_info._files)) and (not change_info.issue) and \ |
| 1058 (not len(new_description) and (not new_cl_files)): | 1071 (not len(new_description) and (not new_cl_files)): |
| 1059 ErrorExit("Empty changelist not saved") | 1072 ErrorExit("Empty changelist not saved") |
| 1060 | 1073 |
| 1061 change_info._files = new_cl_files | 1074 change_info._files = new_cl_files |
| 1062 change_info.Save() | 1075 change_info.Save() |
| 1063 if svn_info.get('URL', '').startswith('http:'): | 1076 if svn_info.get('URL', '').startswith('http:'): |
| 1064 Warn("WARNING: Creating CL in a read-only checkout. You will not be " | 1077 Warn("WARNING: Creating CL in a read-only checkout. You will not be " |
| 1065 "able to commit it!") | 1078 "able to commit it!") |
| 1066 | 1079 |
| 1067 print change_info.name + " changelist saved." | 1080 print change_info.name + " changelist saved." |
| 1068 if change_info.MissingTests(): | 1081 if change_info.MissingTests(): |
| 1069 Warn("WARNING: " + MISSING_TEST_MSG) | 1082 Warn("WARNING: " + MISSING_TEST_MSG) |
| 1070 | 1083 |
| 1084 # Update the Rietveld issue. | |
| 1085 if change_info.issue and change_info.NeedsUpload(): | |
| 1086 change_info.UpdateRietveldDescription() | |
| 1087 change_info.needs_upload = False | |
| 1088 change_info.Save() | |
| 1089 | |
| 1090 | |
| 1071 # Valid extensions for files we want to lint. | 1091 # Valid extensions for files we want to lint. |
| 1072 DEFAULT_LINT_REGEX = r"(.*\.cpp|.*\.cc|.*\.h)" | 1092 DEFAULT_LINT_REGEX = r"(.*\.cpp|.*\.cc|.*\.h)" |
| 1073 DEFAULT_LINT_IGNORE_REGEX = r"" | 1093 DEFAULT_LINT_IGNORE_REGEX = r"" |
| 1074 | 1094 |
| 1075 def Lint(change_info, args): | 1095 def Lint(change_info, args): |
| 1076 """Runs cpplint.py on all the files in |change_info|""" | 1096 """Runs cpplint.py on all the files in |change_info|""" |
| 1077 try: | 1097 try: |
| 1078 import cpplint | 1098 import cpplint |
| 1079 except ImportError: | 1099 except ImportError: |
| 1080 ErrorExit("You need to install cpplint.py to lint C++ files.") | 1100 ErrorExit("You need to install cpplint.py to lint C++ files.") |
| (...skipping 183 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1264 return 0 | 1284 return 0 |
| 1265 args =["svn", command] | 1285 args =["svn", command] |
| 1266 root = GetRepositoryRoot() | 1286 root = GetRepositoryRoot() |
| 1267 args.extend([os.path.join(root, x) for x in change_info.GetFileNames()]) | 1287 args.extend([os.path.join(root, x) for x in change_info.GetFileNames()]) |
| 1268 RunShell(args, True) | 1288 RunShell(args, True) |
| 1269 return 0 | 1289 return 0 |
| 1270 | 1290 |
| 1271 | 1291 |
| 1272 if __name__ == "__main__": | 1292 if __name__ == "__main__": |
| 1273 sys.exit(main()) | 1293 sys.exit(main()) |
| OLD | NEW |