Commit ea452b3f authored by chase@chromium.org's avatar chase@chromium.org

Avoid losing CL description during Rietveld outage.

Save the CL locally prior to attempting to submit
to Rietveld.  The CL is saved locally with a dirty-bit
before the upload is attempted.  If successful, the CL
is saved locally again with a clean-bit.

On loading a dirty CL for editing, we only load the
CL description from Rietveld if the local CL is clean
(there are no pending changes to upload).  Clean CLs
continue to retrieve updated descriptions directly
from Rietveld.

BUG=none
TEST=gcl change <name> saves CL description local
prior to uploading.  gcl change <name> after a failed
Rietveld upload uses local CL description instead of
using the associated issue's description from Rietveld.
Changes in this state are reset to the 'clean' state
after the Rietveld update completes.
Review URL: http://codereview.chromium.org/428001

git-svn-id: svn://svn.chromium.org/chrome/trunk/tools/depot_tools@32791 0039d316-1c4b-4281-b951-d872f2087c98
parent 720d9f3c
...@@ -270,7 +270,8 @@ class ChangeInfo(object): ...@@ -270,7 +270,8 @@ class ChangeInfo(object):
# _SEPARATOR\n # _SEPARATOR\n
# description # description
def __init__(self, name, issue, patchset, description, files, local_root): def __init__(self, name, issue, patchset, description, files, local_root,
needs_upload=False):
self.name = name self.name = name
self.issue = int(issue) self.issue = int(issue)
self.patchset = int(patchset) self.patchset = int(patchset)
...@@ -280,6 +281,10 @@ class ChangeInfo(object): ...@@ -280,6 +281,10 @@ class ChangeInfo(object):
self._files = files self._files = files
self.patch = None self.patch = None
self._local_root = local_root self._local_root = local_root
self.needs_upload = needs_upload
def NeedsUpload(self):
return self.needs_upload
def GetFileNames(self): def GetFileNames(self):
"""Returns the list of file names included in this change.""" """Returns the list of file names included in this change."""
...@@ -308,8 +313,12 @@ class ChangeInfo(object): ...@@ -308,8 +313,12 @@ class ChangeInfo(object):
def Save(self): def Save(self):
"""Writes the changelist information to disk.""" """Writes the changelist information to disk."""
if self.NeedsUpload():
needs_upload = "dirty"
else:
needs_upload = "clean"
data = ChangeInfo._SEPARATOR.join([ data = ChangeInfo._SEPARATOR.join([
"%d, %d" % (self.issue, self.patchset), "%d, %d, %s" % (self.issue, self.patchset, needs_upload),
"\n".join([f[0] + f[1] for f in self.GetFiles()]), "\n".join([f[0] + f[1] for f in self.GetFiles()]),
self.description]) self.description])
WriteFile(GetChangelistInfoFile(self.name), data) WriteFile(GetChangelistInfoFile(self.name), data)
...@@ -427,17 +436,21 @@ class ChangeInfo(object): ...@@ -427,17 +436,21 @@ class ChangeInfo(object):
if not os.path.exists(info_file): if not os.path.exists(info_file):
if fail_on_not_found: if fail_on_not_found:
ErrorExit("Changelist " + changename + " not found.") ErrorExit("Changelist " + changename + " not found.")
return ChangeInfo(changename, 0, 0, '', None, local_root) return ChangeInfo(changename, 0, 0, '', None, local_root,
needs_upload=False)
split_data = ReadFile(info_file).split(ChangeInfo._SEPARATOR, 2) split_data = ReadFile(info_file).split(ChangeInfo._SEPARATOR, 2)
if len(split_data) != 3: if len(split_data) != 3:
ErrorExit("Changelist file %s is corrupt" % info_file) ErrorExit("Changelist file %s is corrupt" % info_file)
items = split_data[0].split(',') items = split_data[0].split(', ')
issue = 0 issue = 0
patchset = 0 patchset = 0
needs_upload = False
if items[0]: if items[0]:
issue = int(items[0]) issue = int(items[0])
if len(items) > 1: if len(items) > 1:
patchset = int(items[1]) patchset = int(items[1])
if len(items) > 2:
needs_upload = (items[2] == "dirty")
files = [] files = []
for line in split_data[1].splitlines(): for line in split_data[1].splitlines():
status = line[:7] status = line[:7]
...@@ -459,7 +472,7 @@ class ChangeInfo(object): ...@@ -459,7 +472,7 @@ class ChangeInfo(object):
save = True save = True
files[files.index(item)] = (status, item[1]) files[files.index(item)] = (status, item[1])
change_info = ChangeInfo(changename, issue, patchset, description, files, change_info = ChangeInfo(changename, issue, patchset, description, files,
local_root) local_root, needs_upload)
if save: if save:
change_info.Save() change_info.Save()
return change_info return change_info
...@@ -479,7 +492,8 @@ def LoadChangelistInfoForMultiple(changenames, local_root, fail_on_not_found, ...@@ -479,7 +492,8 @@ def LoadChangelistInfoForMultiple(changenames, local_root, fail_on_not_found,
This is mainly usefull to concatenate many changes into one for a 'gcl try'. This is mainly usefull to concatenate many changes into one for a 'gcl try'.
""" """
changes = changenames.split(',') changes = changenames.split(',')
aggregate_change_info = ChangeInfo(changenames, 0, 0, '', None, local_root) aggregate_change_info = ChangeInfo(changenames, 0, 0, '', None, local_root,
needs_upload=False)
for change in changes: for change in changes:
aggregate_change_info._files += ChangeInfo.Load(change, aggregate_change_info._files += ChangeInfo.Load(change,
local_root, local_root,
...@@ -979,7 +993,7 @@ def Change(change_info, args): ...@@ -979,7 +993,7 @@ def Change(change_info, args):
else: else:
override_description = None override_description = None
if change_info.issue: if change_info.issue and not change_info.NeedsUpload():
try: try:
description = GetIssueDescription(change_info.issue) description = GetIssueDescription(change_info.issue)
except urllib2.HTTPError, err: except urllib2.HTTPError, err:
...@@ -1036,13 +1050,12 @@ def Change(change_info, args): ...@@ -1036,13 +1050,12 @@ def Change(change_info, args):
if len(split_result) != 2: if len(split_result) != 2:
ErrorExit("Don't modify the text starting with ---!\n\n" + result) ErrorExit("Don't modify the text starting with ---!\n\n" + result)
# Update the CL description if it has changed.
new_description = split_result[0] new_description = split_result[0]
cl_files_text = split_result[1] cl_files_text = split_result[1]
if new_description != description or override_description: if new_description != description or override_description:
change_info.description = new_description change_info.description = new_description
if change_info.issue: change_info.needs_upload = True
# Update the Rietveld issue with the new description.
change_info.UpdateRietveldDescription()
new_cl_files = [] new_cl_files = []
for line in cl_files_text.splitlines(): for line in cl_files_text.splitlines():
...@@ -1068,6 +1081,13 @@ def Change(change_info, args): ...@@ -1068,6 +1081,13 @@ def Change(change_info, args):
if change_info.MissingTests(): if change_info.MissingTests():
Warn("WARNING: " + MISSING_TEST_MSG) Warn("WARNING: " + MISSING_TEST_MSG)
# Update the Rietveld issue.
if change_info.issue and change_info.NeedsUpload():
change_info.UpdateRietveldDescription()
change_info.needs_upload = False
change_info.Save()
# Valid extensions for files we want to lint. # Valid extensions for files we want to lint.
DEFAULT_LINT_REGEX = r"(.*\.cpp|.*\.cc|.*\.h)" DEFAULT_LINT_REGEX = r"(.*\.cpp|.*\.cc|.*\.h)"
DEFAULT_LINT_IGNORE_REGEX = r"" DEFAULT_LINT_IGNORE_REGEX = r""
......
...@@ -160,9 +160,9 @@ class ChangeInfoUnittest(GclTestsBase): ...@@ -160,9 +160,9 @@ class ChangeInfoUnittest(GclTestsBase):
self.mox.ReplayAll() self.mox.ReplayAll()
members = [ members = [
'CloseIssue', 'Delete', 'GetFiles', 'GetFileNames', 'GetLocalRoot', 'CloseIssue', 'Delete', 'GetFiles', 'GetFileNames', 'GetLocalRoot',
'Exists', 'Load', 'MissingTests', 'Save', 'UpdateRietveldDescription', 'Exists', 'Load', 'MissingTests', 'NeedsUpload', 'Save',
'description', 'issue', 'name', 'UpdateRietveldDescription', 'description', 'issue', 'name',
'patch', 'patchset', 'needs_upload', 'patch', 'patchset',
] ]
# If this test fails, you should add the relevant test. # If this test fails, you should add the relevant test.
self.compareMembers(gcl.ChangeInfo('', 0, 0, '', None, self.fake_root_dir), self.compareMembers(gcl.ChangeInfo('', 0, 0, '', None, self.fake_root_dir),
...@@ -187,7 +187,7 @@ class ChangeInfoUnittest(GclTestsBase): ...@@ -187,7 +187,7 @@ class ChangeInfoUnittest(GclTestsBase):
gcl.GetChangelistInfoFile('bleh').AndReturn('bleeeh') gcl.GetChangelistInfoFile('bleh').AndReturn('bleeeh')
gcl.os.path.exists('bleeeh').AndReturn(True) gcl.os.path.exists('bleeeh').AndReturn(True)
gcl.ReadFile('bleeeh').AndReturn( gcl.ReadFile('bleeeh').AndReturn(
gcl.ChangeInfo._SEPARATOR.join(["42,53", "G b.cc"] + description)) gcl.ChangeInfo._SEPARATOR.join(["42, 53", "G b.cc"] + description))
self.mox.ReplayAll() self.mox.ReplayAll()
change_info = gcl.ChangeInfo.Load('bleh', self.fake_root_dir, True, False) change_info = gcl.ChangeInfo.Load('bleh', self.fake_root_dir, True, False)
...@@ -214,12 +214,23 @@ class ChangeInfoUnittest(GclTestsBase): ...@@ -214,12 +214,23 @@ class ChangeInfoUnittest(GclTestsBase):
def testSaveEmpty(self): def testSaveEmpty(self):
gcl.GetChangelistInfoFile('').AndReturn('foo') gcl.GetChangelistInfoFile('').AndReturn('foo')
gcl.WriteFile('foo', gcl.ChangeInfo._SEPARATOR.join(['0, 0', '', ''])) gcl.WriteFile('foo', gcl.ChangeInfo._SEPARATOR.join(['0, 0, clean', '',
'']))
self.mox.ReplayAll() self.mox.ReplayAll()
change_info = gcl.ChangeInfo('', 0, 0, '', None, self.fake_root_dir) change_info = gcl.ChangeInfo('', 0, 0, '', None, self.fake_root_dir)
change_info.Save() change_info.Save()
def testSaveDirty(self):
gcl.GetChangelistInfoFile('').AndReturn('foo')
gcl.WriteFile('foo', gcl.ChangeInfo._SEPARATOR.join(['0, 0, dirty', '',
'']))
self.mox.ReplayAll()
change_info = gcl.ChangeInfo('', 0, 0, '', None, self.fake_root_dir,
needs_upload=True)
change_info.Save()
class UploadCLUnittest(GclTestsBase): class UploadCLUnittest(GclTestsBase):
def setUp(self): def setUp(self):
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment