From b54a392c9a267a06058b663377282c9dcec6878e Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 5 Jan 2009 16:18:58 -0800 Subject: [PATCH] Support Gerrit2's ssh:// based upload In Gerrit2 uploads are sent over "git push ssh://...", as this is a more efficient transport and is easier to code from external scripts and/or direct command line usage by an end-user. Gerrit1's HTTP POST based format is assumed if the review server does not have the /ssh_info URL available on it. Signed-off-by: Shawn O. Pearce --- git_config.py | 42 ++++++++++++++++++++++++++++- project.py | 73 +++++++++++++++++++++++++++++++++++---------------- 2 files changed, 91 insertions(+), 24 deletions(-) diff --git a/git_config.py b/git_config.py index 9ddb2edc..ed5a44a4 100644 --- a/git_config.py +++ b/git_config.py @@ -16,7 +16,8 @@ import os import re import sys -from error import GitError +from urllib2 import urlopen, HTTPError +from error import GitError, UploadError from git_command import GitCommand R_HEADS = 'refs/heads/' @@ -261,6 +262,45 @@ class Remote(object): self.projectname = self._Get('projectname') self.fetch = map(lambda x: RefSpec.FromString(x), self._Get('fetch', all=True)) + self._review_protocol = None + + @property + def ReviewProtocol(self): + if self._review_protocol is None: + if self.review is None: + return None + + u = self.review + if not u.startswith('http:') and not u.startswith('https:'): + u = 'http://%s' % u + if not u.endswith('/'): + u += '/' + u += 'ssh_info' + + try: + info = urlopen(u).read() + if info == 'NOT_AVAILABLE': + raise UploadError('Upload over ssh unavailable') + + self._review_protocol = 'ssh' + self._review_host = info.split(" ")[0] + self._review_port = info.split(" ")[1] + + except HTTPError, e: + if e.code == 404: + self._review_protocol = 'http-post' + else: + raise UploadError('Cannot guess Gerrit version') + return self._review_protocol + + def SshReviewUrl(self, userEmail): + if self.ReviewProtocol != 'ssh': + return None + return 'ssh://%s@%s:%s/%s' % ( + userEmail.split("@")[0], + self._review_host, + self._review_port, + self.projectname) def ToLocal(self, rev): """Convert a remote revision string to something we have locally. diff --git a/project.py b/project.py index 7743ca10..5d036c35 100644 --- a/project.py +++ b/project.py @@ -46,6 +46,8 @@ def _info(fmt, *args): def not_rev(r): return '^' + r +def sq(r): + return "'" + r.replace("'", "'\''") + "'" hook_list = None def repo_hooks(): @@ -475,33 +477,58 @@ class Project(object): if not dest_branch.startswith(R_HEADS): dest_branch = R_HEADS + dest_branch - base_list = [] - for name, id in self._allrefs.iteritems(): - if branch.remote.WritesTo(name): - base_list.append(not_rev(name)) - if not base_list: - raise GitError('no base refs, cannot upload %s' % branch.name) - if not branch.remote.projectname: branch.remote.projectname = self.name branch.remote.Save() - print >>sys.stderr, '' - _info("Uploading %s to %s:", branch.name, self.name) - try: - UploadBundle(project = self, - server = branch.remote.review, - email = self.UserEmail, - dest_project = branch.remote.projectname, - dest_branch = dest_branch, - src_branch = R_HEADS + branch.name, - bases = base_list, - people = people, - replace_changes = replace_changes) - except proto_client.ClientLoginError: - raise UploadError('Login failure') - except urllib2.HTTPError, e: - raise UploadError('HTTP error %d' % e.code) + if branch.remote.ReviewProtocol == 'http-post': + base_list = [] + for name, id in self._allrefs.iteritems(): + if branch.remote.WritesTo(name): + base_list.append(not_rev(name)) + if not base_list: + raise GitError('no base refs, cannot upload %s' % branch.name) + + print >>sys.stderr, '' + _info("Uploading %s to %s:", branch.name, self.name) + try: + UploadBundle(project = self, + server = branch.remote.review, + email = self.UserEmail, + dest_project = branch.remote.projectname, + dest_branch = dest_branch, + src_branch = R_HEADS + branch.name, + bases = base_list, + people = people, + replace_changes = replace_changes) + except proto_client.ClientLoginError: + raise UploadError('Login failure') + except urllib2.HTTPError, e: + raise UploadError('HTTP error %d' % e.code) + + elif branch.remote.ReviewProtocol == 'ssh': + if dest_branch.startswith(R_HEADS): + dest_branch = dest_branch[len(R_HEADS):] + + rp = ['gerrit receive-pack'] + for e in people[0]: + rp.append('--reviewer=%s' % sq(e)) + for e in people[1]: + rp.append('--cc=%s' % sq(e)) + + cmd = ['push'] + cmd.append('--receive-pack=%s' % " ".join(rp)) + cmd.append(branch.remote.SshReviewUrl(self.UserEmail)) + cmd.append('%s:refs/for/%s' % (R_HEADS + branch.name, dest_branch)) + if replace_changes: + for change_id,commit_id in replace_changes.iteritems(): + cmd.append('%s:refs/changes/%s/new' % (commit_id, change_id)) + if GitCommand(self, cmd, bare = True).Wait() != 0: + raise UploadError('Upload failed') + + else: + raise UploadError('Unsupported protocol %s' \ + % branch.remote.review) msg = "posted to %s for %s" % (branch.remote.review, dest_branch) self.bare_git.UpdateRef(R_PUB + branch.name,