Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/websdk/mercurial/bundlerepo.py
diff options
context:
space:
mode:
Diffstat (limited to 'websdk/mercurial/bundlerepo.py')
-rw-r--r--[l---------]websdk/mercurial/bundlerepo.py364
1 files changed, 363 insertions, 1 deletions
diff --git a/websdk/mercurial/bundlerepo.py b/websdk/mercurial/bundlerepo.py
index 8da29ad..5ee67b9 120000..100644
--- a/websdk/mercurial/bundlerepo.py
+++ b/websdk/mercurial/bundlerepo.py
@@ -1 +1,363 @@
-/usr/share/pyshared/mercurial/bundlerepo.py \ No newline at end of file
+# 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
+# GNU General Public License version 2 or any later version.
+
+"""Repository class for viewing uncompressed bundles.
+
+This provides a read-only repository interface to bundles as if they
+were part of the actual repository.
+"""
+
+from node import nullid
+from i18n import _
+import os, tempfile, shutil
+import changegroup, util, mdiff, discovery
+import localrepo, changelog, manifest, filelog, revlog, error
+
+class bundlerevlog(revlog.revlog):
+ def __init__(self, opener, indexfile, bundle, linkmapper):
+ # How it works:
+ # 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 (start), to differentiate a
+ # rev in the bundle and from a rev in the revlog, we check
+ # len(index[r]). If the tuple is bigger than 7, it is a bundle
+ # (it is bigger since we store the node to which the delta is)
+ #
+ revlog.revlog.__init__(self, opener, indexfile)
+ self.bundle = bundle
+ self.basemap = {}
+ n = len(self)
+ chain = None
+ while True:
+ chunkdata = bundle.deltachunk(chain)
+ if not chunkdata:
+ break
+ node = chunkdata['node']
+ p1 = chunkdata['p1']
+ p2 = chunkdata['p2']
+ cs = chunkdata['cs']
+ deltabase = chunkdata['deltabase']
+ delta = chunkdata['delta']
+
+ size = len(delta)
+ start = bundle.tell() - size
+
+ link = linkmapper(cs)
+ if node in self.nodemap:
+ # this can happen if two branches make the same change
+ chain = node
+ continue
+
+ for p in (p1, p2):
+ if not p in self.nodemap:
+ raise error.LookupError(p, self.indexfile,
+ _("unknown parent"))
+ # start, size, full unc. size, base (unused), link, p1, p2, node
+ e = (revlog.offset_type(start, 0), size, -1, -1, link,
+ self.rev(p1), self.rev(p2), node)
+ self.basemap[n] = deltabase
+ self.index.insert(-1, e)
+ self.nodemap[node] = n
+ chain = node
+ n += 1
+
+ def inbundle(self, rev):
+ """is rev from the bundle"""
+ if rev < 0:
+ return False
+ return rev in self.basemap
+ def bundlebase(self, rev):
+ return self.basemap[rev]
+ def _chunk(self, rev):
+ # Warning: in case of bundle, the diff is against bundlebase,
+ # not against rev - 1
+ # XXX: could use some caching
+ if not self.inbundle(rev):
+ return revlog.revlog._chunk(self, rev)
+ self.bundle.seek(self.start(rev))
+ return self.bundle.read(self.length(rev))
+
+ def revdiff(self, rev1, rev2):
+ """return or calculate a delta between two revisions"""
+ if self.inbundle(rev1) and self.inbundle(rev2):
+ # hot path for bundle
+ revb = self.rev(self.bundlebase(rev2))
+ if revb == rev1:
+ return self._chunk(rev2)
+ elif not self.inbundle(rev1) and not self.inbundle(rev2):
+ return revlog.revlog.revdiff(self, rev1, rev2)
+
+ return mdiff.textdiff(self.revision(self.node(rev1)),
+ self.revision(self.node(rev2)))
+
+ def revision(self, node):
+ """return an uncompressed revision of a given"""
+ if node == nullid:
+ return ""
+
+ text = None
+ chain = []
+ iter_node = node
+ rev = self.rev(iter_node)
+ # reconstruct the revision if it is from a changegroup
+ while self.inbundle(rev):
+ if self._cache and self._cache[0] == iter_node:
+ text = self._cache[2]
+ break
+ chain.append(rev)
+ iter_node = self.bundlebase(rev)
+ rev = self.rev(iter_node)
+ if text is None:
+ text = revlog.revlog.revision(self, iter_node)
+
+ while chain:
+ delta = self._chunk(chain.pop())
+ text = mdiff.patches(text, [delta])
+
+ p1, p2 = self.parents(node)
+ if node != revlog.hash(text, p1, p2):
+ raise error.RevlogError(_("integrity check failed on %s:%d")
+ % (self.datafile, self.rev(node)))
+
+ self._cache = (node, self.rev(node), text)
+ return text
+
+ def addrevision(self, text, transaction, link, p1=None, p2=None, d=None):
+ raise NotImplementedError
+ def addgroup(self, revs, linkmapper, transaction):
+ raise NotImplementedError
+ def strip(self, rev, minlink):
+ raise NotImplementedError
+ def checksize(self):
+ raise NotImplementedError
+
+class bundlechangelog(bundlerevlog, changelog.changelog):
+ def __init__(self, opener, bundle):
+ changelog.changelog.__init__(self, opener)
+ linkmapper = lambda x: x
+ bundlerevlog.__init__(self, opener, self.indexfile, bundle,
+ linkmapper)
+
+class bundlemanifest(bundlerevlog, manifest.manifest):
+ def __init__(self, opener, bundle, linkmapper):
+ manifest.manifest.__init__(self, opener)
+ bundlerevlog.__init__(self, opener, self.indexfile, bundle,
+ linkmapper)
+
+class bundlefilelog(bundlerevlog, filelog.filelog):
+ def __init__(self, opener, path, bundle, linkmapper, repo):
+ filelog.filelog.__init__(self, opener, path)
+ bundlerevlog.__init__(self, opener, self.indexfile, bundle,
+ linkmapper)
+ self._repo = repo
+
+ def _file(self, f):
+ self._repo.file(f)
+
+class bundlerepository(localrepo.localrepository):
+ def __init__(self, ui, path, bundlename):
+ self._tempparent = None
+ try:
+ localrepo.localrepository.__init__(self, ui, path)
+ except error.RepoError:
+ self._tempparent = tempfile.mkdtemp()
+ localrepo.instance(ui, self._tempparent, 1)
+ localrepo.localrepository.__init__(self, ui, self._tempparent)
+
+ if path:
+ self._url = 'bundle:' + util.expandpath(path) + '+' + bundlename
+ else:
+ self._url = 'bundle:' + bundlename
+
+ self.tempfile = None
+ f = util.posixfile(bundlename, "rb")
+ self.bundle = changegroup.readbundle(f, bundlename)
+ if self.bundle.compressed():
+ fdtemp, temp = tempfile.mkstemp(prefix="hg-bundle-",
+ suffix=".hg10un", dir=self.path)
+ self.tempfile = temp
+ fptemp = os.fdopen(fdtemp, 'wb')
+
+ try:
+ fptemp.write("HG10UN")
+ while True:
+ chunk = self.bundle.read(2**18)
+ if not chunk:
+ break
+ fptemp.write(chunk)
+ finally:
+ fptemp.close()
+
+ f = util.posixfile(self.tempfile, "rb")
+ self.bundle = changegroup.readbundle(f, bundlename)
+
+ # dict with the mapping 'filename' -> position in the bundle
+ self.bundlefilespos = {}
+
+ @util.propertycache
+ def changelog(self):
+ # consume the header if it exists
+ self.bundle.changelogheader()
+ c = bundlechangelog(self.sopener, self.bundle)
+ self.manstart = self.bundle.tell()
+ return c
+
+ @util.propertycache
+ def manifest(self):
+ self.bundle.seek(self.manstart)
+ # consume the header if it exists
+ self.bundle.manifestheader()
+ m = bundlemanifest(self.sopener, self.bundle, self.changelog.rev)
+ self.filestart = self.bundle.tell()
+ return m
+
+ @util.propertycache
+ def manstart(self):
+ self.changelog
+ return self.manstart
+
+ @util.propertycache
+ def filestart(self):
+ self.manifest
+ return self.filestart
+
+ def url(self):
+ return self._url
+
+ def file(self, f):
+ if not self.bundlefilespos:
+ self.bundle.seek(self.filestart)
+ while True:
+ chunkdata = self.bundle.filelogheader()
+ if not chunkdata:
+ break
+ fname = chunkdata['filename']
+ self.bundlefilespos[fname] = self.bundle.tell()
+ while True:
+ c = self.bundle.deltachunk(None)
+ if not c:
+ break
+
+ if f[0] == '/':
+ f = f[1:]
+ if f in self.bundlefilespos:
+ self.bundle.seek(self.bundlefilespos[f])
+ return bundlefilelog(self.sopener, f, self.bundle,
+ self.changelog.rev, self)
+ else:
+ return filelog.filelog(self.sopener, f)
+
+ def close(self):
+ """Close assigned bundle file immediately."""
+ self.bundle.close()
+ if self.tempfile is not None:
+ os.unlink(self.tempfile)
+ if self._tempparent:
+ shutil.rmtree(self._tempparent, True)
+
+ def cancopy(self):
+ return False
+
+ def getcwd(self):
+ return os.getcwd() # always outside the repo
+
+ def _writebranchcache(self, branches, tip, tiprev):
+ # don't overwrite the disk cache with bundle-augmented data
+ pass
+
+def instance(ui, path, create):
+ if create:
+ raise util.Abort(_('cannot create new bundle repository'))
+ parentpath = ui.config("bundle", "mainreporoot", "")
+ 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.
+ cwd = os.getcwd()
+ if parentpath == cwd:
+ parentpath = ''
+ else:
+ cwd = os.path.join(cwd,'')
+ if parentpath.startswith(cwd):
+ parentpath = parentpath[len(cwd):]
+ u = util.url(path)
+ path = u.localpath()
+ if u.scheme == 'bundle':
+ s = path.split("+", 1)
+ if len(s) == 1:
+ repopath, bundlename = parentpath, s[0]
+ else:
+ repopath, bundlename = s
+ else:
+ repopath, bundlename = parentpath, path
+ return bundlerepository(ui, repopath, bundlename)
+
+def getremotechanges(ui, repo, other, onlyheads=None, bundlename=None,
+ force=False):
+ '''obtains a bundle of changes incoming from other
+
+ "onlyheads" restricts the returned changes to those reachable from the
+ specified heads.
+ "bundlename", if given, stores the bundle to this file path permanently;
+ otherwise it's stored to a temp file and gets deleted again when you call
+ the returned "cleanupfn".
+ "force" indicates whether to proceed on unrelated repos.
+
+ Returns a tuple (local, csets, cleanupfn):
+
+ "local" is a local repo from which to obtain the actual incoming changesets; it
+ is a bundlerepo for the obtained bundle when the original "other" is remote.
+ "csets" lists the incoming changeset node ids.
+ "cleanupfn" must be called without arguments when you're done processing the
+ changes; it closes both the original "other" and the one returned here.
+ '''
+ tmp = discovery.findcommonincoming(repo, other, heads=onlyheads, force=force)
+ common, incoming, rheads = tmp
+ if not incoming:
+ try:
+ if bundlename:
+ os.unlink(bundlename)
+ except OSError:
+ pass
+ return other, [], other.close
+
+ bundle = None
+ bundlerepo = None
+ localrepo = other
+ if bundlename or not other.local():
+ # create a bundle (uncompressed if other repo is not local)
+
+ if other.capable('getbundle'):
+ cg = other.getbundle('incoming', common=common, heads=rheads)
+ elif onlyheads is None and not other.capable('changegroupsubset'):
+ # compat with older servers when pulling all remote heads
+ cg = other.changegroup(incoming, "incoming")
+ rheads = None
+ else:
+ cg = other.changegroupsubset(incoming, rheads, 'incoming')
+ bundletype = other.local() and "HG10BZ" or "HG10UN"
+ fname = bundle = changegroup.writebundle(cg, bundlename, bundletype)
+ # keep written bundle?
+ if bundlename:
+ bundle = None
+ if not other.local():
+ # 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():
+ if bundlerepo:
+ bundlerepo.close()
+ if bundle:
+ os.unlink(bundle)
+ other.close()
+
+ return (localrepo, csets, cleanup)
+