##// END OF EJS Templates
caches: invalidate store caches when lock is taken...
caches: invalidate store caches when lock is taken The fncache was not being properly invalidated each time the lock was taken, so in theory it could contain old data from prior to the caller having the lock. This changes it to be invalidated as soon as the lock is taken (same as all our other caches).

File last commit:

r20775:982f13be default
r20884:2efdd186 default
Show More
wireproto.py
680 lines | 22.4 KiB | text/x-python | PythonLexer
Matt Mackall
protocol: introduce wireproto.py...
r11581 # wireproto.py - generic wire protocol support functions
#
# Copyright 2005-2010 Matt Mackall <mpm@selenic.com>
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
Benoit Boissinot
fix undefined variables, spotted by pylint
r11879 import urllib, tempfile, os, sys
Matt Mackall
protocol: introduce wireproto.py...
r11581 from i18n import _
from node import bin, hex
Matt Mackall
protocol: unify unbundle on the server side
r11593 import changegroup as changegroupmod
Peter Arrenbrecht
peer: introduce real peer classes...
r17192 import peer, error, encoding, util, store
Matt Mackall
protocol: introduce wireproto.py...
r11581
Peter Arrenbrecht
wireproto: add basic command batching infrastructure...
r14621 # abstract batching support
class future(object):
'''placeholder for a value to be set later'''
def set(self, value):
Augie Fackler
wireproto: use safehasattr or getattr instead of hasattr
r14970 if util.safehasattr(self, 'value'):
Peter Arrenbrecht
wireproto: add basic command batching infrastructure...
r14621 raise error.RepoError("future is already set")
self.value = value
class batcher(object):
'''base class for batches of commands submittable in a single request
Brodie Rao
cleanup: eradicate long lines
r16683 All methods invoked on instances of this class are simply queued and
return a a future for the result. Once you call submit(), all the queued
calls are performed and the results set in their respective futures.
Peter Arrenbrecht
wireproto: add basic command batching infrastructure...
r14621 '''
def __init__(self):
self.calls = []
def __getattr__(self, name):
def call(*args, **opts):
resref = future()
self.calls.append((name, args, opts, resref,))
return resref
return call
def submit(self):
pass
class localbatch(batcher):
'''performs the queued calls directly'''
def __init__(self, local):
batcher.__init__(self)
self.local = local
def submit(self):
for name, args, opts, resref in self.calls:
resref.set(getattr(self.local, name)(*args, **opts))
class remotebatch(batcher):
'''batches the queued calls; uses as few roundtrips as possible'''
def __init__(self, remote):
Brodie Rao
cleanup: eradicate long lines
r16683 '''remote must support _submitbatch(encbatch) and
_submitone(op, encargs)'''
Peter Arrenbrecht
wireproto: add basic command batching infrastructure...
r14621 batcher.__init__(self)
self.remote = remote
def submit(self):
req, rsp = [], []
for name, args, opts, resref in self.calls:
mtd = getattr(self.remote, name)
Augie Fackler
wireproto: use safehasattr or getattr instead of hasattr
r14970 batchablefn = getattr(mtd, 'batchable', None)
if batchablefn is not None:
batchable = batchablefn(mtd.im_self, *args, **opts)
Peter Arrenbrecht
wireproto: add basic command batching infrastructure...
r14621 encargsorres, encresref = batchable.next()
if encresref:
req.append((name, encargsorres,))
rsp.append((batchable, encresref, resref,))
else:
resref.set(encargsorres)
else:
if req:
self._submitreq(req, rsp)
req, rsp = [], []
resref.set(mtd(*args, **opts))
if req:
self._submitreq(req, rsp)
def _submitreq(self, req, rsp):
encresults = self.remote._submitbatch(req)
for encres, r in zip(encresults, rsp):
batchable, encresref, resref = r
encresref.set(encres)
resref.set(batchable.next())
def batchable(f):
'''annotation for batchable methods
Such methods must implement a coroutine as follows:
@batchable
def sample(self, one, two=None):
# Handle locally computable results first:
if not one:
yield "a local result", None
# Build list of encoded arguments suitable for your wire protocol:
encargs = [('one', encode(one),), ('two', encode(two),)]
# Create future for injection of encoded result:
encresref = future()
# Return encoded arguments and future:
yield encargs, encresref
Brodie Rao
cleanup: eradicate long lines
r16683 # Assuming the future to be filled with the result from the batched
# request now. Decode it:
Peter Arrenbrecht
wireproto: add basic command batching infrastructure...
r14621 yield decode(encresref.value)
Brodie Rao
cleanup: eradicate long lines
r16683 The decorator returns a function which wraps this coroutine as a plain
method, but adds the original method as an attribute called "batchable",
which is used by remotebatch to split the call into separate encoding and
decoding phases.
Peter Arrenbrecht
wireproto: add basic command batching infrastructure...
r14621 '''
def plain(*args, **opts):
batchable = f(*args, **opts)
encargsorres, encresref = batchable.next()
if not encresref:
return encargsorres # a local result in this case
self = args[0]
encresref.set(self._submitone(f.func_name, encargsorres))
return batchable.next()
setattr(plain, 'batchable', f)
return plain
Benoit Boissinot
wireproto: refactor list of nodeid encoding / decoding
r11597 # list of nodes encoding / decoding
def decodelist(l, sep=' '):
Peter Arrenbrecht
wireproto: fix decodelist to properly return empty list...
r13722 if l:
return map(bin, l.split(sep))
return []
Benoit Boissinot
wireproto: refactor list of nodeid encoding / decoding
r11597
def encodelist(l, sep=' '):
return sep.join(map(hex, l))
Peter Arrenbrecht
wireproto: add batching support to wirerepository...
r14622 # batched call argument encoding
def escapearg(plain):
return (plain
.replace(':', '::')
.replace(',', ':,')
.replace(';', ':;')
.replace('=', ':='))
def unescapearg(escaped):
return (escaped
.replace(':=', '=')
.replace(':;', ';')
.replace(':,', ',')
.replace('::', ':'))
Matt Mackall
protocol: move basic ssh client commands to wirerepository
r11586 # client side
Peter Arrenbrecht
peer: introduce real peer classes...
r17192 class wirepeer(peer.peerrepository):
Peter Arrenbrecht
wireproto: add batching support to wirerepository...
r14622
def batch(self):
return remotebatch(self)
def _submitbatch(self, req):
cmds = []
for op, argsdict in req:
args = ','.join('%s=%s' % p for p in argsdict.iteritems())
cmds.append('%s %s' % (op, args))
rsp = self._call("batch", cmds=';'.join(cmds))
return rsp.split(';')
def _submitone(self, op, args):
return self._call(op, **args)
Peter Arrenbrecht
wireproto: make a number of commands batchable...
r14623 @batchable
Matt Mackall
protocol: move basic ssh client commands to wirerepository
r11586 def lookup(self, key):
self.requirecap('lookup', _('look up remote revision'))
Peter Arrenbrecht
wireproto: make a number of commands batchable...
r14623 f = future()
Augie Fackler
wireproto: remove todict() and use {} literals instead
r20671 yield {'key': encoding.fromlocal(key)}, f
Peter Arrenbrecht
wireproto: make a number of commands batchable...
r14623 d = f.value
Matt Mackall
protocol: move basic ssh client commands to wirerepository
r11586 success, data = d[:-1].split(" ", 1)
if int(success):
Peter Arrenbrecht
wireproto: make a number of commands batchable...
r14623 yield bin(data)
Matt Mackall
protocol: move basic ssh client commands to wirerepository
r11586 self._abort(error.RepoError(data))
Peter Arrenbrecht
wireproto: make a number of commands batchable...
r14623 @batchable
Matt Mackall
protocol: move basic ssh client commands to wirerepository
r11586 def heads(self):
Peter Arrenbrecht
wireproto: make a number of commands batchable...
r14623 f = future()
yield {}, f
d = f.value
Matt Mackall
protocol: move basic ssh client commands to wirerepository
r11586 try:
Peter Arrenbrecht
wireproto: make a number of commands batchable...
r14623 yield decodelist(d[:-1])
Matt Mackall
wireproto: avoid naked excepts
r13726 except ValueError:
Benoit Boissinot
fix undefined variables, spotted by pylint
r11879 self._abort(error.ResponseError(_("unexpected response:"), d))
Matt Mackall
protocol: move basic ssh client commands to wirerepository
r11586
Peter Arrenbrecht
wireproto: make a number of commands batchable...
r14623 @batchable
Peter Arrenbrecht
wireproto: add known([id]) function...
r13723 def known(self, nodes):
Peter Arrenbrecht
wireproto: make a number of commands batchable...
r14623 f = future()
Augie Fackler
wireproto: remove todict() and use {} literals instead
r20671 yield {'nodes': encodelist(nodes)}, f
Peter Arrenbrecht
wireproto: make a number of commands batchable...
r14623 d = f.value
Peter Arrenbrecht
wireproto: add known([id]) function...
r13723 try:
Peter Arrenbrecht
wireproto: make a number of commands batchable...
r14623 yield [bool(int(f)) for f in d]
Matt Mackall
wireproto: avoid naked excepts
r13726 except ValueError:
Peter Arrenbrecht
wireproto: add known([id]) function...
r13723 self._abort(error.ResponseError(_("unexpected response:"), d))
Peter Arrenbrecht
wireproto: make a number of commands batchable...
r14623 @batchable
Matt Mackall
protocol: move basic ssh client commands to wirerepository
r11586 def branchmap(self):
Peter Arrenbrecht
wireproto: make a number of commands batchable...
r14623 f = future()
yield {}, f
d = f.value
Matt Mackall
protocol: move basic ssh client commands to wirerepository
r11586 try:
branchmap = {}
for branchpart in d.splitlines():
Benoit Boissinot
wireproto: refactor list of nodeid encoding / decoding
r11597 branchname, branchheads = branchpart.split(' ', 1)
Matt Mackall
branch: operate on branch names in local string space where possible...
r13047 branchname = encoding.tolocal(urllib.unquote(branchname))
Benoit Boissinot
wireproto: refactor list of nodeid encoding / decoding
r11597 branchheads = decodelist(branchheads)
Matt Mackall
protocol: move basic ssh client commands to wirerepository
r11586 branchmap[branchname] = branchheads
Peter Arrenbrecht
wireproto: make a number of commands batchable...
r14623 yield branchmap
Matt Mackall
protocol: move basic ssh client commands to wirerepository
r11586 except TypeError:
self._abort(error.ResponseError(_("unexpected response:"), d))
def branches(self, nodes):
Benoit Boissinot
wireproto: refactor list of nodeid encoding / decoding
r11597 n = encodelist(nodes)
Matt Mackall
protocol: move basic ssh client commands to wirerepository
r11586 d = self._call("branches", nodes=n)
try:
Benoit Boissinot
wireproto: refactor list of nodeid encoding / decoding
r11597 br = [tuple(decodelist(b)) for b in d.splitlines()]
Matt Mackall
protocol: move basic ssh client commands to wirerepository
r11586 return br
Matt Mackall
wireproto: avoid naked excepts
r13726 except ValueError:
Matt Mackall
protocol: move basic ssh client commands to wirerepository
r11586 self._abort(error.ResponseError(_("unexpected response:"), d))
def between(self, pairs):
Matt Mackall
protocol: unify basic http client requests
r11587 batch = 8 # avoid giant requests
r = []
for i in xrange(0, len(pairs), batch):
Benoit Boissinot
wireproto: refactor list of nodeid encoding / decoding
r11597 n = " ".join([encodelist(p, '-') for p in pairs[i:i + batch]])
Matt Mackall
protocol: unify basic http client requests
r11587 d = self._call("between", pairs=n)
try:
Benoit Boissinot
wireproto: refactor list of nodeid encoding / decoding
r11597 r.extend(l and decodelist(l) or [] for l in d.splitlines())
Matt Mackall
wireproto: avoid naked excepts
r13726 except ValueError:
Matt Mackall
protocol: unify basic http client requests
r11587 self._abort(error.ResponseError(_("unexpected response:"), d))
return r
Matt Mackall
protocol: move basic ssh client commands to wirerepository
r11586
Peter Arrenbrecht
wireproto: make a number of commands batchable...
r14623 @batchable
Matt Mackall
protocol: move basic ssh client commands to wirerepository
r11586 def pushkey(self, namespace, key, old, new):
if not self.capable('pushkey'):
Peter Arrenbrecht
wireproto: make a number of commands batchable...
r14623 yield False, None
f = future()
Pierre-Yves David
pushkey: add more verbose debug output regarding pushkey...
r17293 self.ui.debug('preparing pushkey for "%s:%s"\n' % (namespace, key))
Augie Fackler
wireproto: remove todict() and use {} literals instead
r20671 yield {'namespace': encoding.fromlocal(namespace),
'key': encoding.fromlocal(key),
'old': encoding.fromlocal(old),
'new': encoding.fromlocal(new)}, f
Peter Arrenbrecht
wireproto: make a number of commands batchable...
r14623 d = f.value
Pierre-Yves David
wireproto: handle other server output in pushkey...
r15652 d, output = d.split('\n', 1)
David Soria Parra
wireproto: catch possible cast error in pushkey...
r13450 try:
d = bool(int(d))
except ValueError:
raise error.ResponseError(
_('push failed (unexpected response):'), d)
Pierre-Yves David
wireproto: handle other server output in pushkey...
r15652 for l in output.splitlines(True):
self.ui.status(_('remote: '), l)
Peter Arrenbrecht
wireproto: make a number of commands batchable...
r14623 yield d
Matt Mackall
protocol: move basic ssh client commands to wirerepository
r11586
Peter Arrenbrecht
wireproto: make a number of commands batchable...
r14623 @batchable
Matt Mackall
protocol: move basic ssh client commands to wirerepository
r11586 def listkeys(self, namespace):
if not self.capable('pushkey'):
Peter Arrenbrecht
wireproto: make a number of commands batchable...
r14623 yield {}, None
f = future()
Pierre-Yves David
pushkey: add more verbose debug output regarding pushkey...
r17293 self.ui.debug('preparing listkeys for "%s"\n' % namespace)
Augie Fackler
wireproto: remove todict() and use {} literals instead
r20671 yield {'namespace': encoding.fromlocal(namespace)}, f
Peter Arrenbrecht
wireproto: make a number of commands batchable...
r14623 d = f.value
Matt Mackall
protocol: move basic ssh client commands to wirerepository
r11586 r = {}
for l in d.splitlines():
k, v = l.split('\t')
Matt Mackall
pushkey: use UTF-8
r13050 r[encoding.tolocal(k)] = encoding.tolocal(v)
Peter Arrenbrecht
wireproto: make a number of commands batchable...
r14623 yield r
Matt Mackall
protocol: move basic ssh client commands to wirerepository
r11586
Matt Mackall
protocol: unify stream_out client code
r11588 def stream_out(self):
return self._callstream('stream_out')
Matt Mackall
protocol: unify client changegroup methods
r11591 def changegroup(self, nodes, kind):
Benoit Boissinot
wireproto: refactor list of nodeid encoding / decoding
r11597 n = encodelist(nodes)
Matt Mackall
protocol: unify client changegroup methods
r11591 f = self._callstream("changegroup", roots=n)
Matt Mackall
bundle: encapsulate all bundle streams in unbundle class
r12337 return changegroupmod.unbundle10(self._decompress(f), 'UN')
Matt Mackall
protocol: unify client changegroup methods
r11591
def changegroupsubset(self, bases, heads, kind):
self.requirecap('changegroupsubset', _('look up remote changes'))
Benoit Boissinot
wireproto: refactor list of nodeid encoding / decoding
r11597 bases = encodelist(bases)
heads = encodelist(heads)
Matt Mackall
bundle: encapsulate all bundle streams in unbundle class
r12337 f = self._callstream("changegroupsubset",
bases=bases, heads=heads)
return changegroupmod.unbundle10(self._decompress(f), 'UN')
Matt Mackall
protocol: unify client changegroup methods
r11591
Benoit Boissinot
bundle-ng: add bundlecaps argument to getbundle() command
r19201 def getbundle(self, source, heads=None, common=None, bundlecaps=None):
Peter Arrenbrecht
wireproto: add getbundle() function...
r13741 self.requirecap('getbundle', _('look up remote changes'))
opts = {}
if heads is not None:
opts['heads'] = encodelist(heads)
if common is not None:
opts['common'] = encodelist(common)
Benoit Boissinot
bundle-ng: add bundlecaps argument to getbundle() command
r19201 if bundlecaps is not None:
opts['bundlecaps'] = ','.join(bundlecaps)
Peter Arrenbrecht
wireproto: add getbundle() function...
r13741 f = self._callstream("getbundle", **opts)
return changegroupmod.unbundle10(self._decompress(f), 'UN')
Matt Mackall
protocol: unify client unbundle support...
r11592 def unbundle(self, cg, heads, source):
'''Send cg (a readable file-like object representing the
changegroup to push, typically a chunkbuffer object) to the
remote server as a bundle. Return an integer indicating the
result of the push (see localrepository.addchangegroup()).'''
Martin Geisler
wireproto: do not hash when heads == ['force']...
r14419 if heads != ['force'] and self.capable('unbundlehash'):
Shuhei Takahashi
wireproto: allow unbundle with hashed heads parameter (issue2126)...
r13942 heads = encodelist(['hashed',
util.sha1(''.join(sorted(heads))).digest()])
else:
heads = encodelist(heads)
ret, output = self._callpush("unbundle", cg, heads=heads)
Matt Mackall
protocol: unify client unbundle support...
r11592 if ret == "":
raise error.ResponseError(
_('push failed:'), output)
try:
ret = int(ret)
Brodie Rao
cleanup: remove unused variables
r12063 except ValueError:
Matt Mackall
protocol: unify client unbundle support...
r11592 raise error.ResponseError(
_('push failed (unexpected response):'), ret)
for l in output.splitlines(True):
self.ui.status(_('remote: '), l)
return ret
Peter Arrenbrecht
wireproto: add test for new optional arg missing on server...
r14048 def debugwireargs(self, one, two, three=None, four=None, five=None):
Peter Arrenbrecht
debug: add debugwireargs to test argument passing over the wire...
r13720 # don't pass optional arguments left at their default value
opts = {}
if three is not None:
opts['three'] = three
if four is not None:
opts['four'] = four
return self._call('debugwireargs', one=one, two=two, **opts)
Matt Mackall
protocol: move basic ssh client commands to wirerepository
r11586 # server side
Dirkjan Ochtman
protocol: wrap non-string protocol responses in classes
r11625 class streamres(object):
def __init__(self, gen):
self.gen = gen
class pushres(object):
def __init__(self, res):
self.res = res
Benoit Boissinot
wireproto: introduce pusherr() to deal with "unsynced changes" error...
r12703 class pusherr(object):
def __init__(self, res):
self.res = res
Andrew Pritchard
wireproto: add out-of-band error class to allow remote repo to report errors...
r15017 class ooberror(object):
def __init__(self, message):
self.message = message
Matt Mackall
protocol: introduce wireproto.py...
r11581 def dispatch(repo, proto, command):
Kevin Bullock
filtering: rename filters to their antonyms...
r18382 repo = repo.filtered("served")
Matt Mackall
protocol: introduce wireproto.py...
r11581 func, spec = commands[command]
args = proto.getargs(spec)
Dirkjan Ochtman
protocol: wrap non-string protocol responses in classes
r11625 return func(repo, proto, *args)
Matt Mackall
protocol: introduce wireproto.py...
r11581
Peter Arrenbrecht
wireproto: fix handling of '*' args for HTTP and SSH
r13721 def options(cmd, keys, others):
opts = {}
for k in keys:
if k in others:
opts[k] = others[k]
del others[k]
if others:
sys.stderr.write("abort: %s got unexpected arguments %s\n"
% (cmd, ",".join(others)))
return opts
Peter Arrenbrecht
wireproto: add batching support to wirerepository...
r14622 def batch(repo, proto, cmds, others):
Kevin Bullock
filtering: rename filters to their antonyms...
r18382 repo = repo.filtered("served")
Peter Arrenbrecht
wireproto: add batching support to wirerepository...
r14622 res = []
for pair in cmds.split(';'):
op, args = pair.split(' ', 1)
vals = {}
for a in args.split(','):
if a:
n, v = a.split('=')
vals[n] = unescapearg(v)
func, spec = commands[op]
if spec:
keys = spec.split()
data = {}
for k in keys:
if k == '*':
star = {}
for key in vals.keys():
if key not in keys:
star[key] = vals[key]
data['*'] = star
else:
data[k] = vals[k]
result = func(repo, proto, *[data[k] for k in keys])
else:
result = func(repo, proto)
Andrew Pritchard
wireproto: add out-of-band error class to allow remote repo to report errors...
r15017 if isinstance(result, ooberror):
return result
Peter Arrenbrecht
wireproto: add batching support to wirerepository...
r14622 res.append(escapearg(result))
return ';'.join(res)
Matt Mackall
protocol: add proto to method prototypes
r11583 def between(repo, proto, pairs):
Benoit Boissinot
wireproto: refactor list of nodeid encoding / decoding
r11597 pairs = [decodelist(p, '-') for p in pairs.split(" ")]
Matt Mackall
protocol: introduce wireproto.py...
r11581 r = []
for b in repo.between(pairs):
Benoit Boissinot
wireproto: refactor list of nodeid encoding / decoding
r11597 r.append(encodelist(b) + "\n")
Matt Mackall
protocol: introduce wireproto.py...
r11581 return "".join(r)
Matt Mackall
protocol: add proto to method prototypes
r11583 def branchmap(repo, proto):
Pierre-Yves David
clfilter: drop extra filtering in wireprotocol...
r18281 branchmap = repo.branchmap()
Matt Mackall
protocol: introduce wireproto.py...
r11581 heads = []
for branch, nodes in branchmap.iteritems():
Matt Mackall
branch: operate on branch names in local string space where possible...
r13047 branchname = urllib.quote(encoding.fromlocal(branch))
Benoit Boissinot
wireproto: refactor list of nodeid encoding / decoding
r11597 branchnodes = encodelist(nodes)
heads.append('%s %s' % (branchname, branchnodes))
Matt Mackall
protocol: introduce wireproto.py...
r11581 return '\n'.join(heads)
Matt Mackall
protocol: add proto to method prototypes
r11583 def branches(repo, proto, nodes):
Benoit Boissinot
wireproto: refactor list of nodeid encoding / decoding
r11597 nodes = decodelist(nodes)
Matt Mackall
protocol: introduce wireproto.py...
r11581 r = []
for b in repo.branches(nodes):
Benoit Boissinot
wireproto: refactor list of nodeid encoding / decoding
r11597 r.append(encodelist(b) + "\n")
Matt Mackall
protocol: introduce wireproto.py...
r11581 return "".join(r)
Pierre-Yves David
wireproto: extract capabilities list in outside the wireproto function...
r20774
wireprotocaps = ['lookup', 'changegroupsubset', 'branchmap', 'pushkey',
'known', 'getbundle', 'unbundlehash', 'batch']
Pierre-Yves David
wireproto: move wireproto capabilities computation in a subfunction...
r20775
def _capabilities(repo, proto):
"""return a list of capabilities for a repo
This function exists to allow extensions to easily wrap capabilities
computation
- returns a lists: easy to alter
- change done here will be propagated to both `capabilities` and `hello`
command without any other effort. without any other action needed.
"""
Pierre-Yves David
wireproto: extract capabilities list in outside the wireproto function...
r20774 # copy to prevent modification of the global list
caps = list(wireprotocaps)
Dirkjan Ochtman
protocol: move the streamclone implementation into wireproto
r11627 if _allowstream(repo.ui):
Benoit Allard
protocol: Add the stream-preferred capability...
r16361 if repo.ui.configbool('server', 'preferuncompressed', False):
caps.append('stream-preferred')
Sune Foldager
clone: only use stream when we understand the revlog format...
r12296 requiredformats = repo.requirements & repo.supportedformats
# if our local revlogs are just revlogv1, add 'stream' cap
if not requiredformats - set(('revlogv1',)):
caps.append('stream')
# otherwise, add 'streamreqs' detailing our local revlog format
else:
caps.append('streamreqs=%s' % ','.join(requiredformats))
Matt Mackall
protocol: unify server-side capabilities functions
r11594 caps.append('unbundle=%s' % ','.join(changegroupmod.bundlepriority))
Steven Brown
httprepo: long arguments support (issue2126)...
r14093 caps.append('httpheader=1024')
Pierre-Yves David
wireproto: move wireproto capabilities computation in a subfunction...
r20775 return caps
# If you are writting and extension and consider wrapping this function. Wrap
# `_capabilities` instead.
def capabilities(repo, proto):
return ' '.join(_capabilities(repo, proto))
Matt Mackall
protocol: unify server-side capabilities functions
r11594
Matt Mackall
protocol: unify changegroup commands...
r11584 def changegroup(repo, proto, roots):
Benoit Boissinot
wireproto: refactor list of nodeid encoding / decoding
r11597 nodes = decodelist(roots)
Matt Mackall
protocol: unify changegroup commands...
r11584 cg = repo.changegroup(nodes, 'serve')
Dirkjan Ochtman
protocol: wrap non-string protocol responses in classes
r11625 return streamres(proto.groupchunks(cg))
Matt Mackall
protocol: unify changegroup commands...
r11584
def changegroupsubset(repo, proto, bases, heads):
Benoit Boissinot
wireproto: refactor list of nodeid encoding / decoding
r11597 bases = decodelist(bases)
heads = decodelist(heads)
Matt Mackall
protocol: unify changegroup commands...
r11584 cg = repo.changegroupsubset(bases, heads, 'serve')
Dirkjan Ochtman
protocol: wrap non-string protocol responses in classes
r11625 return streamres(proto.groupchunks(cg))
Matt Mackall
protocol: unify changegroup commands...
r11584
Peter Arrenbrecht
wireproto: fix handling of '*' args for HTTP and SSH
r13721 def debugwireargs(repo, proto, one, two, others):
# only accept optional args from the known set
opts = options('debugwireargs', ['three', 'four'], others)
return repo.debugwireargs(one, two, **opts)
Peter Arrenbrecht
debug: add debugwireargs to test argument passing over the wire...
r13720
Peter Arrenbrecht
wireproto: add getbundle() function...
r13741 def getbundle(repo, proto, others):
Benoit Boissinot
bundle-ng: add bundlecaps argument to getbundle() command
r19201 opts = options('getbundle', ['heads', 'common', 'bundlecaps'], others)
Peter Arrenbrecht
wireproto: add getbundle() function...
r13741 for k, v in opts.iteritems():
Benoit Boissinot
bundle-ng: add bundlecaps argument to getbundle() command
r19201 if k in ('heads', 'common'):
opts[k] = decodelist(v)
elif k == 'bundlecaps':
opts[k] = set(v.split(','))
Peter Arrenbrecht
wireproto: add getbundle() function...
r13741 cg = repo.getbundle('serve', **opts)
return streamres(proto.groupchunks(cg))
Matt Mackall
protocol: add proto to method prototypes
r11583 def heads(repo, proto):
Pierre-Yves David
clfilter: drop extra filtering in wireprotocol...
r18281 h = repo.heads()
Benoit Boissinot
wireproto: refactor list of nodeid encoding / decoding
r11597 return encodelist(h) + "\n"
Matt Mackall
protocol: introduce wireproto.py...
r11581
Matt Mackall
protocol: unify server-side capabilities functions
r11594 def hello(repo, proto):
'''the hello command returns a set of lines describing various
interesting things about the server, in an RFC822-like format.
Currently the only one defined is "capabilities", which
consists of a line in the form:
capabilities: space separated list of tokens
'''
return "capabilities: %s\n" % (capabilities(repo, proto))
Matt Mackall
protocol: add proto to method prototypes
r11583 def listkeys(repo, proto, namespace):
Andreas Freimuth
wireproto: do not call pushkey module directly (issue3041)...
r15217 d = repo.listkeys(encoding.tolocal(namespace)).items()
Matt Mackall
pushkey: use UTF-8
r13050 t = '\n'.join(['%s\t%s' % (encoding.fromlocal(k), encoding.fromlocal(v))
for k, v in d])
Matt Mackall
protocol: introduce wireproto.py...
r11581 return t
Matt Mackall
protocol: add proto to method prototypes
r11583 def lookup(repo, proto, key):
Matt Mackall
protocol: introduce wireproto.py...
r11581 try:
Matt Mackall
wireproto: refuse to lookup secret csets
r15925 k = encoding.tolocal(key)
c = repo[k]
r = c.hex()
Matt Mackall
protocol: introduce wireproto.py...
r11581 success = 1
except Exception, inst:
r = str(inst)
success = 0
return "%s %s\n" % (success, r)
Peter Arrenbrecht
wireproto: enable optional args for known() for future extensibility...
r14436 def known(repo, proto, nodes, others):
Peter Arrenbrecht
wireproto: add known([id]) function...
r13723 return ''.join(b and "1" or "0" for b in repo.known(decodelist(nodes)))
Matt Mackall
protocol: add proto to method prototypes
r11583 def pushkey(repo, proto, namespace, key, old, new):
Matt Mackall
pushkey: use UTF-8
r13050 # compatibility with pre-1.8 clients which were accidentally
# sending raw binary nodes rather than utf-8-encoded hex
if len(new) == 20 and new.encode('string-escape') != new:
# looks like it could be a binary node
try:
Alexander Solovyov
remove unused imports and variables
r14064 new.decode('utf-8')
Matt Mackall
pushkey: use UTF-8
r13050 new = encoding.tolocal(new) # but cleanly decodes as UTF-8
except UnicodeDecodeError:
pass # binary, leave unmodified
else:
new = encoding.tolocal(new) # normal path
Wagner Bruna
wireproto: fix pushkey hook failure and output on remote http repo...
r17793 if util.safehasattr(proto, 'restore'):
proto.redirect()
try:
r = repo.pushkey(encoding.tolocal(namespace), encoding.tolocal(key),
encoding.tolocal(old), new) or False
except util.Abort:
r = False
output = proto.restore()
return '%s\n%s' % (int(r), output)
Andreas Freimuth
wireproto: do not call pushkey module directly (issue3041)...
r15217 r = repo.pushkey(encoding.tolocal(namespace), encoding.tolocal(key),
encoding.tolocal(old), new)
Matt Mackall
protocol: introduce wireproto.py...
r11581 return '%s\n' % int(r)
Dirkjan Ochtman
protocol: move the streamclone implementation into wireproto
r11627 def _allowstream(ui):
return ui.configbool('server', 'uncompressed', True, untrusted=True)
Durham Goode
clone: move file stream walk to a separate function...
r19176 def _walkstreamfiles(repo):
# this is it's own function so extensions can override it
return repo.store.walk()
Matt Mackall
protocol: unify stream_out command
r11585 def stream(repo, proto):
Dirkjan Ochtman
protocol: move the streamclone implementation into wireproto
r11627 '''If the server supports streaming clone, it advertises the "stream"
capability with a value representing the version and flags of the repo
it is serving. Client checks to see if it understands the format.
The format is simple: the server writes out a line with the amount
Mads Kiilerich
fix trivial spelling errors
r17424 of files, then the total amount of bytes to be transferred (separated
Dirkjan Ochtman
protocol: move the streamclone implementation into wireproto
r11627 by a space). Then, for each file, the server first writes the filename
and filesize (separated by the null character), then the file contents.
'''
if not _allowstream(repo.ui):
return '1\n'
entries = []
total_bytes = 0
try:
# get consistent snapshot of repo, lock during scan
lock = repo.lock()
try:
repo.ui.debug('scanning\n')
Durham Goode
clone: move file stream walk to a separate function...
r19176 for name, ename, size in _walkstreamfiles(repo):
Mads Kiilerich
clone: don't include empty revlogs in stream
r18381 if size:
entries.append((name, size))
total_bytes += size
Dirkjan Ochtman
protocol: move the streamclone implementation into wireproto
r11627 finally:
lock.release()
except error.LockError:
return '2\n' # error: 2
def streamer(repo, entries, total):
'''stream out all metadata files in repository.'''
yield '0\n' # success
repo.ui.debug('%d files, %d bytes to transfer\n' %
(len(entries), total_bytes))
yield '%d %d\n' % (len(entries), total_bytes)
Bryan O'Sullivan
wireproto: don't audit local paths during stream_out...
r17556
sopener = repo.sopener
oldaudit = sopener.mustaudit
Bryan O'Sullivan
wireproto: don't format a debug string inside a hot loop...
r17558 debugflag = repo.ui.debugflag
Bryan O'Sullivan
wireproto: don't audit local paths during stream_out...
r17556 sopener.mustaudit = False
try:
for name, size in entries:
Bryan O'Sullivan
wireproto: don't format a debug string inside a hot loop...
r17558 if debugflag:
repo.ui.debug('sending %s (%d bytes)\n' % (name, size))
Bryan O'Sullivan
wireproto: don't audit local paths during stream_out...
r17556 # partially encode name over the wire for backwards compat
yield '%s\0%d\n' % (store.encodedir(name), size)
Bryan O'Sullivan
wireproto: bypass filechunkiter for small files when streaming...
r17557 if size <= 65536:
Patrick Mezard
wireproto: fix check-code.py breakage introduced by 6d97dd630d79
r17567 fp = sopener(name)
try:
data = fp.read(size)
finally:
fp.close()
yield data
Bryan O'Sullivan
wireproto: bypass filechunkiter for small files when streaming...
r17557 else:
for chunk in util.filechunkiter(sopener(name), limit=size):
yield chunk
Thomas Arendsen Hein
wireproto: workaround for yield inside try/finally incompatible with python2.4
r17603 # replace with "finally:" when support for python 2.4 has been dropped
except Exception:
Bryan O'Sullivan
wireproto: don't audit local paths during stream_out...
r17556 sopener.mustaudit = oldaudit
Thomas Arendsen Hein
wireproto: workaround for yield inside try/finally incompatible with python2.4
r17603 raise
sopener.mustaudit = oldaudit
Dirkjan Ochtman
protocol: move the streamclone implementation into wireproto
r11627
return streamres(streamer(repo, entries, total_bytes))
Matt Mackall
protocol: unify stream_out command
r11585
Matt Mackall
protocol: unify unbundle on the server side
r11593 def unbundle(repo, proto, heads):
Benoit Boissinot
wireproto: refactor list of nodeid encoding / decoding
r11597 their_heads = decodelist(heads)
Matt Mackall
protocol: unify unbundle on the server side
r11593
def check_heads():
Pierre-Yves David
clfilter: drop extra filtering in wireprotocol...
r18281 heads = repo.heads()
Shuhei Takahashi
wireproto: allow unbundle with hashed heads parameter (issue2126)...
r13942 heads_hash = util.sha1(''.join(sorted(heads))).digest()
return (their_heads == ['force'] or their_heads == heads or
their_heads == ['hashed', heads_hash])
Matt Mackall
protocol: unify unbundle on the server side
r11593
Benoit Boissinot
wireproto: redirect the output earlier
r12702 proto.redirect()
Matt Mackall
protocol: unify unbundle on the server side
r11593 # fail early if possible
if not check_heads():
Mads Kiilerich
wireproto: clarify cryptic 'remote: unsynced changes' error message on push...
r18957 return pusherr('repository changed while preparing changes - '
'please try again')
Matt Mackall
protocol: unify unbundle on the server side
r11593
# write bundle data to temporary file because it can be big
fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-')
fp = os.fdopen(fd, 'wb+')
r = 0
try:
proto.getfile(fp)
lock = repo.lock()
try:
if not check_heads():
# someone else committed/pushed/unbundled while we
# were transferring data
Mads Kiilerich
wireproto: clarify cryptic 'remote: unsynced changes' error message on push...
r18957 return pusherr('repository changed while uploading changes - '
'please try again')
Matt Mackall
protocol: unify unbundle on the server side
r11593
# push can proceed
fp.seek(0)
Matt Mackall
bundle: unify/refactor unbundle/readbundle
r12042 gen = changegroupmod.readbundle(fp, None)
Matt Mackall
protocol: unify unbundle on the server side
r11593
try:
Pierre-Yves David
addchangegroup: remove the lock argument on the addchangegroup methods...
r15585 r = repo.addchangegroup(gen, 'serve', proto._client())
Matt Mackall
protocol: unify unbundle on the server side
r11593 except util.Abort, inst:
sys.stderr.write("abort: %s\n" % inst)
finally:
lock.release()
Benoit Boissinot
wireproto: return in finally was messing with the return inside the block
r12701 return pushres(r)
Matt Mackall
protocol: unify unbundle on the server side
r11593
finally:
fp.close()
os.unlink(tempname)
Matt Mackall
protocol: introduce wireproto.py...
r11581 commands = {
Peter Arrenbrecht
wireproto: add batching support to wirerepository...
r14622 'batch': (batch, 'cmds *'),
Matt Mackall
protocol: introduce wireproto.py...
r11581 'between': (between, 'pairs'),
'branchmap': (branchmap, ''),
'branches': (branches, 'nodes'),
Matt Mackall
protocol: unify server-side capabilities functions
r11594 'capabilities': (capabilities, ''),
Matt Mackall
protocol: unify changegroup commands...
r11584 'changegroup': (changegroup, 'roots'),
'changegroupsubset': (changegroupsubset, 'bases heads'),
Peter Arrenbrecht
wireproto: fix handling of '*' args for HTTP and SSH
r13721 'debugwireargs': (debugwireargs, 'one two *'),
Peter Arrenbrecht
wireproto: add getbundle() function...
r13741 'getbundle': (getbundle, '*'),
Matt Mackall
protocol: introduce wireproto.py...
r11581 'heads': (heads, ''),
Matt Mackall
protocol: unify server-side capabilities functions
r11594 'hello': (hello, ''),
Peter Arrenbrecht
wireproto: enable optional args for known() for future extensibility...
r14436 'known': (known, 'nodes *'),
Matt Mackall
protocol: introduce wireproto.py...
r11581 'listkeys': (listkeys, 'namespace'),
'lookup': (lookup, 'key'),
'pushkey': (pushkey, 'namespace key old new'),
Matt Mackall
protocol: unify stream_out command
r11585 'stream_out': (stream, ''),
Matt Mackall
protocol: unify unbundle on the server side
r11593 'unbundle': (unbundle, 'heads'),
Matt Mackall
protocol: introduce wireproto.py...
r11581 }