diff --git a/hgext/bookmarks.py b/hgext/bookmarks.py --- a/hgext/bookmarks.py +++ b/hgext/bookmarks.py @@ -30,7 +30,7 @@ from mercurial.i18n import _ from mercurial.node import nullid, nullrev, hex, short -from mercurial import util, commands, localrepo, repair, extensions +from mercurial import util, commands, localrepo, repair, extensions, changegroup import os def write(repo): @@ -316,6 +316,14 @@ setcurrent(repo, rev) return res +def listkey(repo): + return ('expected', 'actually') + +def pushkey(repo, values): + repo.ui.warn("pushed " + str(values) + "\n") + +changegroup.keyhandlers['bookmarks'] = listkey, pushkey + cmdtable = { "bookmarks": (bookmark, diff --git a/mercurial/changegroup.py b/mercurial/changegroup.py --- a/mercurial/changegroup.py +++ b/mercurial/changegroup.py @@ -9,6 +9,10 @@ import util import struct, os, bz2, zlib, tempfile +# format per handler: +# 'key' : (listkey-handler, pushkey-handler) +keyhandlers = {} + def getchunk(source): """return the next chunk from changegroup 'source' as a string""" d = source.read(4) diff --git a/mercurial/hgweb/protocol.py b/mercurial/hgweb/protocol.py --- a/mercurial/hgweb/protocol.py +++ b/mercurial/hgweb/protocol.py @@ -5,7 +5,7 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -import cStringIO, zlib, tempfile, errno, os, sys, urllib, copy +import cStringIO, zlib, tempfile, errno, os, sys, urllib, copy, json from mercurial import util, streamclone from mercurial.node import bin, hex from mercurial import changegroup as changegroupmod @@ -17,11 +17,11 @@ __all__ = [ 'lookup', 'heads', 'branches', 'between', 'changegroup', 'changegroupsubset', 'capabilities', 'unbundle', 'stream_out', - 'branchmap', + 'branchmap', 'listkeys', 'pushkeys' ] HGTYPE = 'application/mercurial-0.1' -basecaps = 'lookup changegroupsubset branchmap'.split() +basecaps = 'lookup changegroupsubset branchmap pushkeys'.split() def lookup(repo, req): try: @@ -206,3 +206,37 @@ yield chunk except streamclone.StreamException, inst: yield str(inst) + +def listkeys(repo, req): + import pdb + if req.env['REQUEST_METHOD'] != 'POST': + # TODO: HTTP METHOD NOT DEFINED + raise ErrorResponse(HTTP_METHOD_NOT_ALLOWED, 'Not allowed') + print "i'm going to read" + pdb.set_trace() + data = req.read() + print "i got my data " + if not data: + rsp = keyhandlers.keys() + else: + obj = json.load(req) + rsp = {} + for ns in obj: + if ns in keyhandlers and keyhandlers[ns][0]: + rsp[ns] = keyhandlers[ns][0] + req.respond(HTTP_OK, 'application/json') + return json.dumps(rsp, ensure_ascii=True) + +def pushkeys(repo, req): + if req.env['REQUEST_METHOD'] != 'POST': + raise ErrorResponse(HTTP_METHOD_NOT_ALLOWED, 'Not allowed') + data = req.read()[0].strip() + if not data: + raise ErrorResponse(HTTP_BAD_REQUEST, 'Require data') + rsp = {} + obj = json.loads(data) + for ns, values in obj.iteritems(): + if ns in keyhandlers and keyhandlers[ns][1]: + rsp[ns] = keyhandlers[ns][1](values) + req.respond(HTTP_OK, 'application/json') + return json.dumps(rsp, ensure_ascii=True) diff --git a/mercurial/httprepo.py b/mercurial/httprepo.py --- a/mercurial/httprepo.py +++ b/mercurial/httprepo.py @@ -9,7 +9,7 @@ from node import bin, hex, nullid from i18n import _ import repo, changegroup, statichttprepo, error, url, util -import os, urllib, urllib2, urlparse, zlib, httplib +import os, urllib, urllib2, urlparse, zlib, httplib, json import errno, socket import encoding @@ -195,6 +195,17 @@ f = self.do_cmd("changegroup", roots=n) return util.chunkbuffer(zgenerator(f)) + def pushkeys(self, data): + f = self.do_cmd("listkeys", json.loads(data)) + self.ui.warn(f) + return util.chunkbuffer(zgenerator(f)) + + def listkeys(self, namespaces=None): + f = self.do_read("listkeys", data=json.dumps(namespaces, + ensure_ascii=True), headers={'Content-Type': 'application/json'}) + self.ui.status(f) + return json.loads(f) + def changegroupsubset(self, bases, heads, source): self.requirecap('changegroupsubset', _('look up remote changes')) baselst = " ".join([hex(n) for n in bases]) diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py --- a/mercurial/localrepo.py +++ b/mercurial/localrepo.py @@ -15,11 +15,12 @@ import merge as merge_ import tags as tags_ from lock import release -import weakref, stat, errno, os, time, inspect +from changegroup import keyhandlers +import weakref, stat, errno, os, time, inspect, json propertycache = util.propertycache class localrepository(repo.repository): - capabilities = set(('lookup', 'changegroupsubset', 'branchmap')) + capabilities = set(('lookup', 'changegroupsubset', 'branchmap', 'pushkeys')) supported = set('revlogv1 store fncache shared'.split()) def __init__(self, baseui, path=None, create=0): @@ -1449,6 +1450,8 @@ if not fetch: self.ui.status(_("no changes found\n")) + if remote.capable('pushkeys'): + self.pullkeys(remote) return 0 if heads is None and remote.capable('changegroupsubset'): @@ -1462,7 +1465,10 @@ "other repository doesn't support " "changegroupsubset.")) cg = remote.changegroupsubset(fetch, heads, 'pull') - return self.addchangegroup(cg, 'pull', remote.url()) + res = self.addchangegroup(cg, 'pull', remote.url()) + if remote.capable('pushkeys'): + self.pullkeys(remote) + return res finally: lock.release() @@ -1476,8 +1482,12 @@ # servers, http servers). if remote.capable('unbundle'): - return self.push_unbundle(remote, force, revs) - return self.push_addchangegroup(remote, force, revs) + res = self.push_unbundle(remote, force, revs) + else: + res = self.push_addchangegroup(remote, force, revs) + if remote.capable('pushkeys'): + self.pushkeysremote(remote) + return res def prepush(self, remote, force, revs): '''Analyze the local and remote repositories and determine which @@ -1618,6 +1628,48 @@ return remote.unbundle(cg, remote_heads, 'push') return ret[1] + def listkeys(self, namespaces=None): + if not namespaces: + return keyhandlers.keys() + rsp = {} + for ns in namespaces: + if ns in keyhandlers and keyhandlers[ns][0]: + rsp[ns] = keyhandlers[ns][0](self) + return rsp + + def pushkeys(self, data): + rsp = {} + for ns, values in data.iteritems(): + if ns in keyhandlers and keyhandlers[ns][0]: + rsp[ns] = keyhandlers[ns][0](self) + return rsp + + def pushkeysremote(self, remote): + """helper function to be called from push()""" + req = {} + for ns in remote.listkeys(): + if ns in keyhandlers and keyhandlers[ns][0]: + req[ns] = keyhandlers[ns][0](self) + obj = remote.pushkeys(req) + for ns, error in obj.iteritems(): + if error: + self.ui.warn(error) + self.ui.status(_("pushing keys completed\n")) + + def pullkeys(self, remote): + """helper function to be called from pull()""" + rsp = {} + keys = remote.listkeys(keyhandlers.keys()) + count = 0 + for ns, values in keys.iteritems(): + if ns in keyhandlers and keyhandlers[ns][1]: + count += len(values) + rsp[ns] = keyhandlers[ns][1](self, values) + for ns, error in rsp.iteritems(): + if error: + self.ui.warn(error) + self.ui.status(_("added %d keys\n" % count)) + def changegroupinfo(self, nodes, source): if self.ui.verbose or source == 'bundle': self.ui.status(_("%d changesets found\n") % len(nodes)) diff --git a/mercurial/sshrepo.py b/mercurial/sshrepo.py --- a/mercurial/sshrepo.py +++ b/mercurial/sshrepo.py @@ -8,7 +8,7 @@ from node import bin, hex from i18n import _ import repo, util, error, encoding -import re, urllib +import re, urllib, json class remotelock(object): def __init__(self, repo): @@ -266,4 +266,13 @@ def stream_out(self): return self.do_cmd('stream_out') + def listkeys(self, namespaces=None): + self.ui.status("listkeys") + return json.loads(self.call('listkeys', + namespaces=json.dumps(namespaces)), ensure_ascii=True) + + def pushkeys(self, data): + return json.loads(self.call('pushkeys', data=json.loads(data)), + ensure_ascii=True) + instance = sshrepository diff --git a/mercurial/sshserver.py b/mercurial/sshserver.py --- a/mercurial/sshserver.py +++ b/mercurial/sshserver.py @@ -9,11 +9,11 @@ from i18n import _ from node import bin, hex import streamclone, util, hook -import os, sys, tempfile, urllib, copy +import os, sys, tempfile, urllib, copy, json class sshserver(object): - caps = 'unbundle lookup changegroupsubset branchmap'.split() + caps = 'unbundle lookup changegroupsubset branchmap pushkeys'.split() def __init__(self, ui, repo): self.ui = ui @@ -223,3 +223,7 @@ except streamclone.StreamException, inst: self.fout.write(str(inst)) self.fout.flush() + + def do_listkeys(self): + namespaces = json.loads(self.getarg()[0].split()) + return json.dumps(self.repo.listkeys(namespaces), ensure_ascii=True)