##// END OF EJS Templates
exchangev2: fetch manifest revisions...
exchangev2: fetch manifest revisions Now that the server has support for retrieving manifest data, we can implement the client bits to call it. We teach the changeset fetching code to capture the manifest revisions that are encountered on incoming changesets. We then feed this into a new function which filters out known manifests and then batches up manifest data requests to the server. This is different from the previous wire protocol in a few notable ways. First, the client fetches manifest data separately and explicitly. Before, we'd ask the server for data pertaining to some changesets (via a "getbundle" command) and manifests (and files) would be sent automatically. Providing an API for looking up just manifest data separately gives clients much more flexibility for manifest management. For example, a client may choose to only fetch manifest data on demand instead of prefetching it (i.e. partial clone). Second, we send N commands to the server for manifest retrieval instead of 1. This property has a few nice side-effects. One is that the deterministic nature of the requests lends itself to server-side caching. For example, say the remote has 50,000 manifests. If the server is configured to cache responses, each time a new commit arrives, you will have a cache miss and need to regenerate all outgoing data. But if you makes N requests requesting 10,000 manifests each, a new commit will still yield cache hits on the initial, unchanged manifest batches/requests. A derived benefit from these properties is that resumable clone is conceptually simpler to implement. When making a monolithic request for all of the repository data, recovering from an interrupted clone is hard because the server was in the driver's seat and was maintaining state about all the data that needed transferred. With the client driving fetching, the client can persist the set of unfetched entities and retry/resume a fetch if something goes wrong. Or we can fetch all data N changesets at a time and slowly build up a repository. This approach is drastically easier to implement when we have server APIs exposing low-level repository primitives (such as manifests and files). We don't yet support tree manifests. But it should be possible to implement that with the existing wire protocol command. Differential Revision: https://phab.mercurial-scm.org/D4489

File last commit:

