Add minimal Gerrit support to 'git cl config' and 'git cl upload'

This is the bare beginnings of Gerrit support for (non-repo) depot_tools,
based on Roland's work.
Differnt from http://codereview.chromium.org/8826015/
 it read codereview.settings and if it has GERRTI_HOST and GERRIT_PORT,
 then "git cl config" configured it for gerrit.
   installs hooks/commit-msg
   git config gerrit.host $GERRIT_HOST
   git config gerrit.port $GERRIT_PORT

 if it has gerrit.host config, "git cl upload" will upload a change
 to gerrit as
   "git push --receive-pack=... origin master"
 it scans description and extract reviewers from R= line.

Review URL: http://codereview.chromium.org/9264065

git-svn-id: svn://svn.chromium.org/chrome/trunk/tools/depot_tools@120276 0039d316-1c4b-4281-b951-d872f2087c98
experimental/szager/collated-output
ukai@chromium.org 13 years ago
parent c920ad2e24
commit e807781320

@ -147,6 +147,7 @@ class Settings(object):
self.viewvc_url = None
self.updated = False
self.did_migrate_check = False
self.is_gerrit = None
def LazyUpdateIfNeeded(self):
"""Updates the settings from a codereview.settings file, if available."""
@ -264,6 +265,12 @@ class Settings(object):
def GetDefaultCCList(self):
return self._GetConfig('rietveld.cc', error_ok=True)
def GetIsGerrit(self):
"""Return true if this repo is assosiated with gerrit code review system."""
if self.is_gerrit is None:
self.is_gerrit = self._GetConfig('gerrit.host', error_ok=True)
return self.is_gerrit
def _GetConfig(self, param, **kwargs):
self.LazyUpdateIfNeeded()
return RunGit(['config', param], **kwargs).strip()
@ -604,6 +611,7 @@ or verify this branch is set up to track another (via the --track argument to
def GetCodereviewSettingsInteractively():
"""Prompt the user for settings."""
# TODO(ukai): ask code review system is rietveld or gerrit?
server = settings.GetDefaultServerUrl(error_ok=True)
prompt = 'Rietveld server (host[:port])'
prompt += ' [%s]' % (server or DEFAULT_SERVER)
@ -666,9 +674,9 @@ class ChangeDescription(object):
content = re.compile(r'^#.*$', re.MULTILINE).sub('', content).strip()
if not content:
DieWithError('No CL description, aborting')
self._ParseDescription(content)
self.ParseDescription(content)
def _ParseDescription(self, description):
def ParseDescription(self, description):
"""Updates the list of reviewers and subject from the description."""
if not description:
self.description = description
@ -723,6 +731,15 @@ def LoadCodereviewSettingsFromFile(fileobj):
SetProperty('tree-status-url', 'STATUS', unset_error_ok=True)
SetProperty('viewvc-url', 'VIEW_VC', unset_error_ok=True)
if 'GERRIT_HOST' in keyvals and 'GERRIT_PORT' in keyvals:
RunGit(['config', 'gerrit.host', keyvals['GERRIT_HOST']])
RunGit(['config', 'gerrit.port', keyvals['GERRIT_PORT']])
# Install the standard commit-msg hook.
RunCommand(['scp', '-p', '-P', keyvals['GERRIT_PORT'],
'%s:hooks/commit-msg' % keyvals['GERRIT_HOST'],
os.path.join(settings.GetRoot(),
'.git', 'hooks', 'commit-msg')])
if 'PUSH_URL_CONFIG' in keyvals and 'ORIGIN_URL_CONFIG' in keyvals:
#should be of the form
#PUSH_URL_CONFIG: url.ssh://gitrw.chromium.org.pushinsteadof
@ -863,64 +880,49 @@ def CMDpresubmit(parser, args):
return 0
@usage('[args to "git diff"]')
def CMDupload(parser, args):
"""upload the current changelist to codereview"""
parser.add_option('--bypass-hooks', action='store_true', dest='bypass_hooks',
help='bypass upload presubmit hook')
parser.add_option('-f', action='store_true', dest='force',
help="force yes to questions (don't prompt)")
parser.add_option('-m', dest='message', help='message for patch')
parser.add_option('-r', '--reviewers',
help='reviewer email addresses')
parser.add_option('--cc',
help='cc email addresses')
parser.add_option('--send-mail', action='store_true',
help='send email to reviewer immediately')
parser.add_option("--emulate_svn_auto_props", action="store_true",
dest="emulate_svn_auto_props",
help="Emulate Subversion's auto properties feature.")
parser.add_option("--desc_from_logs", action="store_true",
dest="from_logs",
help="""Squashes git commit logs into change description and
uses message as subject""")
parser.add_option('-c', '--use-commit-queue', action='store_true',
help='tell the commit queue to commit this patchset')
(options, args) = parser.parse_args(args)
# Make sure index is up-to-date before running diff-index.
RunGit(['update-index', '--refresh', '-q'], error_ok=True)
if RunGit(['diff-index', 'HEAD']):
print 'Cannot upload with a dirty tree. You must commit locally first.'
def GerritUpload(options, args, cl):
"""upload the current branch to gerrit."""
# We assume the remote called "origin" is the one we want.
# It is probably not worthwhile to support different workflows.
remote = 'origin'
branch = 'master'
if options.target_branch:
branch = options.target_branch
log_desc = CreateDescriptionFromLog(args)
if options.reviewers:
log_desc += '\nR=' + options.reviewers
change_desc = ChangeDescription(options.message, log_desc,
options.reviewers)
change_desc.ParseDescription(log_desc)
if change_desc.IsEmpty():
print "Description is empty; aborting."
return 1
cl = Changelist()
if args:
base_branch = args[0]
else:
# Default to diffing against the "upstream" branch.
base_branch = cl.GetUpstreamBranch()
args = [base_branch + "..."]
if not options.bypass_hooks:
hook_results = cl.RunHook(committing=False, upstream_branch=base_branch,
may_prompt=not options.force,
verbose=options.verbose,
author=None)
if not hook_results.should_continue():
return 1
if not options.reviewers and hook_results.reviewers:
options.reviewers = hook_results.reviewers
receive_options = []
cc = cl.GetCCList().split(',')
if options.cc:
cc += options.cc.split(',')
cc = filter(None, cc)
if cc:
receive_options += ['--cc=' + email for email in cc]
if change_desc.reviewers:
reviewers = filter(None, change_desc.reviewers.split(','))
if reviewers:
receive_options += ['--reviewer=' + email for email in reviewers]
git_command = ['push']
if receive_options:
git_command.append('--receive-pack="git receive-pack %s"' %
' '.join(receive_options))
git_command += [remote, 'HEAD:refs/for/' + branch]
RunGit(git_command)
# TODO(ukai): parse Change-Id: and set issue number?
return 0
# --no-ext-diff is broken in some versions of Git, so try to work around
# this by overriding the environment (but there is still a problem if the
# git config key "diff.external" is used).
env = os.environ.copy()
if 'GIT_EXTERNAL_DIFF' in env:
del env['GIT_EXTERNAL_DIFF']
subprocess2.call(
['git', 'diff', '--no-ext-diff', '--stat', '-M'] + args, env=env)
def RietveldUpload(options, args, cl):
"""upload the patch to rietveld."""
upload_args = ['--assume_yes'] # Don't ask about untracked files.
upload_args.extend(['--server', cl.GetRietveldServer()])
if options.emulate_svn_auto_props:
@ -1002,6 +1004,73 @@ def CMDupload(parser, args):
return 0
@usage('[args to "git diff"]')
def CMDupload(parser, args):
"""upload the current changelist to codereview"""
parser.add_option('--bypass-hooks', action='store_true', dest='bypass_hooks',
help='bypass upload presubmit hook')
parser.add_option('-f', action='store_true', dest='force',
help="force yes to questions (don't prompt)")
parser.add_option('-m', dest='message', help='message for patch')
parser.add_option('-r', '--reviewers',
help='reviewer email addresses')
parser.add_option('--cc',
help='cc email addresses')
parser.add_option('--send-mail', action='store_true',
help='send email to reviewer immediately')
parser.add_option("--emulate_svn_auto_props", action="store_true",
dest="emulate_svn_auto_props",
help="Emulate Subversion's auto properties feature.")
parser.add_option("--desc_from_logs", action="store_true",
dest="from_logs",
help="""Squashes git commit logs into change description and
uses message as subject""")
parser.add_option('-c', '--use-commit-queue', action='store_true',
help='tell the commit queue to commit this patchset')
if settings.GetIsGerrit():
parser.add_option('--target_branch', dest='target_branch', default='master',
help='target branch to upload')
(options, args) = parser.parse_args(args)
# Make sure index is up-to-date before running diff-index.
RunGit(['update-index', '--refresh', '-q'], error_ok=True)
if RunGit(['diff-index', 'HEAD']):
print 'Cannot upload with a dirty tree. You must commit locally first.'
return 1
cl = Changelist()
if args:
# TODO(ukai): is it ok for gerrit case?
base_branch = args[0]
else:
# Default to diffing against the "upstream" branch.
base_branch = cl.GetUpstreamBranch()
args = [base_branch + "..."]
if not options.bypass_hooks:
hook_results = cl.RunHook(committing=False, upstream_branch=base_branch,
may_prompt=not options.force,
verbose=options.verbose,
author=None)
if not hook_results.should_continue():
return 1
if not options.reviewers and hook_results.reviewers:
options.reviewers = hook_results.reviewers
# --no-ext-diff is broken in some versions of Git, so try to work around
# this by overriding the environment (but there is still a problem if the
# git config key "diff.external" is used).
env = os.environ.copy()
if 'GIT_EXTERNAL_DIFF' in env:
del env['GIT_EXTERNAL_DIFF']
subprocess2.call(
['git', 'diff', '--no-ext-diff', '--stat', '-M'] + args, env=env)
if settings.GetIsGerrit():
return GerritUpload(options, args, cl)
return RietveldUpload(options, args, cl)
def SendUpstream(parser, args, cmd):
"""Common code for CmdPush and CmdDCommit
@ -1229,6 +1298,7 @@ def CMDpatch(parser, args):
issue_arg = args[0]
# TODO(maruel): Use apply_issue.py
# TODO(ukai): use gerrit-cherry-pick for gerrit repository?
if re.match(r'\d+', issue_arg):
# Input is an issue id. Figure out the URL.

@ -88,6 +88,7 @@ class TestGitCl(TestCase):
@staticmethod
def _git_base_calls():
return [
((['git', 'config', 'gerrit.host'],), ''),
((['git', 'update-index', '--refresh', '-q'],), ''),
((['git', 'diff-index', 'HEAD'],), ''),
((['git', 'config', 'rietveld.server'],), 'codereview.example.com'),
@ -321,5 +322,76 @@ class TestGitCl(TestCase):
git_cl.main(['dcommit', '--bypass-hooks'])
@staticmethod
def _gerrit_base_calls():
return [
((['git', 'config', 'gerrit.host'],), 'gerrit.example.com'),
((['git', 'update-index', '--refresh', '-q'],), ''),
((['git', 'diff-index', 'HEAD'],), ''),
((['git', 'config', 'rietveld.server'],), 'codereview.example.com'),
((['git', 'symbolic-ref', 'HEAD'],), 'master'),
((['git', 'config', 'branch.master.merge'],), 'master'),
((['git', 'config', 'branch.master.remote'],), 'origin'),
((['git', 'rev-parse', '--show-cdup'],), ''),
((['git', 'rev-parse', 'HEAD'],), '12345'),
((['git', 'diff', '--name-status', '-r', 'master...', '.'],),
'M\t.gitignore\n'),
((['git', 'rev-parse', '--git-dir'],), '.git'),
((['git', 'config', 'branch.master.rietveldissue'],), ''),
((['git', 'config', 'branch.master.rietveldpatchset'],), ''),
((['git', 'log', '--pretty=format:%s%n%n%b', 'master...'],), 'foo'),
((['git', 'config', 'user.email'],), 'me@example.com'),
((['git', 'diff', '--no-ext-diff', '--stat', '-M', 'master...'],),
'+dat'),
]
@staticmethod
def _gerrit_upload_calls(description, reviewers):
calls = [
((['git', 'log', '--pretty=format:%s\n\n%b', 'master..'],),
description),
((['git', 'config', 'rietveld.cc'],), '')
]
receive_pack = '--receive-pack="git receive-pack '
receive_pack += '--cc=joe@example.com' # from watch list
if reviewers:
receive_pack += ' '
receive_pack += ' '.join(['--reviewer=' + email for email in reviewers])
receive_pack += '"'
calls += [
((['git', 'push', receive_pack, 'origin', 'HEAD:refs/for/master'],),
'')
]
return calls
def _run_gerrit_reviewer_test(
self,
upload_args,
description,
reviewers):
"""Generic gerrit reviewer test framework."""
self.calls = self._gerrit_base_calls()
self.calls += self._gerrit_upload_calls(description, reviewers)
git_cl.main(['upload'] + upload_args)
def test_gerrit_no_reviewer(self):
self._run_gerrit_reviewer_test(
[],
'desc\n\nBUG=\nTEST=\n',
[])
def test_gerrit_reviewers_cmd_line(self):
self._run_gerrit_reviewer_test(
['-r', 'foo@example.com'],
'desc\n\nBUG=\nTEST=\n',
['foo@example.com'])
def test_gerrit_reviewer_multiple(self):
self._run_gerrit_reviewer_test(
[],
'desc\nTBR=reviewer@example.com\nBUG=\nR=another@example.com\n',
['reviewer@example.com', 'another@example.com'])
if __name__ == '__main__':
unittest.main()

Loading…
Cancel
Save