Index: git_cl.py |
diff --git a/git_cl.py b/git_cl.py |
index f1a450973db18f773e76f21c55c722b5edcb0a63..1ffbb308cba36b84bcc5c418a3e2b40ed295913f 100755 |
--- a/git_cl.py |
+++ b/git_cl.py |
@@ -881,17 +881,17 @@ class Changelist(object): |
self._remote = None |
self._codereview_impl = None |
+ self._codereview = None |
self._load_codereview_impl(codereview, **kwargs) |
+ assert self._codereview_impl |
+ assert self._codereview in _CODEREVIEW_IMPLEMENTATIONS |
def _load_codereview_impl(self, codereview=None, **kwargs): |
if codereview: |
- codereview = codereview.lower() |
- if codereview == 'gerrit': |
- self._codereview_impl = _GerritChangelistImpl(self, **kwargs) |
- elif codereview == 'rietveld': |
- self._codereview_impl = _RietveldChangelistImpl(self, **kwargs) |
- else: |
- assert codereview in ('rietveld', 'gerrit') |
+ assert codereview in _CODEREVIEW_IMPLEMENTATIONS |
+ cls = _CODEREVIEW_IMPLEMENTATIONS[codereview] |
+ self._codereview = codereview |
+ self._codereview_impl = cls(self, **kwargs) |
return |
# Automatic selection based on issue number set for a current branch. |
@@ -899,10 +899,11 @@ class Changelist(object): |
assert not self.issue |
# Whether we find issue or not, we are doing the lookup. |
self.lookedup_issue = True |
- for cls in [_RietveldChangelistImpl, _GerritChangelistImpl]: |
+ for codereview, cls in _CODEREVIEW_IMPLEMENTATIONS.iteritems(): |
setting = cls.IssueSetting(self.GetBranch()) |
issue = RunGit(['config', setting], error_ok=True).strip() |
if issue: |
+ self._codereview = codereview |
self._codereview_impl = cls(self, **kwargs) |
self.issue = int(issue) |
return |
@@ -912,6 +913,8 @@ class Changelist(object): |
codereview='gerrit' if settings.GetIsGerrit() else 'rietveld', |
**kwargs) |
+ def IsGerrit(self): |
+ return self._codereview == 'gerrit' |
def GetCCList(self): |
"""Return the users cc'd on this CL. |
@@ -1650,11 +1653,55 @@ class _GerritChangelistImpl(_ChangelistCodereviewBase): |
def CloseIssue(self): |
gerrit_util.AbandonChange(self._GetGerritHost(), self.GetIssue(), msg='') |
+ def SubmitIssue(self, wait_for_merge=True): |
+ gerrit_util.SubmitChange(self._GetGerritHost(), self.GetIssue(), |
+ wait_for_merge=wait_for_merge) |
def _GetChangeDetail(self, options): |
return gerrit_util.GetChangeDetail(self._GetGerritHost(), self.GetIssue(), |
options) |
+ def CMDLand(self, force, bypass_hooks, verbose): |
+ if git_common.is_dirty_git_tree('land'): |
+ return 1 |
+ differs = True |
+ last_upload = RunGit(['config', |
+ 'branch.%s.gerritsquashhash' % self.GetBranch()], |
+ error_ok=True).strip() |
+ # Note: git diff outputs nothing if there is no diff. |
+ if not last_upload or RunGit(['diff', last_upload]).strip(): |
+ print('WARNING: some changes from local branch haven\'t been uploaded') |
+ else: |
+ detail = self._GetChangeDetail(['CURRENT_REVISION']) |
+ if detail['current_revision'] == last_upload: |
+ differs = False |
+ else: |
+ print('WARNING: local branch contents differ from latest uploaded ' |
+ 'patchset') |
+ if differs: |
+ if not force: |
+ ask_for_data( |
+ 'Do you want to submit latest Gerrit patchset and bypass hooks?') |
+ print('WARNING: bypassing hooks and submitting latest uploaded patchset') |
+ elif not bypass_hooks: |
+ hook_results = self.RunHook( |
+ committing=True, |
+ may_prompt=not force, |
+ verbose=verbose, |
+ change=self.GetChange(self.GetCommonAncestorWithUpstream(), None)) |
+ if not hook_results.should_continue(): |
+ return 1 |
+ |
+ self.SubmitIssue(wait_for_merge=True) |
+ print('Issue %s has been submitted.' % self.GetIssueURL()) |
+ return 0 |
+ |
+ |
+_CODEREVIEW_IMPLEMENTATIONS = { |
+ 'rietveld': _RietveldChangelistImpl, |
+ 'gerrit': _GerritChangelistImpl, |
+} |
+ |
class ChangeDescription(object): |
"""Contains a parsed form of the change description.""" |
@@ -3110,10 +3157,14 @@ def IsSubmoduleMergeCommit(ref): |
def SendUpstream(parser, args, cmd): |
"""Common code for CMDland and CmdDCommit |
- Squashes branch into a single commit. |
- Updates changelog with metadata (e.g. pointer to review). |
- Pushes/dcommits the code upstream. |
- Updates review and closes. |
+ In case of Gerrit, uses Gerrit REST api to "submit" the issue, which pushes |
+ upstream and closes the issue automatically and atomically. |
+ |
+ Otherwise (in case of Rietveld): |
+ Squashes branch into a single commit. |
+ Updates changelog with metadata (e.g. pointer to review). |
+ Pushes/dcommits the code upstream. |
+ Updates review and closes. |
""" |
parser.add_option('--bypass-hooks', action='store_true', dest='bypass_hooks', |
help='bypass upload presubmit hook') |
@@ -3132,6 +3183,24 @@ def SendUpstream(parser, args, cmd): |
cl = Changelist(auth_config=auth_config) |
+ # TODO(tandrii): refactor this into _RietveldChangelistImpl method. |
+ if cl.IsGerrit(): |
+ if options.message: |
+ # This could be implemented, but it requires sending a new patch to |
+ # Gerrit, as Gerrit unlike Rietveld versions messages with patchsets. |
+ # Besides, Gerrit has the ability to change the commit message on submit |
+ # automatically, thus there is no need to support this option (so far?). |
+ parser.error('-m MESSAGE option is not supported for Gerrit.') |
+ if options.contributor: |
+ parser.error( |
+ '-c CONTRIBUTOR option is not supported for Gerrit.\n' |
+ 'Before uploading a commit to Gerrit, ensure it\'s author field is ' |
+ 'the contributor\'s "name <email>". If you can\'t upload such a ' |
+ 'commit for review, contact your repository admin and request' |
+ '"Forge-Author" permission.') |
+ return cl._codereview_impl.CMDLand(options.force, options.bypass_hooks, |
+ options.verbose) |
+ |
current = cl.GetBranch() |
remote, upstream_branch = cl.FetchUpstreamTuple(cl.GetBranch()) |
if not settings.GetIsGitSvn() and remote == '.': |