[PATCH 6 of 6] peer: introduce peer and local/http/ssh-peer
Peter Arrenbrecht
peter.arrenbrecht at gmail.com
Wed Jun 8 11:45:33 CDT 2011
# HG changeset patch
# User Peter Arrenbrecht <peter.arrenbrecht at gmail.com>
# Date 1307551088 -7200
peer: introduce peer and local/http/ssh-peer
This change actually separates peer implementations from the repository
implementation. localpeer currently is a simple pass-through to
localrepository, except for legacy calls, which have already been
removed from localpeer. This ensures that the local client code only
uses the most modern peer API when talking to local repos.
diff --git a/mercurial/commands.py b/mercurial/commands.py
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -10,7 +10,7 @@
from i18n import _, gettext
import os, re, sys, difflib, time, tempfile, errno
import hg, scmutil, util, revlog, extensions, copies, error, bookmarks
-import patch, help, url, encoding, templatekw, discovery
+import patch, help, url, encoding, templatekw, discovery, localrepo
import archival, changegroup, cmdutil, sshserver, hbisect, hgweb, hgweb.server
import merge as mergemod
import minirst, revset, fileset
@@ -1548,10 +1548,13 @@
# make sure tests are repeatable
random.seed(12323)
- def doit(localheads, remoteheads):
+ def doit(localheads, remoteheads, remote=remote):
if opts.get('old'):
if localheads:
raise util.Abort('cannot use localheads with old style discovery')
+ if not hasattr(remote, 'branches'):
+ # enable in-client legacy support
+ remote = localrepo.locallegacypeer(remote.repo())
common, _in, hds = treediscovery.findcommonincoming(repo, remote,
force=True)
common = set(common)
diff --git a/mercurial/hg.py b/mercurial/hg.py
--- a/mercurial/hg.py
+++ b/mercurial/hg.py
@@ -9,7 +9,7 @@
from i18n import _
from lock import release
from node import hex, nullid
-import localrepo, bundlerepo, httprepo, sshrepo, statichttprepo, bookmarks
+import localrepo, bundlerepo, httppeer, sshpeer, statichttprepo, bookmarks
import lock, util, extensions, error, node
import cmdutil, discovery
import merge as mergemod
@@ -35,7 +35,7 @@
def primary(branch):
if branch == '.':
- if not lrepo or not lrepo.local():
+ if not lrepo:
raise util.Abort(_("dirstate branch not accessible"))
branch = lrepo.dirstate.branch()
if branch in branchmap:
@@ -65,9 +65,9 @@
schemes = {
'bundle': bundlerepo,
'file': _local,
- 'http': httprepo,
- 'https': httprepo,
- 'ssh': sshrepo,
+ 'http': httppeer,
+ 'https': httppeer,
+ 'ssh': sshpeer,
'static-http': statichttprepo,
}
@@ -130,7 +130,7 @@
def peer(ui, opts, url, create=False):
"""return a repository peer for the specified url"""
- return _repoorpeer(_peerui(ui, opts), url, create)
+ return _repoorpeer(_peerui(ui, opts), url, create).peer()
def repository(ui, path='', create=False):
"""return a repository object for the specified path"""
diff --git a/mercurial/httprepo.py b/mercurial/httppeer.py
rename from mercurial/httprepo.py
rename to mercurial/httppeer.py
--- a/mercurial/httprepo.py
+++ b/mercurial/httppeer.py
@@ -1,4 +1,4 @@
-# httprepo.py - HTTP repository proxy classes for mercurial
+# httppeer.py - HTTP repository proxy classes for mercurial
#
# Copyright 2005, 2006 Matt Mackall <mpm at selenic.com>
# Copyright 2006 Vadim Gelfer <vadim.gelfer at gmail.com>
@@ -23,7 +23,7 @@
raise IOError(None, _('connection ended unexpectedly'))
yield zd.flush()
-class httprepository(wireproto.wirerepository):
+class httppeer(wireproto.wirepeer):
def __init__(self, ui, path):
self.path = path
self.caps = None
@@ -55,7 +55,7 @@
def _fetchcaps(self):
self.caps = set(self._call('capabilities').split())
- def get_caps(self):
+ def _capabilities(self):
if self.caps is None:
try:
self._fetchcaps()
@@ -65,8 +65,6 @@
(' '.join(self.caps or ['none'])))
return self.caps
- capabilities = property(get_caps)
-
def lock(self):
raise util.Abort(_('operation not supported over http'))
@@ -206,21 +204,21 @@
def _decompress(self, stream):
return util.chunkbuffer(zgenerator(stream))
-class httpsrepository(httprepository):
+class httpspeer(httppeer):
def __init__(self, ui, path):
if not url.has_https:
raise util.Abort(_('Python support for SSL and HTTPS '
'is not installed'))
- httprepository.__init__(self, ui, path)
+ httppeer.__init__(self, ui, path)
def instance(ui, path, create):
if create:
raise util.Abort(_('cannot create new http repository'))
try:
if path.startswith('https:'):
- inst = httpsrepository(ui, path)
+ inst = httpspeer(ui, path)
else:
- inst = httprepository(ui, path)
+ inst = httppeer(ui, path)
try:
# Try to do useful work when checking compatibility.
# Usually saves a roundtrip since we want the caps anyway.
diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py
--- a/mercurial/localrepo.py
+++ b/mercurial/localrepo.py
@@ -7,7 +7,7 @@
from node import bin, hex, nullid, nullrev, short
from i18n import _
-import repo, changegroup, subrepo, discovery, pushkey
+import peer, changegroup, subrepo, discovery, pushkey
import changelog, dirstate, filelog, manifest, context, bookmarks
import lock, transaction, store, encoding
import scmutil, util, extensions, hook, error
@@ -18,15 +18,98 @@
import weakref, errno, os, time, inspect
propertycache = util.propertycache
-class localrepository(repo.repository):
- capabilities = set(('lookup', 'changegroupsubset', 'branchmap', 'pushkey',
- 'known', 'getbundle'))
+MODERNCAPS = set(('lookup', 'branchmap', 'pushkey', 'known', 'getbundle'))
+LEGACYCAPS = MODERNCAPS.union(set(['changegroupsubset']))
+
+class localpeer(peer.peerrepository):
+ '''peer for a local repo; reflects only the most recent API'''
+
+ def __init__(self, repo, caps=MODERNCAPS):
+ peer.peerrepository.__init__(self)
+ self._repo = repo
+ self.ui = repo.ui
+ self._caps = repo._restrictcapabilities(caps)
+ self.requirements = repo.requirements
+ self.supportedformats = repo.supportedformats
+
+ def repo(self):
+ return self._repo
+
+ def close(self):
+ self._repo.close()
+
+ def _capabilities(self):
+ return self._caps
+
+ def local(self):
+ return self._repo.local() # so statichttprepo can override
+
+ def cancopy(self):
+ return self._repo.cancopy() # so bundlerepo can override
+
+ def url(self):
+ return self._repo.url()
+
+ def lookup(self, key):
+ return self._repo.lookup(key)
+
+ def branchmap(self):
+ return self._repo.branchmap()
+
+ def heads(self):
+ return self._repo.heads()
+
+ def known(self, nodes):
+ return self._repo.known(nodes)
+
+ def getbundle(self, source, heads=None, common=None):
+ return self._repo.getbundle(source, heads=heads, common=common)
+
+ # TODO We might want to move the next two calls into legacypeer and add
+ # unbundle instead.
+
+ def lock(self):
+ return self._repo.lock()
+
+ def addchangegroup(self, cg, source, url, lock=None):
+ return self._repo.addchangegroup(cg, source, url, lock=lock)
+
+ def pushkey(self, namespace, key, old, new):
+ return self._repo.pushkey(namespace, key, old, new)
+
+ def listkeys(self, namespace):
+ return self._repo.listkeys(namespace)
+
+ def debugwireargs(self, one, two, three=None, four=None, five=None):
+ '''used to test argument passing over the wire'''
+ return "%s %s %s %s %s" % (one, two, three, four, five)
+
+class locallegacypeer(localpeer):
+ '''peer extension which implements legacy methods too; used for tests with
+ restricted capabilities'''
+
+ def __init__(self, repo):
+ localpeer.__init__(self, repo, caps=LEGACYCAPS)
+
+ def branches(self, nodes):
+ return self._repo.branches(nodes)
+
+ def between(self, pairs):
+ return self._repo.between(pairs)
+
+ def changegroup(self, basenodes, source):
+ return self._repo.changegroup(basenodes, source)
+
+ def changegroupsubset(self, bases, heads, source):
+ return self._repo.changegroupsubset(bases, heads, source)
+
+class localrepository(object):
+
supportedformats = set(('revlogv1', 'generaldelta'))
supported = supportedformats | set(('store', 'fncache', 'shared',
'dotencode'))
def __init__(self, baseui, path=None, create=False):
- repo.repository.__init__(self)
self.root = os.path.realpath(util.expandpath(path))
self.path = os.path.join(self.root, ".hg")
self.origroot = path
@@ -110,6 +193,15 @@
self._datafilters = {}
self._transref = self._lockref = self._wlockref = None
+ def repo(self):
+ return self
+
+ def close(self):
+ pass
+
+ def _restrictcapabilities(self, caps):
+ return caps
+
def _applyrequirements(self, requirements):
self.requirements = requirements
openerreqs = set(('revlogv1', 'generaldelta'))
@@ -159,6 +251,9 @@
parts.pop()
return False
+ def peer(self):
+ return localpeer(self) # not cached to avoid reference cycle
+
@util.propertycache
def _bookmarks(self):
return bookmarks.read(self)
@@ -565,6 +660,9 @@
def local(self):
return True
+ def cancopy(self):
+ return self.local() # so statichttprepo's override of local() works
+
def join(self, f):
return os.path.join(self.path, f)
diff --git a/mercurial/repo.py b/mercurial/peer.py
rename from mercurial/repo.py
rename to mercurial/peer.py
--- a/mercurial/repo.py
+++ b/mercurial/peer.py
@@ -9,14 +9,9 @@
from i18n import _
import error
-class repository(object):
-
- def repo(self):
- '''return peer as a repository - currently a no-op'''
- return self
+class peerrepository(object):
def peer(self):
- '''return the peer view of this repo - currently a no-op'''
return self
def capable(self, name):
@@ -24,10 +19,11 @@
return False if not supported.
if boolean capability, return True.
if string capability, return string.'''
- if name in self.capabilities:
+ caps = self._capabilities()
+ if name in caps:
return True
name_eq = name + '='
- for cap in self.capabilities:
+ for cap in caps:
if cap.startswith(name_eq):
return cap[len(name_eq):]
return False
@@ -43,7 +39,7 @@
return False
def cancopy(self):
- return self.local()
+ return False
def close(self):
pass
diff --git a/mercurial/sshrepo.py b/mercurial/sshpeer.py
rename from mercurial/sshrepo.py
rename to mercurial/sshpeer.py
--- a/mercurial/sshrepo.py
+++ b/mercurial/sshpeer.py
@@ -18,7 +18,7 @@
if self.repo:
self.release()
-class sshrepository(wireproto.wirerepository):
+class sshpeer(wireproto.wirepeer):
def __init__(self, ui, path, create=False):
self._url = path
self.ui = ui
@@ -81,12 +81,15 @@
else:
self._abort(error.RepoError(_("no suitable response from remote hg")))
- self.capabilities = set()
+ self._caps = set()
for l in reversed(lines):
if l.startswith("capabilities:"):
- self.capabilities.update(l[:-1].split(":")[1].split())
+ self._caps.update(l[:-1].split(":")[1].split())
break
+ def _capabilities(self):
+ return self._caps
+
def readerr(self):
while True:
size = util.fstat(self.pipee).st_size
@@ -211,4 +214,4 @@
except ValueError:
self._abort(error.ResponseError(_("unexpected response:"), r))
-instance = sshrepository
+instance = sshpeer
diff --git a/mercurial/statichttprepo.py b/mercurial/statichttprepo.py
--- a/mercurial/statichttprepo.py
+++ b/mercurial/statichttprepo.py
@@ -115,6 +115,7 @@
self.spath = self.store.path
self.sopener = self.store.opener
self.sjoin = self.store.join
+ self.requirements = requirements
self.manifest = manifest.manifest(self.sopener)
self.changelog = changelog.changelog(self.sopener)
@@ -124,7 +125,9 @@
self._branchcachetip = None
self.encodepats = None
self.decodepats = None
- self.capabilities.difference_update(["pushkey"])
+
+ def _restrictcapabilities(self, caps):
+ return caps.difference(["pushkey"])
def url(self):
return self._url
diff --git a/mercurial/wireproto.py b/mercurial/wireproto.py
--- a/mercurial/wireproto.py
+++ b/mercurial/wireproto.py
@@ -9,7 +9,7 @@
from i18n import _
from node import bin, hex
import changegroup as changegroupmod
-import repo, error, encoding, util, store
+import peer, error, encoding, util, store
import pushkey as pushkeymod
# list of nodes encoding / decoding
@@ -24,7 +24,8 @@
# client side
-class wirerepository(repo.repository):
+class wirepeer(peer.peerrepository):
+
def lookup(self, key):
self.requirecap('lookup', _('look up remote revision'))
d = self._call("lookup", key=encoding.fromlocal(key))
diff --git a/tests/notcapable b/tests/notcapable
--- a/tests/notcapable
+++ b/tests/notcapable
@@ -6,13 +6,18 @@
fi
cat > notcapable-$CAP.py << EOF
-from mercurial import extensions, repo
+from mercurial import extensions, peer, localrepo
def extsetup():
- extensions.wrapfunction(repo.repository, 'capable', wrapper)
-def wrapper(orig, self, name, *args, **kwargs):
+ extensions.wrapfunction(peer.peerrepository, 'capable', wrapcapable)
+ extensions.wrapfunction(localrepo.localrepository, 'peer', wrappeer)
+def wrapcapable(orig, self, name, *args, **kwargs):
if name in '$CAP'.split(' '):
return False
return orig(self, name, *args, **kwargs)
+def wrappeer(orig, self):
+ # Since we're disabling some newer features, we need to make sure local
+ # repos add in the legacy features again.
+ return localrepo.locallegacypeer(self)
EOF
echo '[extensions]' >> $HGRCPATH
More information about the Mercurial-devel
mailing list