r39640:335ae4d0 default
r39674:d292328e default
Show More
bundlerepo.py
657 lines | 23.4 KiB | text/x-python | PythonLexer
Martin Geisler
put license and copyright info into comment blocks
r8226 # bundlerepo.py - repository class for viewing uncompressed bundles
#
# Copyright 2006, 2007 Benoit Boissinot <bboissin@gmail.com>
#
# This software may be used and distributed according to the terms of the
Matt Mackall
Update license to GPLv2+
r10263 # GNU General Public License version 2 or any later version.
Benoit Boissinot
add bundlerepo.py: a read-only repo that can use uncompressed bundles...
r1942
Martin Geisler
turn some comments back into module docstrings
r8227 """Repository class for viewing uncompressed bundles.
This provides a read-only repository interface to bundles as if they
were part of the actual repository.
"""
Gregory Szorc
bundlerepo: use absolute_import
r25920 from __future__ import absolute_import
import os
import shutil
from .i18n import _
from .node import nullid
from . import (
bundle2,
changegroup,
changelog,
cmdutil,
discovery,
error,
exchange,
filelog,
localrepo,
manifest,
mdiff,
liscju
bundle: warn when update to revision existing only in a bundle (issue5004)...
r28714 node as nodemod,
Gregory Szorc
bundlerepo: use absolute_import
r25920 pathutil,
phases,
Pulkit Goyal
py3: use pycompat.getcwd() instead of os.getcwd()...
r30519 pycompat,
Gregory Szorc
bundlerepo: use absolute_import
r25920 revlog,
util,
Pierre-Yves David
vfs: use 'vfs' module directly in 'mercurial.bundlerepo'...
r31240 vfs as vfsmod,
Gregory Szorc
bundlerepo: use absolute_import
r25920 )
Benoit Boissinot
add bundlerepo.py: a read-only repo that can use uncompressed bundles...
r1942
Benoit Boissinot
bundlerepo: remove relative import, fix a comment
r1946 class bundlerevlog(revlog.revlog):
Gregory Szorc
bundlerepo: rename "bundle" arguments to "cgunpacker"...
r35075 def __init__(self, opener, indexfile, cgunpacker, linkmapper):
Benoit Boissinot
add bundlerepo.py: a read-only repo that can use uncompressed bundles...
r1942 # How it works:
Mads Kiilerich
bundlerepo: fix outdated comment...
r18410 # To retrieve a revision, we need to know the offset of the revision in
# the bundle (an unbundle object). We store this offset in the index
Mads Kiilerich
bundlerepo: replace basemap with the base field in the index...
r18643 # (start). The base of the delta is stored in the base field.
Benoit Boissinot
add bundlerepo.py: a read-only repo that can use uncompressed bundles...
r1942 #
Mads Kiilerich
bundlerepo: fix outdated comment...
r18410 # To differentiate a rev in the bundle from a rev in the revlog, we
Mads Kiilerich
bundlerepo: replace basemap with the base field in the index...
r18643 # check revision against repotiprev.
Pierre-Yves David
vfs: use 'vfs' module directly in 'mercurial.bundlerepo'...
r31240 opener = vfsmod.readonlyvfs(opener)
Matt Mackall
revlog: don't pass datafile as an argument
r4257 revlog.revlog.__init__(self, opener, indexfile)
Gregory Szorc
bundlerepo: rename "bundle" arguments to "cgunpacker"...
r35075 self.bundle = cgunpacker
Matt Mackall
add __len__ and __iter__ methods to repo and revlog
r6750 n = len(self)
Mads Kiilerich
bundlerepo: replace basemap with the base field in the index...
r18643 self.repotiprev = n - 1
Mads Kiilerich
bundlerepo: improve performance for bundle() revset expression...
r18411 self.bundlerevs = set() # used by 'bundle()' revset expression
Gregory Szorc
bundlerepo: rename "bundle" arguments to "cgunpacker"...
r35075 for deltadata in cgunpacker.deltaiter():
Durham Goode
bundlerepo: update to use new deltaiter api...
r34294 node, p1, p2, cs, deltabase, delta, flags = deltadata
Benoit Boissinot
bundlerepo: port to new bundle API
r14142
size = len(delta)
Gregory Szorc
bundlerepo: rename "bundle" arguments to "cgunpacker"...
r35075 start = cgunpacker.tell() - size
Benoit Boissinot
bundlerepo: port to new bundle API
r14142
link = linkmapper(cs)
Benoit Boissinot
add bundlerepo.py: a read-only repo that can use uncompressed bundles...
r1942 if node in self.nodemap:
Benoit Boissinot
bundlerepo: port to new bundle API
r14142 # this can happen if two branches make the same change
Mads Kiilerich
bundlerepo: improve performance for bundle() revset expression...
r18411 self.bundlerevs.add(self.nodemap[node])
Benoit Boissinot
add bundlerepo.py: a read-only repo that can use uncompressed bundles...
r1942 continue
Benoit Boissinot
bundlerepo: port to new bundle API
r14142
Benoit Boissinot
add bundlerepo.py: a read-only repo that can use uncompressed bundles...
r1942 for p in (p1, p2):
Brodie Rao
cleanup: "not x in y" -> "x not in y"
r16686 if p not in self.nodemap:
Sune Foldager
bundlerepo: fix small bug in exception raising
r9650 raise error.LookupError(p, self.indexfile,
Matt Mackall
errors: move revlog errors...
r7633 _("unknown parent"))
Mads Kiilerich
bundlerepo: store validated deltabase rev in basemap instead of node...
r18416
if deltabase not in self.nodemap:
raise LookupError(deltabase, self.indexfile,
_('unknown delta base'))
baserev = self.rev(deltabase)
Benoit Boissinot
fix bundlerepo broken by 4205f626dc05...
r5167 # start, size, full unc. size, base (unused), link, p1, p2, node
Jun Wu
bundlerepo: build revlog index with flags...
r31835 e = (revlog.offset_type(start, flags), size, -1, baserev, link,
Matt Mackall
revlog: add a magic null revision to our index...
r4979 self.rev(p1), self.rev(p2), node)
Martin von Zweigbergk
index: replace insert(-1, e) method by append(e) method...
r38886 self.index.append(e)
Benoit Boissinot
add bundlerepo.py: a read-only repo that can use uncompressed bundles...
r1942 self.nodemap[node] = n
Mads Kiilerich
bundlerepo: improve performance for bundle() revset expression...
r18411 self.bundlerevs.add(n)
Benoit Boissinot
add bundlerepo.py: a read-only repo that can use uncompressed bundles...
r1942 n += 1
Gregory Szorc
bundlerepo: make methods agree with base class...
r35038 def _chunk(self, rev, df=None):
Mads Kiilerich
bundlerepo: replace basemap with the base field in the index...
r18643 # Warning: in case of bundle, the diff is against what we stored as
# delta base, not against rev - 1
Benoit Boissinot
add bundlerepo.py: a read-only repo that can use uncompressed bundles...
r1942 # XXX: could use some caching
Mads Kiilerich
bundlerepo: replace basemap with the base field in the index...
r18643 if rev <= self.repotiprev:
Benoit Boissinot
bundlerepo: keep the bundlerevlog interface in sync with revlog
r9676 return revlog.revlog._chunk(self, rev)
Matt Mackall
bundlerepo: use bundle objects everywhere
r12332 self.bundle.seek(self.start(rev))
return self.bundle.read(self.length(rev))
Benoit Boissinot
add bundlerepo.py: a read-only repo that can use uncompressed bundles...
r1942
def revdiff(self, rev1, rev2):
"""return or calculate a delta between two revisions"""
Mads Kiilerich
bundlerepo: replace basemap with the base field in the index...
r18643 if rev1 > self.repotiprev and rev2 > self.repotiprev:
Benoit Boissinot
add bundlerepo.py: a read-only repo that can use uncompressed bundles...
r1942 # hot path for bundle
Mads Kiilerich
bundlerepo: replace basemap with the base field in the index...
r18643 revb = self.index[rev2][3]
Benoit Boissinot
add bundlerepo.py: a read-only repo that can use uncompressed bundles...
r1942 if revb == rev1:
Benoit Boissinot
bundlerepo: keep the bundlerevlog interface in sync with revlog
r9676 return self._chunk(rev2)
Mads Kiilerich
bundlerepo: replace basemap with the base field in the index...
r18643 elif rev1 <= self.repotiprev and rev2 <= self.repotiprev:
Benoit Boissinot
bundlerepo: it was meant to be revdiff() instead of chunk()
r4028 return revlog.revlog.revdiff(self, rev1, rev2)
Benoit Boissinot
add bundlerepo.py: a read-only repo that can use uncompressed bundles...
r1942
Jun Wu
bundlerepo: use raw revision in revdiff()...
r31837 return mdiff.textdiff(self.revision(rev1, raw=True),
self.revision(rev2, raw=True))
Benoit Boissinot
add bundlerepo.py: a read-only repo that can use uncompressed bundles...
r1942
Gregory Szorc
bundlerepo: make methods agree with base class...
r35038 def revision(self, nodeorrev, _df=None, raw=False):
Patrick Mezard
revlog: fix partial revision() docstring (from d7d64b89a65c)
r16435 """return an uncompressed revision of a given node or revision
number.
"""
Matt Mackall
revlog: allow retrieving contents by revision number
r16375 if isinstance(nodeorrev, int):
rev = nodeorrev
node = self.node(rev)
else:
node = nodeorrev
rev = self.rev(node)
Matt Mackall
many, many trivial check-code fixups
r10282 if node == nullid:
return ""
Benoit Boissinot
add bundlerepo.py: a read-only repo that can use uncompressed bundles...
r1942
Jun Wu
bundlerepo: fix raw handling in revision()...
r31836 rawtext = None
Benoit Boissinot
add bundlerepo.py: a read-only repo that can use uncompressed bundles...
r1942 chain = []
Mads Kiilerich
bundlerepo: use rev instead of node for iteration in revision()...
r18415 iterrev = rev
Benoit Boissinot
add bundlerepo.py: a read-only repo that can use uncompressed bundles...
r1942 # reconstruct the revision if it is from a changegroup
Mads Kiilerich
bundlerepo: replace basemap with the base field in the index...
r18643 while iterrev > self.repotiprev:
Mads Kiilerich
bundlerepo: use rev instead of node for iteration in revision()...
r18415 if self._cache and self._cache[1] == iterrev:
Jun Wu
bundlerepo: fix raw handling in revision()...
r31836 rawtext = self._cache[2]
Benoit Boissinot
add bundlerepo.py: a read-only repo that can use uncompressed bundles...
r1942 break
Mads Kiilerich
bundlerepo: use rev instead of node for iteration in revision()...
r18415 chain.append(iterrev)
Mads Kiilerich
bundlerepo: replace basemap with the base field in the index...
r18643 iterrev = self.index[iterrev][3]
Jun Wu
bundlerepo: fix raw handling in revision()...
r31836 if rawtext is None:
rawtext = self.baserevision(iterrev)
Benoit Boissinot
add bundlerepo.py: a read-only repo that can use uncompressed bundles...
r1942
while chain:
Benoit Boissinot
bundlerepo: keep the bundlerevlog interface in sync with revlog
r9676 delta = self._chunk(chain.pop())
Jun Wu
bundlerepo: fix raw handling in revision()...
r31836 rawtext = mdiff.patches(rawtext, [delta])
Benoit Boissinot
add bundlerepo.py: a read-only repo that can use uncompressed bundles...
r1942
Jun Wu
bundlerepo: fix raw handling in revision()...
r31836 text, validatehash = self._processflags(rawtext, self.flags(rev),
Remi Chaintron
revlog: flag processor...
r30745 'read', raw=raw)
if validatehash:
self.checkhash(text, node, rev=rev)
Jun Wu
bundlerepo: fix raw handling in revision()...
r31836 self._cache = (node, rev, rawtext)
Benoit Boissinot
add bundlerepo.py: a read-only repo that can use uncompressed bundles...
r1942 return text
Wojciech Lopata
bundlerevlog: extract 'baserevision' method...
r19629 def baserevision(self, nodeorrev):
# Revlog subclasses may override 'revision' method to modify format of
# content retrieved from revlog. To use bundlerevlog with such class one
# needs to override 'baserevision' and make more specific call here.
Jun Wu
bundlerepo: make baserevision return raw text...
r31834 return revlog.revlog.revision(self, nodeorrev, raw=True)
Wojciech Lopata
bundlerevlog: extract 'baserevision' method...
r19629
Gregory Szorc
bundlerepo: make methods agree with base class...
r35038 def addrevision(self, *args, **kwargs):
raise NotImplementedError
def addgroup(self, *args, **kwargs):
Benoit Boissinot
add bundlerepo.py: a read-only repo that can use uncompressed bundles...
r1942 raise NotImplementedError
Gregory Szorc
bundlerepo: make methods agree with base class...
r35038
def strip(self, *args, **kwargs):
Benoit Boissinot
add bundlerepo.py: a read-only repo that can use uncompressed bundles...
r1942 raise NotImplementedError
Gregory Szorc
bundlerepo: make methods agree with base class...
r35038
Benoit Boissinot
add bundlerepo.py: a read-only repo that can use uncompressed bundles...
r1942 def checksize(self):
raise NotImplementedError
Benoit Boissinot
bundlerepo: remove relative import, fix a comment
r1946 class bundlechangelog(bundlerevlog, changelog.changelog):
Gregory Szorc
bundlerepo: rename "bundle" arguments to "cgunpacker"...
r35075 def __init__(self, opener, cgunpacker):
Benoit Boissinot
bundlerepo: remove relative import, fix a comment
r1946 changelog.changelog.__init__(self, opener)
Benoit Boissinot
bundlerepo: port to new bundle API
r14142 linkmapper = lambda x: x
Gregory Szorc
bundlerepo: rename "bundle" arguments to "cgunpacker"...
r35075 bundlerevlog.__init__(self, opener, self.indexfile, cgunpacker,
Benoit Boissinot
bundlerepo: port to new bundle API
r14142 linkmapper)
Benoit Boissinot
add bundlerepo.py: a read-only repo that can use uncompressed bundles...
r1942
Wojciech Lopata
bundlerevlog: extract 'baserevision' method...
r19629 def baserevision(self, nodeorrev):
# Although changelog doesn't override 'revision' method, some extensions
# may replace this class with another that does. Same story with
# manifest and filelog classes.
Yuya Nishihara
bundlerepo: disable filtering of changelog while constructing revision text...
r24882
# This bypasses filtering on changelog.node() and rev() because we need
# revision text of the bundle base even if it is hidden.
oldfilter = self.filteredrevs
try:
self.filteredrevs = ()
Jun Wu
bundlerepo: make baserevision return raw text...
r31834 return changelog.changelog.revision(self, nodeorrev, raw=True)
Yuya Nishihara
bundlerepo: disable filtering of changelog while constructing revision text...
r24882 finally:
self.filteredrevs = oldfilter
Wojciech Lopata
bundlerevlog: extract 'baserevision' method...
r19629
Durham Goode
manifest: add bundlemanifestlog support...
r30373 class bundlemanifest(bundlerevlog, manifest.manifestrevlog):
Gregory Szorc
bundlerepo: rename "bundle" arguments to "cgunpacker"...
r35075 def __init__(self, opener, cgunpacker, linkmapper, dirlogstarts=None,
dir=''):
Gregory Szorc
manifest: rename dir argument and attribute to tree...
r39279 manifest.manifestrevlog.__init__(self, opener, tree=dir)
Gregory Szorc
bundlerepo: rename "bundle" arguments to "cgunpacker"...
r35075 bundlerevlog.__init__(self, opener, self.indexfile, cgunpacker,
Matt Mackall
revlog: don't pass datafile as an argument
r4257 linkmapper)
Augie Fackler
bundlerepo: add support for treemanifests in cg3 bundles...
r29715 if dirlogstarts is None:
dirlogstarts = {}
if self.bundle.version == "03":
dirlogstarts = _getfilestarts(self.bundle)
self._dirlogstarts = dirlogstarts
self._linkmapper = linkmapper
Benoit Boissinot
add bundlerepo.py: a read-only repo that can use uncompressed bundles...
r1942
Wojciech Lopata
bundlerevlog: extract 'baserevision' method...
r19629 def baserevision(self, nodeorrev):
Durham Goode
bundlerepo: let bundle repo look in the _mancache...
r26399 node = nodeorrev
if isinstance(node, int):
node = self.node(node)
Durham Goode
manifest: make one use of _mancache avoid manifestctxs...
r29925 if node in self.fulltextcache:
Augie Fackler
py3: use bytearray() instead of array('c', ...) constructions...
r31346 result = '%s' % self.fulltextcache[node]
Durham Goode
bundlerepo: let bundle repo look in the _mancache...
r26399 else:
Jun Wu
bundlerepo: make baserevision return raw text...
r31834 result = manifest.manifestrevlog.revision(self, nodeorrev, raw=True)
Durham Goode
bundlerepo: let bundle repo look in the _mancache...
r26399 return result
Wojciech Lopata
bundlerevlog: extract 'baserevision' method...
r19629
Augie Fackler
bundlerepo: add support for treemanifests in cg3 bundles...
r29715 def dirlog(self, d):
if d in self._dirlogstarts:
self.bundle.seek(self._dirlogstarts[d])
return bundlemanifest(
self.opener, self.bundle, self._linkmapper,
self._dirlogstarts, dir=d)
return super(bundlemanifest, self).dirlog(d)
Gregory Szorc
filelog: wrap revlog instead of inheriting it (API)...
r37515 class bundlefilelog(filelog.filelog):
Gregory Szorc
bundlerepo: rename "bundle" arguments to "cgunpacker"...
r35075 def __init__(self, opener, path, cgunpacker, linkmapper):
Benoit Boissinot
bundlerepo: remove relative import, fix a comment
r1946 filelog.filelog.__init__(self, opener, path)
Gregory Szorc
filelog: wrap revlog instead of inheriting it (API)...
r37515 self._revlog = bundlerevlog(opener, self.indexfile,
cgunpacker, linkmapper)
Sune Foldager
filelog: add file function to open other filelogs
r14287
Wojciech Lopata
bundlerevlog: extract 'baserevision' method...
r19629 def baserevision(self, nodeorrev):
Jun Wu
bundlerepo: make baserevision return raw text...
r31834 return filelog.filelog.revision(self, nodeorrev, raw=True)
Wojciech Lopata
bundlerevlog: extract 'baserevision' method...
r19629
Sune Foldager
peer: introduce canpush and improve error message
r17193 class bundlepeer(localrepo.localpeer):
def canpush(self):
return False
Eric Sumner
bundlerepo: implement safe phasecache...
r23631 class bundlephasecache(phases.phasecache):
def __init__(self, *args, **kwargs):
super(bundlephasecache, self).__init__(*args, **kwargs)
if util.safehasattr(self, 'opener'):
Pierre-Yves David
vfs: use 'vfs' module directly in 'mercurial.bundlerepo'...
r31240 self.opener = vfsmod.readonlyvfs(self.opener)
Eric Sumner
bundlerepo: implement safe phasecache...
r23631
def write(self):
raise NotImplementedError
def _write(self, fp):
raise NotImplementedError
def _updateroots(self, phase, newroots, tr):
self.phaseroots[phase] = newroots
self.invalidate()
self.dirty = True
Gregory Szorc
bundlerepo: rename "bundle" arguments to "cgunpacker"...
r35075 def _getfilestarts(cgunpacker):
Gregory Szorc
bundlerepo: rename "bundlefilespos" variable and attribute...
r35076 filespos = {}
Gregory Szorc
bundlerepo: rename "bundle" arguments to "cgunpacker"...
r35075 for chunkdata in iter(cgunpacker.filelogheader, {}):
Augie Fackler
bundlerepo: introduce method to find file starts and use it...
r29712 fname = chunkdata['filename']
Gregory Szorc
bundlerepo: rename "bundlefilespos" variable and attribute...
r35076 filespos[fname] = cgunpacker.tell()
Gregory Szorc
bundlerepo: rename "bundle" arguments to "cgunpacker"...
r35075 for chunk in iter(lambda: cgunpacker.deltachunk(None), {}):
Augie Fackler
bundlerepo: introduce method to find file starts and use it...
r29712 pass
Gregory Szorc
bundlerepo: rename "bundlefilespos" variable and attribute...
r35076 return filespos
Augie Fackler
bundlerepo: introduce method to find file starts and use it...
r29712
Gregory Szorc
bundlerepo: dynamically create repository type from base repository...
r39640 class bundlerepository(object):
Gregory Szorc
bundlerepo: add docstring for bundlerepository class...
r35041 """A repository instance that is a union of a local repo and a bundle.
Instances represent a read-only repository composed of a local repository
with the contents of a bundle file applied. The repository instance is
conceptually similar to the state of a repository after an
``hg unbundle`` operation. However, the contents of the bundle are never
applied to the actual base repository.
Gregory Szorc
bundlerepo: dynamically create repository type from base repository...
r39640
Instances constructed directly are not usable as repository objects.
Use instance() or makebundlerepository() to create instances.
Gregory Szorc
bundlerepo: add docstring for bundlerepository class...
r35041 """
Gregory Szorc
bundlerepo: dynamically create repository type from base repository...
r39640 def __init__(self, bundlepath, url, tempparent):
self._tempparent = tempparent
self._url = url
Mads Kiilerich
config: set a 'source' in most cases where config don't come from file but code...
r20790 self.ui.setconfig('phases', 'publish', False, 'bundlerepo')
Vadim Gelfer
hooks: add url to changegroup, incoming, prechangegroup, pretxnchangegroup hooks...
r2673
Benoit Boissinot
add support for compressed bundle repositories...
r2273 self.tempfile = None
Gregory Szorc
bundlerepo: rename arguments to bundlerepository.__init__...
r35040 f = util.posixfile(bundlepath, "rb")
Gregory Szorc
bundlerepo: dynamically create repository type from base repository...
r39640 bundle = exchange.readbundle(self.ui, f, bundlepath)
Matt Mackall
bundlerepo: remove duplication of bundle decompressors
r12044
Gregory Szorc
bundlerepo: assign bundle attributes in bundle type blocks...
r35044 if isinstance(bundle, bundle2.unbundle20):
self._bundlefile = bundle
Gregory Szorc
bundlerepo: rename _bundle to _cgunpacker...
r35045 self._cgunpacker = None
Gregory Szorc
bundlerepo: assign bundle attributes in bundle type blocks...
r35044
Gregory Szorc
bundle2: only seek to beginning of part in bundlerepo...
r35112 cgpart = None
Gregory Szorc
bundle2: don't use seekable bundle2 parts by default (issue5691)...
r35113 for part in bundle.iterparts(seekable=True):
Pierre-Yves David
bundlerepo: properly extract compressed changegroup from bundle2...
r26803 if part.type == 'changegroup':
Gregory Szorc
bundle2: only seek to beginning of part in bundlerepo...
r35112 if cgpart:
Pierre-Yves David
bundlerepo: properly extract compressed changegroup from bundle2...
r26803 raise NotImplementedError("can't process "
"multiple changegroups")
Gregory Szorc
bundle2: only seek to beginning of part in bundlerepo...
r35112 cgpart = part
Eric Sumner
bundlerepo: basic bundle2 support...
r24073
Gregory Szorc
bundlerepo: assign bundle attributes in bundle type blocks...
r35044 self._handlebundle2part(bundle, part)
Eric Sumner
bundlerepo: basic bundle2 support...
r24073
Gregory Szorc
bundle2: only seek to beginning of part in bundlerepo...
r35112 if not cgpart:
Durham Goode
bundlerepo: move bundle2 part handling out to a function...
r33890 raise error.Abort(_("No changegroups found"))
Gregory Szorc
bundle2: only seek to beginning of part in bundlerepo...
r35112
# This is required to placate a later consumer, which expects
# the payload offset to be at the beginning of the changegroup.
# We need to do this after the iterparts() generator advances
# because iterparts() will seek to end of payload after the
# generator returns control to iterparts().
cgpart.seek(0, os.SEEK_SET)
Gregory Szorc
bundlerepo: assign bundle attributes in bundle type blocks...
r35044 elif isinstance(bundle, changegroup.cg1unpacker):
if bundle.compressed():
f = self._writetempbundle(bundle.read, '.hg10un',
Gregory Szorc
bundlerepo: don't assume there are only two bundle classes...
r35042 header='HG10UN')
Gregory Szorc
bundlerepo: dynamically create repository type from base repository...
r39640 bundle = exchange.readbundle(self.ui, f, bundlepath, self.vfs)
Gregory Szorc
bundlerepo: assign bundle attributes in bundle type blocks...
r35044
self._bundlefile = bundle
Gregory Szorc
bundlerepo: rename _bundle to _cgunpacker...
r35045 self._cgunpacker = bundle
Gregory Szorc
bundlerepo: don't assume there are only two bundle classes...
r35042 else:
raise error.Abort(_('bundle type %s cannot be read') %
Gregory Szorc
bundlerepo: assign bundle attributes in bundle type blocks...
r35044 type(bundle))
Pierre-Yves David
bundlerepo: uncompress changegroup in bundle1 case only...
r26801
Gregory Szorc
bundlerepo: rename "bundlefilespos" variable and attribute...
r35076 # dict with the mapping 'filename' -> position in the changegroup.
self._cgfilespos = {}
Brendan Cully
Make bundlerepo lazier...
r5262
Eric Sumner
bundlerepo: retract phase boundary...
r23632 self.firstnewrev = self.changelog.repotiprev + 1
phases.retractboundary(self, None, phases.draft,
[ctx.node() for ctx in self[self.firstnewrev:]])
Gregory Szorc
bundlerepo: assign bundle attributes in bundle type blocks...
r35044 def _handlebundle2part(self, bundle, part):
Gregory Szorc
bundlerepo: use early return...
r35074 if part.type != 'changegroup':
return
Durham Goode
bundlerepo: move bundle2 part handling out to a function...
r33890
Gregory Szorc
bundlerepo: use early return...
r35074 cgstream = part
version = part.params.get('version', '01')
legalcgvers = changegroup.supportedincomingversions(self)
if version not in legalcgvers:
msg = _('Unsupported changegroup version: %s')
raise error.Abort(msg % version)
if bundle.compressed():
cgstream = self._writetempbundle(part.read, '.cg%sun' % version)
self._cgunpacker = changegroup.getunbundler(version, cgstream, 'UN')
Durham Goode
bundlerepo: move bundle2 part handling out to a function...
r33890
Durham Goode
bundlerepo: move temp bundle creation to a separate function...
r33888 def _writetempbundle(self, readfn, suffix, header=''):
"""Write a temporary file to disk
"""
fdtemp, temp = self.vfs.mkstemp(prefix="hg-bundle-",
Gregory Szorc
bundlerepo: use suffix variable...
r35039 suffix=suffix)
Durham Goode
bundlerepo: move temp bundle creation to a separate function...
r33888 self.tempfile = temp
Yuya Nishihara
py3: use r'' instead of sysstr('') to get around code transformer...
r36853 with os.fdopen(fdtemp, r'wb') as fptemp:
Durham Goode
bundlerepo: move temp bundle creation to a separate function...
r33888 fptemp.write(header)
while True:
chunk = readfn(2**18)
if not chunk:
break
fptemp.write(chunk)
return self.vfs.open(self.tempfile, mode="rb")
Pierre-Yves David
clfilter: ensure that filecache on localrepo is unfiltered...
r18014 @localrepo.unfilteredpropertycache
Eric Sumner
bundlerepo: implement safe phasecache...
r23631 def _phasecache(self):
return bundlephasecache(self, self._phasedefaults)
@localrepo.unfilteredpropertycache
Matt Mackall
localrepo: use propertycache
r8260 def changelog(self):
Benoit Boissinot
unbundler: separate delta and header parsing...
r14144 # consume the header if it exists
Gregory Szorc
bundlerepo: rename _bundle to _cgunpacker...
r35045 self._cgunpacker.changelogheader()
c = bundlechangelog(self.svfs, self._cgunpacker)
self.manstart = self._cgunpacker.tell()
Matt Mackall
localrepo: use propertycache
r8260 return c
Durham Goode
manifest: move manifest creation to a helper function...
r30218 def _constructmanifest(self):
Gregory Szorc
bundlerepo: rename _bundle to _cgunpacker...
r35045 self._cgunpacker.seek(self.manstart)
Benoit Boissinot
unbundler: separate delta and header parsing...
r14144 # consume the header if it exists
Gregory Szorc
bundlerepo: rename _bundle to _cgunpacker...
r35045 self._cgunpacker.manifestheader()
Pierre-Yves David
bundlerepo: properly handle hidden linkrev in manifestlog (issue4945)...
r28221 linkmapper = self.unfiltered().changelog.rev
Gregory Szorc
bundlerepo: rename _bundle to _cgunpacker...
r35045 m = bundlemanifest(self.svfs, self._cgunpacker, linkmapper)
self.filestart = self._cgunpacker.tell()
Matt Mackall
localrepo: use propertycache
r8260 return m
Durham Goode
bundle: allow bundlerepo to support alternative manifest implementations...
r35012 def _consumemanifest(self):
"""Consumes the manifest portion of the bundle, setting filestart so the
file portion can be read."""
Gregory Szorc
bundlerepo: rename _bundle to _cgunpacker...
r35045 self._cgunpacker.seek(self.manstart)
self._cgunpacker.manifestheader()
for delta in self._cgunpacker.deltaiter():
Durham Goode
bundle: allow bundlerepo to support alternative manifest implementations...
r35012 pass
Gregory Szorc
bundlerepo: rename _bundle to _cgunpacker...
r35045 self.filestart = self._cgunpacker.tell()
Durham Goode
bundle: allow bundlerepo to support alternative manifest implementations...
r35012
Pierre-Yves David
clfilter: ensure that filecache on localrepo is unfiltered...
r18014 @localrepo.unfilteredpropertycache
Matt Mackall
localrepo: use propertycache
r8260 def manstart(self):
self.changelog
return self.manstart
Pierre-Yves David
clfilter: ensure that filecache on localrepo is unfiltered...
r18014 @localrepo.unfilteredpropertycache
Matt Mackall
localrepo: use propertycache
r8260 def filestart(self):
Durham Goode
manifest: remove last uses of repo.manifest...
r30375 self.manifestlog
Durham Goode
bundle: allow bundlerepo to support alternative manifest implementations...
r35012
# If filestart was not set by self.manifestlog, that means the
# manifestlog implementation did not consume the manifests from the
# changegroup (ex: it might be consuming trees from a separate bundle2
# part instead). So we need to manually consume it.
Augie Fackler
bundlerepo: use native str when peeking in __dict__...
r35851 if r'filestart' not in self.__dict__:
Durham Goode
bundle: allow bundlerepo to support alternative manifest implementations...
r35012 self._consumemanifest()
Matt Mackall
localrepo: use propertycache
r8260 return self.filestart
Benoit Boissinot
add bundlerepo.py: a read-only repo that can use uncompressed bundles...
r1942
Vadim Gelfer
hooks: add url to changegroup, incoming, prechangegroup, pretxnchangegroup hooks...
r2673 def url(self):
return self._url
Benoit Boissinot
add bundlerepo.py: a read-only repo that can use uncompressed bundles...
r1942 def file(self, f):
Gregory Szorc
bundlerepo: rename "bundlefilespos" variable and attribute...
r35076 if not self._cgfilespos:
Gregory Szorc
bundlerepo: rename _bundle to _cgunpacker...
r35045 self._cgunpacker.seek(self.filestart)
Gregory Szorc
bundlerepo: rename "bundlefilespos" variable and attribute...
r35076 self._cgfilespos = _getfilestarts(self._cgunpacker)
Brendan Cully
Make bundlerepo lazier...
r5262
Gregory Szorc
bundlerepo: rename "bundlefilespos" variable and attribute...
r35076 if f in self._cgfilespos:
self._cgunpacker.seek(self._cgfilespos[f])
Pierre-Yves David
bundlerepo: properly handle hidden linkrev in filelog (issue4945)...
r28186 linkmapper = self.unfiltered().changelog.rev
Gregory Szorc
bundlerepo: rename _bundle to _cgunpacker...
r35045 return bundlefilelog(self.svfs, f, self._cgunpacker, linkmapper)
Benoit Boissinot
add bundlerepo.py: a read-only repo that can use uncompressed bundles...
r1942 else:
Gregory Szorc
bundlerepo: use super() when calling file()...
r37347 return super(bundlerepository, self).file(f)
Benoit Boissinot
add bundlerepo.py: a read-only repo that can use uncompressed bundles...
r1942
Matt Mackall
bundlerepo: restore close() method
r12347 def close(self):
"""Close assigned bundle file immediately."""
Gregory Szorc
bundlerepo: make bundle and bundlefile attributes private...
r35043 self._bundlefile.close()
Klaus Koch
bundlerepository: get rid of temporary bundle files (issue2478)...
r12962 if self.tempfile is not None:
FUJIWARA Katsunori
bundlerepo: treat temporarily extracted bundle file via vfs
r20981 self.vfs.unlink(self.tempfile)
John Mulligan
Add ability to directly clone from all-history bundles...
r6314 if self._tempparent:
shutil.rmtree(self._tempparent, True)
Vadim Gelfer
clean up hg.py: move repo constructor code into each repo module
r2740
Matt Mackall
clone: use cancopy
r6315 def cancopy(self):
return False
Sune Foldager
peer: introduce canpush and improve error message
r17193 def peer(self):
return bundlepeer(self)
Dirkjan Ochtman
bundlerepo doesn't really have a dirstate, throw AttributeError if requested
r7435 def getcwd(self):
Pulkit Goyal
py3: use pycompat.getcwd() instead of os.getcwd()...
r30519 return pycompat.getcwd() # always outside the repo
Dirkjan Ochtman
bundlerepo doesn't really have a dirstate, throw AttributeError if requested
r7435
liscju
bundle: warn when update to revision existing only in a bundle (issue5004)...
r28714 # Check if parents exist in localrepo before setting
def setparents(self, p1, p2=nullid):
p1rev = self.changelog.rev(p1)
p2rev = self.changelog.rev(p2)
msg = _("setting parent to node %s that only exists in the bundle\n")
if self.changelog.repotiprev < p1rev:
self.ui.warn(msg % nodemod.hex(p1))
if self.changelog.repotiprev < p2rev:
self.ui.warn(msg % nodemod.hex(p2))
return super(bundlerepository, self).setparents(p1, p2)
Sune Foldager
bundlerepo: don't write branch cache to disk...
r15597
Gregory Szorc
hg: allow extra arguments to be passed to repo creation (API)...
r39585 def instance(ui, path, create, intents=None, createopts=None):
Vadim Gelfer
clean up hg.py: move repo constructor code into each repo module
r2740 if create:
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(_('cannot create new bundle repository'))
Matt Mackall
bundlerepo: mark internal-only config variable
r25830 # internal config: bundle.mainreporoot
configitems: register the 'bundle.mainreporoot' config
r33179 parentpath = ui.config("bundle", "mainreporoot")
Matt Mackall
bundlerepo: try to find containing repo on creation (issue1812)
r16042 if not parentpath:
# try to find the correct path to the working directory repo
Pulkit Goyal
py3: use pycompat.getcwd() instead of os.getcwd()...
r30519 parentpath = cmdutil.findrepo(pycompat.getcwd())
Matt Mackall
bundlerepo: try to find containing repo on creation (issue1812)
r16042 if parentpath is None:
parentpath = ''
Peter Arrenbrecht
Fix income/pull with bundle and -R (issue 820)....
r5664 if parentpath:
# Try to make the full path relative so we get a nice, short URL.
# In particular, we don't want temp dir names in test outputs.
Pulkit Goyal
py3: use pycompat.getcwd() instead of os.getcwd()...
r30519 cwd = pycompat.getcwd()
Peter Arrenbrecht
Fix income/pull with bundle and -R (issue 820)....
r5664 if parentpath == cwd:
parentpath = ''
else:
FUJIWARA Katsunori
bundlerepo: use pathutil.normasprefix to ensure os.sep at the end of cwd...
r24834 cwd = pathutil.normasprefix(cwd)
Peter Arrenbrecht
Fix income/pull with bundle and -R (issue 820)....
r5664 if parentpath.startswith(cwd):
parentpath = parentpath[len(cwd):]
Brodie Rao
url: move URL parsing functions into util to improve startup time...
r14076 u = util.url(path)
Brodie Rao
url: refactor util.drop_scheme() and hg.localpath() into url.localpath()...
r13826 path = u.localpath()
if u.scheme == 'bundle':
Vadim Gelfer
clean up hg.py: move repo constructor code into each repo module
r2740 s = path.split("+", 1)
if len(s) == 1:
Peter Arrenbrecht
Fix income/pull with bundle and -R (issue 820)....
r5664 repopath, bundlename = parentpath, s[0]
Vadim Gelfer
clean up hg.py: move repo constructor code into each repo module
r2740 else:
repopath, bundlename = s
else:
Peter Arrenbrecht
Fix income/pull with bundle and -R (issue 820)....
r5664 repopath, bundlename = parentpath, path
Gregory Szorc
bundlerepo: factor out code for instantiating a bundle repository...
r39639
return makebundlerepository(ui, repopath, bundlename)
def makebundlerepository(ui, repopath, bundlepath):
"""Make a bundle repository object based on repo and bundle paths."""
Gregory Szorc
bundlerepo: dynamically create repository type from base repository...
r39640 if repopath:
url = 'bundle:%s+%s' % (util.expandpath(repopath), bundlepath)
else:
url = 'bundle:%s' % bundlepath
# Because we can't make any guarantees about the type of the base
# repository, we can't have a static class representing the bundle
# repository. We also can't make any guarantees about how to even
# call the base repository's constructor!
#
# So, our strategy is to go through ``localrepo.instance()`` to construct
# a repo instance. Then, we dynamically create a new type derived from
# both it and our ``bundlerepository`` class which overrides some
# functionality. We then change the type of the constructed repository
# to this new type and initialize the bundle-specific bits of it.
try:
parentrepo = localrepo.instance(ui, repopath, create=False)
tempparent = None
except error.RepoError:
tempparent = pycompat.mkdtemp()
try:
parentrepo = localrepo.instance(ui, tempparent, create=True)
except Exception:
shutil.rmtree(tempparent)
raise
class derivedbundlerepository(bundlerepository, parentrepo.__class__):
pass
repo = parentrepo
repo.__class__ = derivedbundlerepository
bundlerepository.__init__(repo, bundlepath, url, tempparent)
return repo
Nicolas Dumazet
bundlerepo: unify common code into a new getremotechanges...
r12734
Eric Sumner
incoming: handle phases the same as pull...
r23633 class bundletransactionmanager(object):
def transaction(self):
return None
def close(self):
raise NotImplementedError
def release(self):
raise NotImplementedError
Gregory Szorc
bundlerepo: rename "other" to "peer"...
r37660 def getremotechanges(ui, repo, peer, onlyheads=None, bundlename=None,
Peter Arrenbrecht
discovery: drop findoutgoing and simplify findcommonincoming's api...
r14073 force=False):
Gregory Szorc
bundlerepo: rename "other" to "peer"...
r37660 '''obtains a bundle of changes incoming from peer
Peter Arrenbrecht
bundlerepo: fix and improve getremotechanges...
r14161
"onlyheads" restricts the returned changes to those reachable from the
specified heads.
"bundlename", if given, stores the bundle to this file path permanently;
Peter Arrenbrecht
bundlerepo: fix closing and docstring of getremotechanges...
r14190 otherwise it's stored to a temp file and gets deleted again when you call
the returned "cleanupfn".
Peter Arrenbrecht
bundlerepo: fix and improve getremotechanges...
r14161 "force" indicates whether to proceed on unrelated repos.
Returns a tuple (local, csets, cleanupfn):
Brodie Rao
cleanup: eradicate long lines
r16683 "local" is a local repo from which to obtain the actual incoming
changesets; it is a bundlerepo for the obtained bundle when the
Gregory Szorc
bundlerepo: rename "other" to "peer"...
r37660 original "peer" is remote.
Peter Arrenbrecht
bundlerepo: fix and improve getremotechanges...
r14161 "csets" lists the incoming changeset node ids.
Brodie Rao
cleanup: eradicate long lines
r16683 "cleanupfn" must be called without arguments when you're done processing
Gregory Szorc
bundlerepo: rename "other" to "peer"...
r37660 the changes; it closes both the original "peer" and the one returned
Brodie Rao
cleanup: eradicate long lines
r16683 here.
Peter Arrenbrecht
bundlerepo: fix and improve getremotechanges...
r14161 '''
Gregory Szorc
bundlerepo: rename "other" to "peer"...
r37660 tmp = discovery.findcommonincoming(repo, peer, heads=onlyheads,
Brodie Rao
cleanup: eradicate long lines
r16683 force=force)
Nicolas Dumazet
bundlerepo: unify common code into a new getremotechanges...
r12734 common, incoming, rheads = tmp
if not incoming:
try:
Sune Foldager
bundlerepo: add argument check before unlink...
r15091 if bundlename:
Matt Mackall
bundlerepo: backout dbf292f65b09...
r21694 os.unlink(bundlename)
Idan Kamara
eliminate various naked except clauses
r14004 except OSError:
Nicolas Dumazet
bundlerepo: unify common code into a new getremotechanges...
r12734 pass
Gregory Szorc
bundlerepo: rename "other" to "peer"...
r37660 return repo, [], peer.close
Nicolas Dumazet
bundlerepo: unify common code into a new getremotechanges...
r12734
Mads Kiilerich
incoming: don't request heads that already are common...
r22182 commonset = set(common)
rheads = [x for x in rheads if x not in commonset]
Nicolas Dumazet
bundlerepo: unify common code into a new getremotechanges...
r12734 bundle = None
Peter Arrenbrecht
bundlerepo: fix and improve getremotechanges...
r14161 bundlerepo = None
Gregory Szorc
bundlerepo: rename "other" to "peer"...
r37660 localrepo = peer.local()
Sune Foldager
peer: introduce peer methods to prepare for peer classes...
r17191 if bundlename or not localrepo:
Gregory Szorc
bundlerepo: rename "other" to "peer"...
r37660 # create a bundle (uncompressed if peer repo is not local)
Nicolas Dumazet
bundlerepo: unify common code into a new getremotechanges...
r12734
Pierre-Yves David
bundlerepo: also read the 'devel.legacy.exchange' config...
r29684 # developer config: devel.legacy.exchange
legexc = ui.configlist('devel', 'legacy.exchange')
Pierre-Yves David
bundle2: remove 'experimental.bundle2-exp' boolean config (BC)...
r29689 forcebundle1 = 'bundle2' not in legexc and 'bundle1' in legexc
Pierre-Yves David
bundlerepo: also read the 'devel.legacy.exchange' config...
r29684 canbundle2 = (not forcebundle1
Gregory Szorc
bundlerepo: rename "other" to "peer"...
r37660 and peer.capable('getbundle')
and peer.capable('bundle2'))
Pierre-Yves David
incoming: request a bundle2 when possible (BC)...
r26544 if canbundle2:
Gregory Szorc
bundlerepo: use command executor for wire protocol commands...
r37661 with peer.commandexecutor() as e:
b2 = e.callcommand('getbundle', {
'source': 'incoming',
'common': common,
'heads': rheads,
'bundlecaps': exchange.caps20to10(repo, role='client'),
'cg': True,
}).result()
fname = bundle = changegroup.writechunks(ui,
b2._forwardchunks(),
bundlename)
Pierre-Yves David
incoming: request a bundle2 when possible (BC)...
r26544 else:
Gregory Szorc
bundlerepo: rename "other" to "peer"...
r37660 if peer.capable('getbundle'):
Gregory Szorc
bundlerepo: use command executor for wire protocol commands...
r37661 with peer.commandexecutor() as e:
cg = e.callcommand('getbundle', {
'source': 'incoming',
'common': common,
'heads': rheads,
}).result()
Gregory Szorc
bundlerepo: rename "other" to "peer"...
r37660 elif onlyheads is None and not peer.capable('changegroupsubset'):
Pierre-Yves David
bundlerepo: indent some code to prepare next patch...
r26543 # compat with older servers when pulling all remote heads
Gregory Szorc
wireproto: convert legacy commands to command executor...
r37653
Gregory Szorc
bundlerepo: rename "other" to "peer"...
r37660 with peer.commandexecutor() as e:
Gregory Szorc
wireproto: convert legacy commands to command executor...
r37653 cg = e.callcommand('changegroup', {
'nodes': incoming,
'source': 'incoming',
}).result()
Pierre-Yves David
bundlerepo: indent some code to prepare next patch...
r26543 rheads = None
else:
Gregory Szorc
bundlerepo: rename "other" to "peer"...
r37660 with peer.commandexecutor() as e:
Gregory Szorc
wireproto: convert legacy commands to command executor...
r37653 cg = e.callcommand('changegroupsubset', {
'bases': incoming,
'heads': rheads,
'source': 'incoming',
}).result()
Pierre-Yves David
bundlerepo: indent some code to prepare next patch...
r26543 if localrepo:
bundletype = "HG10BZ"
else:
bundletype = "HG10UN"
Martin von Zweigbergk
bundle: move writebundle() from changegroup.py to bundle2.py (API)...
r28666 fname = bundle = bundle2.writebundle(ui, cg, bundlename,
Pierre-Yves David
bundlerepo: indent some code to prepare next patch...
r26543 bundletype)
Nicolas Dumazet
bundlerepo: unify common code into a new getremotechanges...
r12734 # keep written bundle?
if bundlename:
bundle = None
Sune Foldager
peer: introduce peer methods to prepare for peer classes...
r17191 if not localrepo:
Nicolas Dumazet
bundlerepo: unify common code into a new getremotechanges...
r12734 # use the created uncompressed bundlerepo
Gregory Szorc
bundlerepo: factor out code for instantiating a bundle repository...
r39639 localrepo = bundlerepo = makebundlerepository(repo. baseui,
repo.root,
fname)
Gregory Szorc
bundlerepo: rename "other" to "peer"...
r37660 # this repo contains local and peer now, so filter out local again
Peter Arrenbrecht
bundlerepo: fix and improve getremotechanges...
r14161 common = repo.heads()
Pierre-Yves David
incoming: fix incoming when a local head is remotely filtered (issue3805)...
r18568 if localrepo:
# Part of common may be remotely filtered
# So use an unfiltered version
# The discovery process probably need cleanup to avoid that
localrepo = localrepo.unfiltered()
Peter Arrenbrecht
bundlerepo: fix and improve getremotechanges...
r14161
Peter Arrenbrecht
bundlerepo: make getremotechanges support filtering of incoming...
r14412 csets = localrepo.changelog.findmissing(common, rheads)
Nicolas Dumazet
bundlerepo: unify common code into a new getremotechanges...
r12734
Eric Sumner
incoming: handle phases the same as pull...
r23633 if bundlerepo:
reponodes = [ctx.node() for ctx in bundlerepo[bundlerepo.firstnewrev:]]
Gregory Szorc
bundlerepo: use command executor for wire protocol commands...
r37661
with peer.commandexecutor() as e:
remotephases = e.callcommand('listkeys', {
'namespace': 'phases',
}).result()
Eric Sumner
incoming: handle phases the same as pull...
r23633
Gregory Szorc
bundlerepo: rename "other" to "peer"...
r37660 pullop = exchange.pulloperation(bundlerepo, peer, heads=reponodes)
Eric Sumner
incoming: handle phases the same as pull...
r23633 pullop.trmanager = bundletransactionmanager()
exchange._pullapplyphases(pullop, remotephases)
Peter Arrenbrecht
bundlerepo: fix and improve getremotechanges...
r14161 def cleanup():
if bundlerepo:
bundlerepo.close()
if bundle:
Matt Mackall
bundlerepo: backout dbf292f65b09...
r21694 os.unlink(bundle)
Gregory Szorc
bundlerepo: rename "other" to "peer"...
r37660 peer.close()
Peter Arrenbrecht
bundlerepo: fix and improve getremotechanges...
r14161
return (localrepo, csets, cleanup)