[PATCH RFC] repo: differentiate between repos and proxies for potentially remotes
Peter Arrenbrecht
peter.arrenbrecht at gmail.com
Sat Jun 4 07:05:14 CDT 2011
On Fri, Jun 3, 2011 at 9:41 PM, Matt Mackall <mpm at selenic.com> wrote:
> On Fri, 2011-06-03 at 20:37 +0200, Peter Arrenbrecht wrote:
>> # HG changeset patch
>> # User Peter Arrenbrecht <peter.arrenbrecht at gmail.com>
>> # Date 1307126195 -7200
>> # Node ID 063cd4fae0ee0606678ed22dbaf1c77948301662
>> # Parent 1ffeeb91c55d0b00445ceabde14a0d0faf906a33
>> repo: differentiate between repos and proxies for potentially remotes
>>
>> Currently, there is no clear distinction between working with a
>> known-to-be local repo and proxies for remote repos. So if tests don't
>> actually exercise code that could accept remote proxies with actual remote
>> repos, we won't catch improper calls.
>>
>> This patch introduces localproxy, which has basically the same contract
>> as wirerepository, but for localrepository instances. Commands which want
>> to work with possibly remote repos now have to use hg.repoproxy() instead
>> of hg.repository(). The latter newly enforces that it only returns local
>> repos.
>>
>> I also split the local proxy into localproxy and localwireproto. The
>> former only exposes the most modern methods to ensure that these are the
>> only ones used against servers supporting the most modern set.
>>
>> Then there is localwireproto which adds in the legacy commands so the
>> server can still expose them to older clients.
>>
>> The last change in sshrepo.py actually fixes a bug with new hg against
>> old ssh servers (those not supporting unbundle), I think.
>
> This is quite a big patch, is there a way we can split it up into
> smaller steps?
I guess I can do the following steps:
* sshrepo fix (should have been separate anyway)
* make all users go through repo.capable() instead of .capabilities
* turn repo.capabilities into repo._capabilities()
* factor legacy support out of localrepo into localwireproto
* introduce hg.repoproxy and make callers use it appropriately
* rename wirerepo, httprepo, sshrepo into *repoproxy (this is not
part of the current patch)
* introduce localproxy and make hg.repoproxy return it
It's quite a bit of work, so I'd rather only do this if you think it will help.
-parren
>> diff --git a/hgext/convert/hg.py b/hgext/convert/hg.py
>> --- a/hgext/convert/hg.py
>> +++ b/hgext/convert/hg.py
>> @@ -112,7 +112,7 @@
>> self.after()
>> for pbranch, heads in missings.iteritems():
>> pbranchpath = os.path.join(self.path, pbranch)
>> - prepo = hg.repository(self.ui, pbranchpath)
>> + prepo = hg.repoproxy(self.ui, pbranchpath)
>> self.ui.note(_('pulling from %s into %s\n') % (pbranch, branch))
>> self.repo.pull(prepo, [prepo.lookup(h) for h in heads])
>> self.before()
>> diff --git a/hgext/fetch.py b/hgext/fetch.py
>> --- a/hgext/fetch.py
>> +++ b/hgext/fetch.py
>> @@ -63,8 +63,8 @@
>> raise util.Abort(_('multiple heads in this branch '
>> '(use "hg heads ." and "hg merge" to merge)'))
>>
>> - other = hg.repository(hg.remoteui(repo, opts),
>> - ui.expandpath(source))
>> + other = hg.repoproxy(hg.remoteui(repo, opts),
>> + ui.expandpath(source))
>> ui.status(_('pulling from %s\n') %
>> util.hidepassword(ui.expandpath(source)))
>> revs = None
>> diff --git a/hgext/mq.py b/hgext/mq.py
>> --- a/hgext/mq.py
>> +++ b/hgext/mq.py
>> @@ -2060,24 +2060,25 @@
>> return url + '/.hg/patches'
>> if dest is None:
>> dest = hg.defaultdest(source)
>> - sr = hg.repository(hg.remoteui(ui, opts), ui.expandpath(source))
>> + sr = hg.repoproxy(hg.remoteui(ui, opts), ui.expandpath(source))
>> if opts.get('patches'):
>> patchespath = ui.expandpath(opts.get('patches'))
>> else:
>> patchespath = patchdir(sr)
>> try:
>> - hg.repository(ui, patchespath)
>> + hg.repoproxy(ui, patchespath)
>> except error.RepoError:
>> raise util.Abort(_('versioned patch repository not found'
>> ' (see init --mq)'))
>> qbase, destrev = None, None
>> if sr.local():
>> - if sr.mq.applied:
>> - qbase = sr.mq.applied[0].node
>> + repo = sr.repo
>> + if repo.mq.applied:
>> + qbase = repo.mq.applied[0].node
>> if not hg.islocal(dest):
>> - heads = set(sr.heads())
>> - destrev = list(heads.difference(sr.heads(qbase)))
>> - destrev.append(sr.changelog.parents(qbase)[0])
>> + heads = set(repo.heads())
>> + destrev = list(heads.difference(repo.heads(qbase)))
>> + destrev.append(repo.changelog.parents(qbase)[0])
>> elif sr.capable('lookup'):
>> try:
>> qbase = sr.lookup('qbase')
>> @@ -2094,13 +2095,14 @@
>> pull=opts.get('pull'), update=not opts.get('noupdate'),
>> stream=opts.get('uncompressed'))
>> if dr.local():
>> + repo = dr.repo
>> if qbase:
>> ui.note(_('stripping applied patches from destination '
>> 'repository\n'))
>> - dr.mq.strip(dr, [qbase], update=False, backup=None)
>> + repo.mq.strip(repo, [qbase], update=False, backup=None)
>> if not opts.get('noupdate'):
>> ui.note(_('updating destination repository\n'))
>> - hg.update(dr, dr.changelog.tip())
>> + hg.update(repo, repo.changelog.tip())
>>
>> @command("qcommit|qci",
>> commands.table["^commit|ci"][1],
>> diff --git a/hgext/patchbomb.py b/hgext/patchbomb.py
>> --- a/hgext/patchbomb.py
>> +++ b/hgext/patchbomb.py
>> @@ -276,7 +276,7 @@
>> dest = ui.expandpath(dest or 'default-push', dest or 'default')
>> dest, branches = hg.parseurl(dest)
>> revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
>> - other = hg.repository(hg.remoteui(repo, opts), dest)
>> + other = hg.repoproxy(hg.remoteui(repo, opts), dest)
>> ui.status(_('comparing with %s\n') % util.hidepassword(dest))
>> common, _anyinc, _heads = discovery.findcommonincoming(repo, other)
>> nodes = revs and map(repo.lookup, revs) or revs
>> diff --git a/hgext/relink.py b/hgext/relink.py
>> --- a/hgext/relink.py
>> +++ b/hgext/relink.py
>> @@ -41,8 +41,6 @@
>> src = hg.repository(hg.remoteui(repo, opts),
>> ui.expandpath(origin or 'default-relink',
>> origin or 'default'))
>> - if not src.local():
>> - raise util.Abort(_('must specify local origin repository'))
>> ui.status(_('relinking %s to %s\n') % (src.store.path, repo.store.path))
>> if repo.root == src.root:
>> ui.status(_('there is nothing to relink\n'))
>> diff --git a/hgext/transplant.py b/hgext/transplant.py
>> --- a/hgext/transplant.py
>> +++ b/hgext/transplant.py
>> @@ -128,7 +128,7 @@
>> continue
>> if pulls:
>> if source != repo:
>> - repo.pull(source, heads=pulls)
>> + repo.pull(source.proxy(), heads=pulls)
>> merge.update(repo, pulls[-1], False, False, None)
>> p1, p2 = repo.dirstate.parents()
>> pulls = []
>> @@ -173,7 +173,7 @@
>> if patchfile:
>> os.unlink(patchfile)
>> if pulls:
>> - repo.pull(source, heads=pulls)
>> + repo.pull(source.proxy(), heads=pulls)
>> merge.update(repo, pulls[-1], False, False, None)
>> finally:
>> self.saveseries(revmap, merges)
>> @@ -561,10 +561,11 @@
>>
>> sourcerepo = opts.get('source')
>> if sourcerepo:
>> - source = hg.repository(ui, ui.expandpath(sourcerepo))
>> - branches = map(source.lookup, opts.get('branch', ()))
>> - source, csets, cleanupfn = bundlerepo.getremotechanges(ui, repo, source,
>> + proxy = hg.repoproxy(hg.remoteui(ui, opts), ui.expandpath(sourcerepo))
>> + branches = map(proxy.lookup, opts.get('branch', ()))
>> + source, csets, cleanupfn = bundlerepo.getremotechanges(ui, repo, proxy,
>> onlyheads=branches, force=True)
>> + source = hasattr(source, 'localrepo') and source.localrepo() or source
>> else:
>> source = repo
>> branches = map(source.lookup, opts.get('branch', ()))
>> diff --git a/mercurial/bundlerepo.py b/mercurial/bundlerepo.py
>> --- a/mercurial/bundlerepo.py
>> +++ b/mercurial/bundlerepo.py
>> @@ -322,8 +322,8 @@
>>
>> bundle = None
>> bundlerepo = None
>> - localrepo = other
>> - if bundlename or not other.local():
>> + localrepo = other.local() and other.repo or None
>> + if bundlename or not localrepo:
>> # create a bundle (uncompressed if other repo is not local)
>>
>> if other.capable('getbundle'):
>> @@ -334,17 +334,16 @@
>> rheads = None
>> else:
>> cg = other.changegroupsubset(incoming, rheads, 'incoming')
>> - bundletype = other.local() and "HG10BZ" or "HG10UN"
>> + bundletype = localrepo and "HG10BZ" or "HG10UN"
>> fname = bundle = changegroup.writebundle(cg, bundlename, bundletype)
>> # keep written bundle?
>> if bundlename:
>> bundle = None
>> - if not other.local():
>> + if not localrepo:
>> # use the created uncompressed bundlerepo
>> localrepo = bundlerepo = bundlerepository(ui, repo.root, fname)
>> # this repo contains local and other now, so filter out local again
>> common = repo.heads()
>> -
>> csets = localrepo.changelog.findmissing(common, rheads)
>>
>> def cleanup():
>> 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
>> @@ -891,14 +891,14 @@
>> else:
>> dest = ui.expandpath(dest or 'default-push', dest or 'default')
>> dest, branches = hg.parseurl(dest, opts.get('branch'))
>> - other = hg.repository(hg.remoteui(repo, opts), dest)
>> + other = hg.repoproxy(hg.remoteui(repo, opts), dest)
>> revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
>> heads = revs and map(repo.lookup, revs) or revs
>> common, outheads = discovery.findcommonoutgoing(repo, other,
>> onlyheads=heads,
>> force=opts.get('force'))
>>
>> - cg = repo.getbundle('bundle', common=common, heads=heads)
>> + cg = repo._getbundle('bundle', common=common, heads=heads)
>> if not cg:
>> ui.status(_("no changes found\n"))
>> return 1
>> @@ -1542,16 +1542,19 @@
>> def debugdiscovery(ui, repo, remoteurl="default", **opts):
>> """runs the changeset discovery protocol in isolation"""
>> remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl), opts.get('branch'))
>> - remote = hg.repository(hg.remoteui(repo, opts), remoteurl)
>> + remote = hg.repoproxy(hg.remoteui(repo, opts), remoteurl)
>> ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
>>
>> # 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'):
>> + # re-enable legacy support
>> + remote = localrepo.localwireproto(remote.repo)
>> common, _in, hds = treediscovery.findcommonincoming(repo, remote,
>> force=True)
>> common = set(common)
>> @@ -1618,7 +1621,7 @@
>> Every ID must be a full-length hex node id string. Saves the bundle to the
>> given file.
>> """
>> - repo = hg.repository(ui, repopath)
>> + repo = hg.repoproxy(hg.remoteui(ui, opts), repopath)
>> if not repo.capable('getbundle'):
>> raise util.Abort("getbundle() not supported by target repository")
>> args = {}
>> @@ -1793,14 +1796,14 @@
>> Every ID must be a full-length hex node id string. Returns a list of 0s and 1s
>> indicating unknown/known.
>> """
>> - repo = hg.repository(ui, repopath)
>> + repo = hg.repoproxy(hg.remoteui(ui, opts), repopath)
>> if not repo.capable('known'):
>> raise util.Abort("known() not supported by target repository")
>> flags = repo.known([bin(s) for s in ids])
>> ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
>>
>> @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'))
>> -def debugpushkey(ui, repopath, namespace, *keyinfo):
>> +def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
>> '''access the pushkey key/value protocol
>>
>> With two args, list the keys in the given namespace.
>> @@ -1809,7 +1812,7 @@
>> Reports success or failure.
>> '''
>>
>> - target = hg.repository(ui, repopath)
>> + target = hg.repoproxy(hg.remoteui(ui, opts), repopath)
>> if keyinfo:
>> key, old, new = keyinfo
>> r = target.pushkey(namespace, key, old, new)
>> @@ -2106,7 +2109,7 @@
>> ] + remoteopts,
>> _('REPO [OPTIONS]... [ONE [TWO]]'))
>> def debugwireargs(ui, repopath, *vals, **opts):
>> - repo = hg.repository(hg.remoteui(ui, opts), repopath)
>> + repo = hg.repoproxy(hg.remoteui(ui, opts), repopath)
>> for opt in remoteopts:
>> del opts[opt[1]]
>> args = {}
>> @@ -2903,10 +2906,11 @@
>>
>> if source:
>> source, branches = hg.parseurl(ui.expandpath(source))
>> - repo = hg.repository(ui, source)
>> - revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
>> -
>> - if not repo.local():
>> + proxy = hg.repoproxy(ui, source)
>> + repo = proxy.local() and proxy.repo or None
>> + revs, checkout = hg.addbranchrevs(repo, proxy, branches, None)
>> +
>> + if not repo:
>> if num or branch or tags:
>> raise util.Abort(
>> _("can't query remote revision number, branch, or tags"))
>> @@ -2915,16 +2919,16 @@
>> if not rev:
>> rev = "tip"
>>
>> - remoterev = repo.lookup(rev)
>> + remoterev = proxy.lookup(rev)
>> if default or id:
>> output = [hexfunc(remoterev)]
>>
>> def getbms():
>> bms = []
>>
>> - if 'bookmarks' in repo.listkeys('namespaces'):
>> + if 'bookmarks' in proxy.listkeys('namespaces'):
>> hexremoterev = hex(remoterev)
>> - bms = [bm for bm, bmr in repo.listkeys('bookmarks').iteritems()
>> + bms = [bm for bm, bmr in proxy.listkeys('bookmarks').iteritems()
>> if bmr == hexremoterev]
>>
>> return bms
>> @@ -3188,7 +3192,7 @@
>> if opts.get('bookmarks'):
>> source, branches = hg.parseurl(ui.expandpath(source),
>> opts.get('branch'))
>> - other = hg.repository(hg.remoteui(repo, opts), source)
>> + other = hg.repoproxy(hg.remoteui(repo, opts), source)
>> if 'bookmarks' not in other.listkeys('namespaces'):
>> ui.warn(_("remote doesn't support bookmarks\n"))
>> return 0
>> @@ -3216,7 +3220,7 @@
>>
>> Returns 0 on success.
>> """
>> - hg.repository(hg.remoteui(ui, opts), ui.expandpath(dest), create=True)
>> + hg.repoproxy(hg.remoteui(ui, opts), ui.expandpath(dest), create=True)
>>
>> @command('locate',
>> [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
>> @@ -3550,7 +3554,7 @@
>> if opts.get('bookmarks'):
>> dest = ui.expandpath(dest or 'default-push', dest or 'default')
>> dest, branches = hg.parseurl(dest, opts.get('branch'))
>> - other = hg.repository(hg.remoteui(repo, opts), dest)
>> + other = hg.repoproxy(hg.remoteui(repo, opts), dest)
>> if 'bookmarks' not in other.listkeys('namespaces'):
>> ui.warn(_("remote doesn't support bookmarks\n"))
>> return 0
>> @@ -3702,7 +3706,7 @@
>> Returns 0 on success, 1 if an update had unresolved files.
>> """
>> source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
>> - other = hg.repository(hg.remoteui(repo, opts), source)
>> + other = hg.repoproxy(hg.remoteui(repo, opts), source).proxy()
>> ui.status(_('pulling from %s\n') % util.hidepassword(source))
>> revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
>>
>> @@ -3799,7 +3803,7 @@
>> dest, branches = hg.parseurl(dest, opts.get('branch'))
>> ui.status(_('pushing to %s\n') % util.hidepassword(dest))
>> revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
>> - other = hg.repository(hg.remoteui(repo, opts), dest)
>> + other = hg.repoproxy(hg.remoteui(repo, opts), dest).proxy()
>> if revs:
>> revs = [repo.lookup(rev) for rev in revs]
>>
>> @@ -4749,7 +4753,7 @@
>> if opts.get('remote'):
>> t = []
>> source, branches = hg.parseurl(ui.expandpath('default'))
>> - other = hg.repository(hg.remoteui(repo, {}), source)
>> + other = hg.repoproxy(hg.remoteui(repo, {}), source)
>> revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
>> ui.debug('comparing with %s\n' % util.hidepassword(source))
>> repo.ui.pushbuffer()
>> @@ -4762,7 +4766,7 @@
>> dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
>> revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
>> if source != dest:
>> - other = hg.repository(hg.remoteui(repo, {}), dest)
>> + other = hg.repoproxy(hg.remoteui(repo, {}), dest)
>> commoninc = None
>> ui.debug('comparing with %s\n' % util.hidepassword(dest))
>> repo.ui.pushbuffer()
>> diff --git a/mercurial/discovery.py b/mercurial/discovery.py
>> --- a/mercurial/discovery.py
>> +++ b/mercurial/discovery.py
>> @@ -186,5 +186,5 @@
>> # use the fast path, no race possible on push
>> cg = repo._changegroup(outg, 'push')
>> else:
>> - cg = repo.getbundle('push', heads=revs, common=common)
>> + cg = repo._getbundle('push', heads=revs, common=common)
>> return cg, remoteheads
>> diff --git a/mercurial/dispatch.py b/mercurial/dispatch.py
>> --- a/mercurial/dispatch.py
>> +++ b/mercurial/dispatch.py
>> @@ -595,8 +595,6 @@
>> try:
>> repo = hg.repository(ui, path=path)
>> ui = repo.ui
>> - if not repo.local():
>> - raise util.Abort(_("repository '%s' is not local") % path)
>> ui.setconfig("bundle", "mainreporoot", repo.root)
>> except error.RequirementError:
>> raise
>> diff --git a/mercurial/hg.py b/mercurial/hg.py
>> --- a/mercurial/hg.py
>> +++ b/mercurial/hg.py
>> @@ -21,6 +21,7 @@
>> return (os.path.isfile(path) and bundlerepo or localrepo)
>>
>> def addbranchrevs(lrepo, repo, branches, revs):
>> + repo = repo.proxy()
>> hashbranch, branches = branches
>> if not hashbranch and not branches:
>> return revs or None, revs and revs[0] or None
>> @@ -34,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:
>> @@ -88,7 +89,7 @@
>> return False
>> return repo.local()
>>
>> -def repository(ui, path='', create=False):
>> +def repoproxy(ui, path='', create=False):
>> """return a repository object for the specified path"""
>> repo = _lookup(path).instance(ui, path, create)
>> ui = getattr(repo, "ui", ui)
>> @@ -96,7 +97,14 @@
>> hook = getattr(module, 'reposetup', None)
>> if hook:
>> hook(ui, repo)
>> - return repo
>> + return repo.proxy()
>> +
>> +def repository(ui, path='', create=False):
>> + """return a repository object for the specified path"""
>> + proxy = repoproxy(ui, path, create)
>> + if not proxy.local():
>> + raise util.Abort(_("repository '%s' is not local") % (path or proxy.url()))
>> + return proxy.repo
>>
>> def defaultdest(source):
>> '''return default destination of clone if none is given'''
>> @@ -119,7 +127,7 @@
>> srcrepo = repository(ui, source)
>> rev, checkout = addbranchrevs(srcrepo, srcrepo, branches, None)
>> else:
>> - srcrepo = source
>> + srcrepo = source.repo
>> origsource = source = srcrepo.url()
>> checkout = None
>>
>> @@ -209,12 +217,12 @@
>> if isinstance(source, str):
>> origsource = ui.expandpath(source)
>> source, branch = parseurl(origsource, branch)
>> - srcrepo = repository(ui, source)
>> + srcproxy = repoproxy(ui, source)
>> else:
>> - srcrepo = source
>> + srcproxy = source.proxy()
>> branch = (None, branch or [])
>> - origsource = source = srcrepo.url()
>> - rev, checkout = addbranchrevs(srcrepo, srcrepo, branch, rev)
>> + origsource = source = srcproxy.url()
>> + rev, checkout = addbranchrevs(srcproxy, srcproxy, branch, rev)
>>
>> if dest is None:
>> dest = defaultdest(source)
>> @@ -251,10 +259,11 @@
>> dircleanup = DirCleanup(dest)
>>
>> copy = False
>> - if srcrepo.cancopy() and islocal(dest):
>> + if srcproxy.cancopy() and islocal(dest):
>> copy = not pull and not rev
>>
>> if copy:
>> + srcrepo = srcproxy.repo
>> try:
>> # we use a lock here because if we race with commit, we
>> # can end up with extra data in the cloned revlogs that's
>> @@ -303,12 +312,12 @@
>>
>> # we need to re-init the repo after manually copying the data
>> # into it
>> - destrepo = repository(ui, dest)
>> + destproxy = repoproxy(ui, dest)
>> srcrepo.hook('outgoing', source='clone',
>> node=node.hex(node.nullid))
>> else:
>> try:
>> - destrepo = repository(ui, dest, create=True)
>> + destproxy = repoproxy(ui, dest, create=True)
>> except OSError, inst:
>> if inst.errno == errno.EEXIST:
>> dircleanup.close()
>> @@ -318,23 +327,24 @@
>>
>> revs = None
>> if rev:
>> - if 'lookup' not in srcrepo.capabilities:
>> + if not srcproxy.capable('lookup'):
>> raise util.Abort(_("src repository does not support "
>> "revision lookup and so doesn't "
>> "support clone by revision"))
>> - revs = [srcrepo.lookup(r) for r in rev]
>> + revs = [srcproxy.lookup(r) for r in rev]
>> checkout = revs[0]
>> - if destrepo.local():
>> - destrepo.clone(srcrepo, heads=revs, stream=stream)
>> - elif srcrepo.local():
>> - srcrepo.push(destrepo, revs=revs)
>> + if destproxy.local():
>> + destproxy.repo.clone(srcproxy, heads=revs, stream=stream)
>> + elif srcproxy.local():
>> + srcproxy.repo.push(destproxy, revs=revs)
>> else:
>> raise util.Abort(_("clone from remote to remote not supported"))
>>
>> if dircleanup:
>> dircleanup.close()
>>
>> - if destrepo.local():
>> + if destproxy.local():
>> + destrepo = destproxy.repo
>> fp = destrepo.opener("hgrc", "w", text=True)
>> fp.write("[paths]\n")
>> fp.write("default = %s\n" % abspath)
>> @@ -345,8 +355,8 @@
>> if update:
>> if update is not True:
>> checkout = update
>> - if srcrepo.local():
>> - checkout = srcrepo.lookup(update)
>> + if srcproxy.local(): # FIXME why this test?
>> + checkout = srcproxy.repo.lookup(update)
>> for test in (checkout, 'default', 'tip'):
>> if test is None:
>> continue
>> @@ -360,8 +370,9 @@
>> _update(destrepo, uprev)
>>
>> # clone all bookmarks
>> - if destrepo.local() and srcrepo.capable("pushkey"):
>> - rb = srcrepo.listkeys('bookmarks')
>> + if destproxy.local() and srcproxy.capable("pushkey"):
>> + destrepo = destproxy.repo
>> + rb = srcproxy.listkeys('bookmarks')
>> for k, n in rb.iteritems():
>> try:
>> m = destrepo.lookup(n)
>> @@ -370,11 +381,12 @@
>> pass
>> if rb:
>> bookmarks.write(destrepo)
>> - elif srcrepo.local() and destrepo.capable("pushkey"):
>> + elif srcproxy.local() and destproxy.capable("pushkey"):
>> + srcrepo = srcproxy.repo
>> for k, n in srcrepo._bookmarks.iteritems():
>> - destrepo.pushkey('bookmarks', k, '', hex(n))
>> + destproxy.pushkey('bookmarks', k, '', hex(n))
>>
>> - return srcrepo, destrepo
>> + return srcproxy, destproxy
>> finally:
>> release(srclock, destlock)
>> if dircleanup is not None:
>> @@ -423,7 +435,7 @@
>> and is supposed to contain only code that can't be unified.
>> """
>> source, branches = parseurl(ui.expandpath(source), opts.get('branch'))
>> - other = repository(remoteui(repo, opts), source)
>> + other = repoproxy(remoteui(repo, opts), source)
>> ui.status(_('comparing with %s\n') % util.hidepassword(source))
>> revs, checkout = addbranchrevs(repo, other, branches, opts.get('rev'))
>>
>> @@ -481,7 +493,7 @@
>> if revs:
>> revs = [repo.lookup(rev) for rev in revs]
>>
>> - other = repository(remoteui(repo, opts), dest)
>> + other = repoproxy(remoteui(repo, opts), dest)
>> common, outheads = discovery.findcommonoutgoing(repo, other, revs,
>> force=opts.get('force'))
>> o = repo.changelog.findmissing(common, outheads)
>> diff --git a/mercurial/httprepo.py b/mercurial/httprepo.py
>> --- a/mercurial/httprepo.py
>> +++ b/mercurial/httprepo.py
>> @@ -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'))
>>
>> diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py
>> --- a/mercurial/localrepo.py
>> +++ b/mercurial/localrepo.py
>> @@ -18,9 +18,134 @@
>> import weakref, errno, os, time, inspect
>> propertycache = util.propertycache
>>
>> +MODERNCAPS = set(('lookup', 'branchmap', 'pushkey', 'known', 'getbundle'))
>> +LEGACYCAPS = MODERNCAPS.union(set(['changegroupsubset']))
>> +
>> +class localproxy(repo.repositoryproxy):
>> + '''proxy for a local repo; reflects only the most recent API'''
>> +
>> + def capabilities(self):
>> + return MODERNCAPS
>> +
>> + def __init__(self, repo):
>> + self.repo = repo
>> + self.ui = repo.ui
>> +
>> + def localrepo(self):
>> + return self.repo
>> +
>> + def close(self):
>> + self.repo.close()
>> +
>> + 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):
>> + nm = self.repo.changelog.nodemap
>> + return [(n in nm) for n in nodes]
>> +
>> + def getbundle(self, source, heads=None, common=None):
>> + """Like changegroupsubset, but returns the set difference between the
>> + ancestors of heads and the ancestors common.
>> +
>> + If heads is None, use the local heads. If common is None, use [nullid].
>> +
>> + The nodes in common might not all be known locally due to the way the
>> + current discovery protocol works.
>> + """
>> + return self.repo._getbundle(source, heads=heads, common=common)
>> +
>> + # FIXME We might want to lose these 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 localwireproto(localproxy):
>> + '''proxy extension for use by wireproto; implements legacy methods too'''
>> +
>> + def __init__(self, repo):
>> + localproxy.__init__(self, repo)
>> + self.requirements = repo.requirements
>> + self.supportedformats = repo.supportedformats
>> +
>> + def capabilities(self):
>> + return self.repo._capabilities()
>> +
>> + def branches(self, nodes):
>> + cl = self.repo.changelog
>> + if not nodes:
>> + nodes = [cl.tip()]
>> + b = []
>> + for n in nodes:
>> + t = n
>> + while True:
>> + p = cl.parents(n)
>> + if p[1] != nullid or p[0] == nullid:
>> + b.append((t, n, p[0], p[1]))
>> + break
>> + n = p[0]
>> + return b
>> +
>> + def between(self, pairs):
>> + cl = self.repo.changelog
>> + r = []
>> + for top, bottom in pairs:
>> + n, l, i = top, [], 0
>> + f = 1
>> + while n != bottom and n != nullid:
>> + p = cl.parents(n)[0]
>> + if i == f:
>> + l.append(n)
>> + f = f * 2
>> + n = p
>> + i += 1
>> + r.append(l)
>> + return r
>> +
>> + def changegroup(self, nodes, kind):
>> + return self.repo.changegroup(nodes, kind)
>> +
>> + def changegroupsubset(self, bases, heads, kind):
>> + return self.repo.changegroupsubset(bases, heads, kind)
>> +
>> + def pushkey(self, namespace, key, old, new):
>> + # don't run hooks for server-side calls
>> + return pushkey.push(self.repo, namespace, key, old, new)
>> +
>> + def listkeys(self, namespace):
>> + # don't run hooks for server-side calls
>> + return pushkey.list(self.repo, namespace)
>> +
>> class localrepository(repo.repository):
>> - capabilities = set(('lookup', 'changegroupsubset', 'branchmap', 'pushkey',
>> - 'known', 'getbundle'))
>> +
>> supportedformats = set(('revlogv1', 'generaldelta'))
>> supported = supportedformats | set(('store', 'fncache', 'shared',
>> 'dotencode'))
>> @@ -122,6 +247,9 @@
>> reqfile.write("%s\n" % r)
>> reqfile.close()
>>
>> + def _capabilities(self):
>> + return LEGACYCAPS
>> +
>> def _checknested(self, path):
>> """Determine if path is a legal nested repository."""
>> if not path.startswith(self.root):
>> @@ -159,6 +287,11 @@
>> parts.pop()
>> return False
>>
>> + def proxy(self):
>> + if not hasattr(self, '_proxy'):
>> + self._proxy = localproxy(self)
>> + return self._proxy
>> +
>> @util.propertycache
>> def _bookmarks(self):
>> return bookmarks.read(self)
>> @@ -558,13 +691,12 @@
>> repo = (remote and remote.local()) and remote or self
>> return repo[key].branch()
>>
>> - def known(self, nodes):
>> - nm = self.changelog.nodemap
>> - return [(n in nm) for n in nodes]
>> -
>> 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)
>>
>> @@ -1303,39 +1435,6 @@
>> ('close' not in self.changelog.read(h)[5])]
>> return bheads
>>
>> - def branches(self, nodes):
>> - if not nodes:
>> - nodes = [self.changelog.tip()]
>> - b = []
>> - for n in nodes:
>> - t = n
>> - while True:
>> - p = self.changelog.parents(n)
>> - if p[1] != nullid or p[0] == nullid:
>> - b.append((t, n, p[0], p[1]))
>> - break
>> - n = p[0]
>> - return b
>> -
>> - def between(self, pairs):
>> - r = []
>> -
>> - for top, bottom in pairs:
>> - n, l, i = top, [], 0
>> - f = 1
>> -
>> - while n != bottom and n != nullid:
>> - p = self.changelog.parents(n)[0]
>> - if i == f:
>> - l.append(n)
>> - f = f * 2
>> - n = p
>> - i += 1
>> -
>> - r.append(l)
>> -
>> - return r
>> -
>> def pull(self, remote, heads=None, force=False):
>> lock = self.lock()
>> try:
>> @@ -1468,15 +1567,7 @@
>> common = set(cl.ancestors(*[cl.rev(n) for n in bases]))
>> return self._changegroupsubset(common, csets, heads, source)
>>
>> - def getbundle(self, source, heads=None, common=None):
>> - """Like changegroupsubset, but returns the set difference between the
>> - ancestors of heads and the ancestors common.
>> -
>> - If heads is None, use the local heads. If common is None, use [nullid].
>> -
>> - The nodes in common might not all be known locally due to the way the
>> - current discovery protocol works.
>> - """
>> + def _getbundle(self, source, heads=None, common=None):
>> cl = self.changelog
>> if common:
>> nm = cl.nodemap
>> @@ -1958,10 +2049,6 @@
>> self.hook('listkeys', namespace=namespace, values=values)
>> return values
>>
>> - 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)
>> -
>> # used to avoid circular references so destructors work
>> def aftertrans(files):
>> renamefiles = [tuple(t) for t in files]
>> diff --git a/mercurial/repo.py b/mercurial/repo.py
>> --- a/mercurial/repo.py
>> +++ b/mercurial/repo.py
>> @@ -10,15 +10,30 @@
>> import error
>>
>> class repository(object):
>> +
>> + # def proxy(self):
>> +
>> + def close(self):
>> + pass
>> +
>> +class repositoryproxy(object):
>> +
>> + def proxy(self):
>> + return self
>> +
>> + def close(self):
>> + pass
>> +
>> def capable(self, name):
>> '''tell whether repo supports named capability.
>> 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
>> @@ -36,5 +51,6 @@
>> def cancopy(self):
>> return self.local()
>>
>> - def close(self):
>> - pass
>> + # def heads(self):
>> + # def known(self, nodes):
>> + # ...
>> diff --git a/mercurial/revset.py b/mercurial/revset.py
>> --- a/mercurial/revset.py
>> +++ b/mercurial/revset.py
>> @@ -599,7 +599,7 @@
>> revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
>> if revs:
>> revs = [repo.lookup(rev) for rev in revs]
>> - other = hg.repository(hg.remoteui(repo, {}), dest)
>> + other = hg.repoproxy(hg.remoteui(repo, {}), dest)
>> repo.ui.pushbuffer()
>> common, outheads = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
>> repo.ui.popbuffer()
>> diff --git a/mercurial/sshrepo.py b/mercurial/sshrepo.py
>> --- a/mercurial/sshrepo.py
>> +++ b/mercurial/sshrepo.py
>> @@ -81,12 +81,15 @@
>> else:
>> self._abort(error.RepoError(_("no suitable response from remote hg")))
>>
>> - self.capabilities = set()
>> + self._capabilities = set()
>> for l in reversed(lines):
>> if l.startswith("capabilities:"):
>> - self.capabilities.update(l[:-1].split(":")[1].split())
>> + self._capabilities.update(l[:-1].split(":")[1].split())
>> break
>>
>> + def capabilities(self):
>> + return self._capabilities
>> +
>> def readerr(self):
>> while True:
>> size = util.fstat(self.pipee).st_size
>> @@ -186,7 +189,7 @@
>> def unlock(self):
>> self._call("unlock")
>>
>> - def addchangegroup(self, cg, source, url):
>> + def addchangegroup(self, cg, source, url, lock=None):
>> '''Send a changegroup to the remote server. Return an integer
>> similar to unbundle(). DEPRECATED, since it requires locking the
>> remote.'''
>> diff --git a/mercurial/statichttprepo.py b/mercurial/statichttprepo.py
>> --- a/mercurial/statichttprepo.py
>> +++ b/mercurial/statichttprepo.py
>> @@ -124,7 +124,9 @@
>> self._branchcachetip = None
>> self.encodepats = None
>> self.decodepats = None
>> - self.capabilities = self.capabilities.difference(["pushkey"])
>> +
>> + def _capabilities(self):
>> + return localrepo.localrepository._capabilities(self).difference(["pushkey"])
>>
>> def url(self):
>> return self._url
>> diff --git a/mercurial/subrepo.py b/mercurial/subrepo.py
>> --- a/mercurial/subrepo.py
>> +++ b/mercurial/subrepo.py
>> @@ -437,14 +437,15 @@
>> if revision not in self._repo:
>> self._repo._subsource = source
>> srcurl = _abssource(self._repo)
>> - other = hg.repository(self._repo.ui, srcurl)
>> + other = hg.repoproxy(self._repo.ui, srcurl)
>> if len(self._repo) == 0:
>> self._repo.ui.status(_('cloning subrepo %s from %s\n')
>> % (subrelpath(self), srcurl))
>> parentrepo = self._repo._subparent
>> shutil.rmtree(self._repo.root)
>> - other, self._repo = hg.clone(self._repo._subparent.ui, other,
>> - self._repo.root, update=False)
>> + other, cloned = hg.clone(self._repo._subparent.ui, other,
>> + self._repo.root, update=False)
>> + self._repo = cloned.repo
>> self._initrepo(parentrepo, source, create=True)
>> else:
>> self._repo.ui.status(_('pulling subrepo %s from %s\n')
>> @@ -495,7 +496,7 @@
>> dsturl = _abssource(self._repo, True)
>> self._repo.ui.status(_('pushing subrepo %s to %s\n') %
>> (subrelpath(self), dsturl))
>> - other = hg.repository(self._repo.ui, dsturl)
>> + other = hg.repoproxy(self._repo.ui, dsturl)
>> return self._repo.push(other, force)
>>
>> def outgoing(self, ui, dest, opts):
>> 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 repo, localrepo, error, encoding, util, store
>> import pushkey as pushkeymod
>>
>> # list of nodes encoding / decoding
>> @@ -24,7 +24,7 @@
>>
>> # client side
>>
>> -class wirerepository(repo.repository):
>> +class wirerepository(repo.repositoryproxy):
>> def lookup(self, key):
>> self.requirecap('lookup', _('look up remote revision'))
>> d = self._call("lookup", key=encoding.fromlocal(key))
>> @@ -185,7 +185,8 @@
>> def dispatch(repo, proto, command):
>> func, spec = commands[command]
>> args = proto.getargs(spec)
>> - return func(repo, proto, *args)
>> + proxy = localrepo.localwireproto(repo)
>> + return func(proxy, proto, *args)
>>
>> def options(cmd, keys, others):
>> opts = {}
>> @@ -274,7 +275,7 @@
>> return "capabilities: %s\n" % (capabilities(repo, proto))
>>
>> def listkeys(repo, proto, namespace):
>> - d = pushkeymod.list(repo, encoding.tolocal(namespace)).items()
>> + d = repo.listkeys(encoding.tolocal(namespace)).items()
>> t = '\n'.join(['%s\t%s' % (encoding.fromlocal(k), encoding.fromlocal(v))
>> for k, v in d])
>> return t
>> @@ -304,9 +305,8 @@
>> else:
>> new = encoding.tolocal(new) # normal path
>>
>> - r = pushkeymod.push(repo,
>> - encoding.tolocal(namespace), encoding.tolocal(key),
>> - encoding.tolocal(old), new)
>> + r = repo.pushkey(encoding.tolocal(namespace), encoding.tolocal(key),
>> + encoding.tolocal(old), new)
>> return '%s\n' % int(r)
>>
>> def _allowstream(ui):
>> @@ -326,6 +326,7 @@
>> if not _allowstream(repo.ui):
>> return '1\n'
>>
>> + repo = repo.repo # undo the localwireproto wrapper
>> entries = []
>> total_bytes = 0
>> try:
>> diff --git a/tests/notcapable b/tests/notcapable
>> --- a/tests/notcapable
>> +++ b/tests/notcapable
>> @@ -6,13 +6,20 @@
>> fi
>>
>> cat > notcapable-$CAP.py << EOF
>> -from mercurial import extensions, repo
>> +from mercurial import extensions, repo, localrepo
>> def extsetup():
>> - extensions.wrapfunction(repo.repository, 'capable', wrapper)
>> + extensions.wrapfunction(repo.repositoryproxy, 'capable', wrapper)
>> + extensions.wrapfunction(localrepo.localrepository, 'proxy', proxy)
>> def wrapper(orig, self, name, *args, **kwargs):
>> if name in '$CAP'.split(' '):
>> return False
>> return orig(self, name, *args, **kwargs)
>> +def proxy(orig, self):
>> + # Since we're disabling some newer features, we need to make sure local
>> + # proxies add in the legacy features again.
>> + if not hasattr(self, '_proxy'):
>> + self._proxy = localrepo.localwireproto(self)
>> + return self._proxy
>> EOF
>>
>> echo '[extensions]' >> $HGRCPATH
>
>
> --
> Mathematics is the supreme nostalgia of our time.
>
>
>
More information about the Mercurial-devel
mailing list