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 <sop@google.com>
diff --git a/git_config.py b/git_config.py
index 9ddb2ed..ed5a44a 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 @@
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 7743ca1..5d036c3 100644
--- a/project.py
+++ b/project.py
@@ -46,6 +46,8 @@
def not_rev(r):
return '^' + r
+def sq(r):
+ return "'" + r.replace("'", "'\''") + "'"
hook_list = None
def repo_hooks():
@@ -475,33 +477,58 @@
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,