Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(246)

Side by Side Diff: gcl.py

Issue 428001: Avoid losing CL description during Rietveld outage.... (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/depot_tools/
Patch Set: '' Created 11 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | tests/gcl_unittest.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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
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())
OLDNEW
« no previous file with comments | « no previous file | tests/gcl_unittest.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698