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

Side by Side Diff: git_cl.py

Issue 1844343002: Gerrit git cl: implement "git cl land". (Closed) Base URL: https://chromium.googlesource.com/chromium/tools/depot_tools.git@master
Patch Set: argh, rebase Created 4 years, 8 months 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
« no previous file with comments | « no previous file | no next file » | 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/env python 1 #!/usr/bin/env python
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 # Copyright (c) 2012 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 # Copyright (C) 2008 Evan Martin <martine@danga.com> 6 # Copyright (C) 2008 Evan Martin <martine@danga.com>
7 7
8 """A git-command for integrating reviews on Rietveld and Gerrit.""" 8 """A git-command for integrating reviews on Rietveld and Gerrit."""
9 9
10 from distutils.version import LooseVersion 10 from distutils.version import LooseVersion
(...skipping 863 matching lines...) Expand 10 before | Expand all | Expand 10 after
874 self.issue = issue or None 874 self.issue = issue or None
875 self.has_description = False 875 self.has_description = False
876 self.description = None 876 self.description = None
877 self.lookedup_patchset = False 877 self.lookedup_patchset = False
878 self.patchset = None 878 self.patchset = None
879 self.cc = None 879 self.cc = None
880 self.watchers = () 880 self.watchers = ()
881 self._remote = None 881 self._remote = None
882 882
883 self._codereview_impl = None 883 self._codereview_impl = None
884 self._codereview = None
884 self._load_codereview_impl(codereview, **kwargs) 885 self._load_codereview_impl(codereview, **kwargs)
886 assert self._codereview_impl
887 assert self._codereview in _CODEREVIEW_IMPLEMENTATIONS
885 888
886 def _load_codereview_impl(self, codereview=None, **kwargs): 889 def _load_codereview_impl(self, codereview=None, **kwargs):
887 if codereview: 890 if codereview:
888 codereview = codereview.lower() 891 assert codereview in _CODEREVIEW_IMPLEMENTATIONS
889 if codereview == 'gerrit': 892 cls = _CODEREVIEW_IMPLEMENTATIONS[codereview]
890 self._codereview_impl = _GerritChangelistImpl(self, **kwargs) 893 self._codereview = codereview
891 elif codereview == 'rietveld': 894 self._codereview_impl = cls(self, **kwargs)
892 self._codereview_impl = _RietveldChangelistImpl(self, **kwargs)
893 else:
894 assert codereview in ('rietveld', 'gerrit')
895 return 895 return
896 896
897 # Automatic selection based on issue number set for a current branch. 897 # Automatic selection based on issue number set for a current branch.
898 # Rietveld takes precedence over Gerrit. 898 # Rietveld takes precedence over Gerrit.
899 assert not self.issue 899 assert not self.issue
900 # Whether we find issue or not, we are doing the lookup. 900 # Whether we find issue or not, we are doing the lookup.
901 self.lookedup_issue = True 901 self.lookedup_issue = True
902 for cls in [_RietveldChangelistImpl, _GerritChangelistImpl]: 902 for codereview, cls in _CODEREVIEW_IMPLEMENTATIONS.iteritems():
903 setting = cls.IssueSetting(self.GetBranch()) 903 setting = cls.IssueSetting(self.GetBranch())
904 issue = RunGit(['config', setting], error_ok=True).strip() 904 issue = RunGit(['config', setting], error_ok=True).strip()
905 if issue: 905 if issue:
906 self._codereview = codereview
906 self._codereview_impl = cls(self, **kwargs) 907 self._codereview_impl = cls(self, **kwargs)
907 self.issue = int(issue) 908 self.issue = int(issue)
908 return 909 return
909 910
910 # No issue is set for this branch, so decide based on repo-wide settings. 911 # No issue is set for this branch, so decide based on repo-wide settings.
911 return self._load_codereview_impl( 912 return self._load_codereview_impl(
912 codereview='gerrit' if settings.GetIsGerrit() else 'rietveld', 913 codereview='gerrit' if settings.GetIsGerrit() else 'rietveld',
913 **kwargs) 914 **kwargs)
914 915
916 def IsGerrit(self):
917 return self._codereview == 'gerrit'
915 918
916 def GetCCList(self): 919 def GetCCList(self):
917 """Return the users cc'd on this CL. 920 """Return the users cc'd on this CL.
918 921
919 Return is a string suitable for passing to gcl with the --cc flag. 922 Return is a string suitable for passing to gcl with the --cc flag.
920 """ 923 """
921 if self.cc is None: 924 if self.cc is None:
922 base_cc = settings.GetDefaultCCList() 925 base_cc = settings.GetDefaultCCList()
923 more_cc = ','.join(self.watchers) 926 more_cc = ','.join(self.watchers)
924 self.cc = ','.join(filter(None, (base_cc, more_cc))) or '' 927 self.cc = ','.join(filter(None, (base_cc, more_cc))) or ''
(...skipping 718 matching lines...) Expand 10 before | Expand all | Expand 10 after
1643 data = self._GetChangeDetail(['COMMIT_FOOTERS', 'CURRENT_REVISION']) 1646 data = self._GetChangeDetail(['COMMIT_FOOTERS', 'CURRENT_REVISION'])
1644 return data['revisions'][data['current_revision']]['commit_with_footers'] 1647 return data['revisions'][data['current_revision']]['commit_with_footers']
1645 1648
1646 def UpdateDescriptionRemote(self, description): 1649 def UpdateDescriptionRemote(self, description):
1647 # TODO(tandrii) 1650 # TODO(tandrii)
1648 raise NotImplementedError() 1651 raise NotImplementedError()
1649 1652
1650 def CloseIssue(self): 1653 def CloseIssue(self):
1651 gerrit_util.AbandonChange(self._GetGerritHost(), self.GetIssue(), msg='') 1654 gerrit_util.AbandonChange(self._GetGerritHost(), self.GetIssue(), msg='')
1652 1655
1656 def SubmitIssue(self, wait_for_merge=True):
1657 gerrit_util.SubmitChange(self._GetGerritHost(), self.GetIssue(),
1658 wait_for_merge=wait_for_merge)
1653 1659
1654 def _GetChangeDetail(self, options): 1660 def _GetChangeDetail(self, options):
1655 return gerrit_util.GetChangeDetail(self._GetGerritHost(), self.GetIssue(), 1661 return gerrit_util.GetChangeDetail(self._GetGerritHost(), self.GetIssue(),
1656 options) 1662 options)
1657 1663
1664 def CMDLand(self, force, bypass_hooks, verbose):
1665 if git_common.is_dirty_git_tree('land'):
1666 return 1
1667 differs = True
1668 last_upload = RunGit(['config',
1669 'branch.%s.gerritsquashhash' % self.GetBranch()],
1670 error_ok=True).strip()
1671 # Note: git diff outputs nothing if there is no diff.
1672 if not last_upload or RunGit(['diff', last_upload]).strip():
1673 print('WARNING: some changes from local branch haven\'t been uploaded')
1674 else:
1675 detail = self._GetChangeDetail(['CURRENT_REVISION'])
1676 if detail['current_revision'] == last_upload:
1677 differs = False
1678 else:
1679 print('WARNING: local branch contents differ from latest uploaded '
1680 'patchset')
1681 if differs:
1682 if not force:
1683 ask_for_data(
1684 'Do you want to submit latest Gerrit patchset and bypass hooks?')
1685 print('WARNING: bypassing hooks and submitting latest uploaded patchset')
1686 elif not bypass_hooks:
1687 hook_results = self.RunHook(
1688 committing=True,
1689 may_prompt=not force,
1690 verbose=verbose,
1691 change=self.GetChange(self.GetCommonAncestorWithUpstream(), None))
1692 if not hook_results.should_continue():
1693 return 1
1694
1695 self.SubmitIssue(wait_for_merge=True)
1696 print('Issue %s has been submitted.' % self.GetIssueURL())
1697 return 0
1698
1699
1700 _CODEREVIEW_IMPLEMENTATIONS = {
1701 'rietveld': _RietveldChangelistImpl,
1702 'gerrit': _GerritChangelistImpl,
1703 }
1704
1658 1705
1659 class ChangeDescription(object): 1706 class ChangeDescription(object):
1660 """Contains a parsed form of the change description.""" 1707 """Contains a parsed form of the change description."""
1661 R_LINE = r'^[ \t]*(TBR|R)[ \t]*=[ \t]*(.*?)[ \t]*$' 1708 R_LINE = r'^[ \t]*(TBR|R)[ \t]*=[ \t]*(.*?)[ \t]*$'
1662 BUG_LINE = r'^[ \t]*(BUG)[ \t]*=[ \t]*(.*?)[ \t]*$' 1709 BUG_LINE = r'^[ \t]*(BUG)[ \t]*=[ \t]*(.*?)[ \t]*$'
1663 1710
1664 def __init__(self, description): 1711 def __init__(self, description):
1665 self._description_lines = (description or '').strip().splitlines() 1712 self._description_lines = (description or '').strip().splitlines()
1666 1713
1667 @property # www.logilab.org/ticket/89786 1714 @property # www.logilab.org/ticket/89786
(...skipping 1435 matching lines...) Expand 10 before | Expand all | Expand 10 after
3103 # When submodules are added to the repo, we expect there to be a single 3150 # When submodules are added to the repo, we expect there to be a single
3104 # non-git-svn merge commit at remote HEAD with a signature comment. 3151 # non-git-svn merge commit at remote HEAD with a signature comment.
3105 pattern = '^SVN changes up to revision [0-9]*$' 3152 pattern = '^SVN changes up to revision [0-9]*$'
3106 cmd = ['rev-list', '--merges', '--grep=%s' % pattern, '%s^!' % ref] 3153 cmd = ['rev-list', '--merges', '--grep=%s' % pattern, '%s^!' % ref]
3107 return RunGit(cmd) != '' 3154 return RunGit(cmd) != ''
3108 3155
3109 3156
3110 def SendUpstream(parser, args, cmd): 3157 def SendUpstream(parser, args, cmd):
3111 """Common code for CMDland and CmdDCommit 3158 """Common code for CMDland and CmdDCommit
3112 3159
3113 Squashes branch into a single commit. 3160 In case of Gerrit, uses Gerrit REST api to "submit" the issue, which pushes
3114 Updates changelog with metadata (e.g. pointer to review). 3161 upstream and closes the issue automatically and atomically.
3115 Pushes/dcommits the code upstream. 3162
3116 Updates review and closes. 3163 Otherwise (in case of Rietveld):
3164 Squashes branch into a single commit.
3165 Updates changelog with metadata (e.g. pointer to review).
3166 Pushes/dcommits the code upstream.
3167 Updates review and closes.
3117 """ 3168 """
3118 parser.add_option('--bypass-hooks', action='store_true', dest='bypass_hooks', 3169 parser.add_option('--bypass-hooks', action='store_true', dest='bypass_hooks',
3119 help='bypass upload presubmit hook') 3170 help='bypass upload presubmit hook')
3120 parser.add_option('-m', dest='message', 3171 parser.add_option('-m', dest='message',
3121 help="override review description") 3172 help="override review description")
3122 parser.add_option('-f', action='store_true', dest='force', 3173 parser.add_option('-f', action='store_true', dest='force',
3123 help="force yes to questions (don't prompt)") 3174 help="force yes to questions (don't prompt)")
3124 parser.add_option('-c', dest='contributor', 3175 parser.add_option('-c', dest='contributor',
3125 help="external contributor for patch (appended to " + 3176 help="external contributor for patch (appended to " +
3126 "description and used as author for git). Should be " + 3177 "description and used as author for git). Should be " +
3127 "formatted as 'First Last <email@example.com>'") 3178 "formatted as 'First Last <email@example.com>'")
3128 add_git_similarity(parser) 3179 add_git_similarity(parser)
3129 auth.add_auth_options(parser) 3180 auth.add_auth_options(parser)
3130 (options, args) = parser.parse_args(args) 3181 (options, args) = parser.parse_args(args)
3131 auth_config = auth.extract_auth_config_from_options(options) 3182 auth_config = auth.extract_auth_config_from_options(options)
3132 3183
3133 cl = Changelist(auth_config=auth_config) 3184 cl = Changelist(auth_config=auth_config)
3134 3185
3186 # TODO(tandrii): refactor this into _RietveldChangelistImpl method.
3187 if cl.IsGerrit():
3188 if options.message:
3189 # This could be implemented, but it requires sending a new patch to
3190 # Gerrit, as Gerrit unlike Rietveld versions messages with patchsets.
3191 # Besides, Gerrit has the ability to change the commit message on submit
3192 # automatically, thus there is no need to support this option (so far?).
3193 parser.error('-m MESSAGE option is not supported for Gerrit.')
3194 if options.contributor:
3195 parser.error(
3196 '-c CONTRIBUTOR option is not supported for Gerrit.\n'
3197 'Before uploading a commit to Gerrit, ensure it\'s author field is '
3198 'the contributor\'s "name <email>". If you can\'t upload such a '
3199 'commit for review, contact your repository admin and request'
3200 '"Forge-Author" permission.')
3201 return cl._codereview_impl.CMDLand(options.force, options.bypass_hooks,
3202 options.verbose)
3203
3135 current = cl.GetBranch() 3204 current = cl.GetBranch()
3136 remote, upstream_branch = cl.FetchUpstreamTuple(cl.GetBranch()) 3205 remote, upstream_branch = cl.FetchUpstreamTuple(cl.GetBranch())
3137 if not settings.GetIsGitSvn() and remote == '.': 3206 if not settings.GetIsGitSvn() and remote == '.':
3138 print 3207 print
3139 print 'Attempting to push branch %r into another local branch!' % current 3208 print 'Attempting to push branch %r into another local branch!' % current
3140 print 3209 print
3141 print 'Either reparent this branch on top of origin/master:' 3210 print 'Either reparent this branch on top of origin/master:'
3142 print ' git reparent-branch --root' 3211 print ' git reparent-branch --root'
3143 print 3212 print
3144 print 'OR run `git rebase-update` if you think the parent branch is already' 3213 print 'OR run `git rebase-update` if you think the parent branch is already'
(...skipping 1263 matching lines...) Expand 10 before | Expand all | Expand 10 after
4408 if __name__ == '__main__': 4477 if __name__ == '__main__':
4409 # These affect sys.stdout so do it outside of main() to simplify mocks in 4478 # These affect sys.stdout so do it outside of main() to simplify mocks in
4410 # unit testing. 4479 # unit testing.
4411 fix_encoding.fix_encoding() 4480 fix_encoding.fix_encoding()
4412 colorama.init() 4481 colorama.init()
4413 try: 4482 try:
4414 sys.exit(main(sys.argv[1:])) 4483 sys.exit(main(sys.argv[1:]))
4415 except KeyboardInterrupt: 4484 except KeyboardInterrupt:
4416 sys.stderr.write('interrupted\n') 4485 sys.stderr.write('interrupted\n')
4417 sys.exit(1) 4486 sys.exit(1)
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698