diff --git a/gerrit_util.py b/gerrit_util.py index 77fcb07c4..011606f65 100755 --- a/gerrit_util.py +++ b/gerrit_util.py @@ -545,6 +545,30 @@ def SubmitChange(host, change, wait_for_merge=True): return ReadHttpJsonResponse(conn, ignore_404=False) +def HasPendingChangeEdit(host, change): + conn = CreateHttpConn(host, 'changes/%s/edit' % change) + try: + ReadHttpResponse(conn, ignore_404=False) + except GerritError as e: + # On success, gerrit returns status 204; anything else is an error. + if e.http_status != 204: + raise + return False + else: + return True + + +def DeletePendingChangeEdit(host, change): + conn = CreateHttpConn(host, 'changes/%s/edit' % change, reqtype='DELETE') + try: + ReadHttpResponse(conn, ignore_404=False) + except GerritError as e: + # On success, gerrit returns status 204; if the edit was already deleted it + # returns 404. Anything else is an error. + if e.http_status not in (204, 404): + raise + + def SetCommitMessage(host, change, description): """Updates a commit message.""" # First, edit the commit message in a draft. diff --git a/git_cl.py b/git_cl.py index 07c528c36..c6d283c3c 100755 --- a/git_cl.py +++ b/git_cl.py @@ -1393,9 +1393,10 @@ class Changelist(object): author, upstream=upstream_branch) - def UpdateDescription(self, description): + def UpdateDescription(self, description, force=False): self.description = description - return self._codereview_impl.UpdateDescriptionRemote(description) + return self._codereview_impl.UpdateDescriptionRemote( + description, force=force) def RunHook(self, committing, may_prompt, verbose, change): """Calls sys.exit() if the hook fails; returns a HookResults otherwise.""" @@ -1603,7 +1604,7 @@ class _ChangelistCodereviewBase(object): # None is valid return value, otherwise presubmit_support.GerritAccessor. return None - def UpdateDescriptionRemote(self, description): + def UpdateDescriptionRemote(self, description, force=False): """Update the description on codereview site.""" raise NotImplementedError() @@ -1798,7 +1799,7 @@ class _RietveldChangelistImpl(_ChangelistCodereviewBase): return 'reply' return 'waiting' - def UpdateDescriptionRemote(self, description): + def UpdateDescriptionRemote(self, description, force=False): return self.RpcServer().update_description( self.GetIssue(), self.description) @@ -2294,7 +2295,17 @@ class _GerritChangelistImpl(_ChangelistCodereviewBase): url = data['revisions'][current_rev]['fetch']['http']['url'] return gerrit_util.GetChangeDescriptionFromGitiles(url, current_rev) - def UpdateDescriptionRemote(self, description): + def UpdateDescriptionRemote(self, description, force=False): + if gerrit_util.HasPendingChangeEdit(self._GetGerritHost(), self.GetIssue()): + if not force: + ask_for_data( + 'The description cannot be modified while the issue has a pending ' + 'unpublished edit. Either publish the edit in the Gerrit web UI ' + 'or delete it.\n\n' + 'Press Enter to delete the unpublished edit, Ctrl+C to abort.') + + gerrit_util.DeletePendingChangeEdit(self._GetGerritHost(), + self.GetIssue()) gerrit_util.SetCommitMessage(self._GetGerritHost(), self.GetIssue(), description) @@ -3607,6 +3618,9 @@ def CMDdescription(parser, args): parser.add_option('-n', '--new-description', help='New description to set for this issue (- for stdin, ' '+ to load from local commit HEAD)') + parser.add_option('-f', '--force', action='store_true', + help='Delete any unpublished Gerrit edits for this issue ' + 'without prompting') _add_codereview_select_options(parser) auth.add_auth_options(parser) @@ -3655,7 +3669,7 @@ def CMDdescription(parser, args): description.prompt() if cl.GetDescription() != description.description: - cl.UpdateDescription(description.description) + cl.UpdateDescription(description.description, force=options.force) return 0 diff --git a/tests/git_cl_test.py b/tests/git_cl_test.py index 428f48796..1bb517e38 100755 --- a/tests/git_cl_test.py +++ b/tests/git_cl_test.py @@ -38,7 +38,7 @@ class ChangelistMock(object): return 1 def GetDescription(self): return ChangelistMock.desc - def UpdateDescription(self, desc): + def UpdateDescription(self, desc, force=False): ChangelistMock.desc = desc @@ -1664,7 +1664,7 @@ class TestGitCl(TestCase): # Simulate user changing something. return 'Some.\n\nBUG=123\n\nChange-Id: xxx' - def UpdateDescriptionRemote(_, desc): + def UpdateDescriptionRemote(_, desc, force=False): self.assertEquals(desc, 'Some.\n\nBUG=123\n\nChange-Id: xxx') self.mock(git_cl.sys, 'stdout', StringIO.StringIO())