eggs/mercurial-1.7.3-py2.6-linux-x86_64.egg/mercurial/bundlerepo.py
changeset 69 c6bca38c1cbf
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eggs/mercurial-1.7.3-py2.6-linux-x86_64.egg/mercurial/bundlerepo.py	Sat Jan 08 11:20:57 2011 +0530
@@ -0,0 +1,324 @@
+# 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, struct, 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=None):
+        # 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 = {}
+        def chunkpositer():
+            while 1:
+                chunk = bundle.chunk()
+                if not chunk:
+                    break
+                pos = bundle.tell()
+                yield chunk, pos - len(chunk)
+        n = len(self)
+        prev = None
+        for chunk, start in chunkpositer():
+            size = len(chunk)
+            if size < 80:
+                raise util.Abort(_("invalid changegroup"))
+            start += 80
+            size -= 80
+            node, p1, p2, cs = struct.unpack("20s20s20s20s", chunk[:80])
+            if node in self.nodemap:
+                prev = node
+                continue
+            for p in (p1, p2):
+                if not p in self.nodemap:
+                    raise error.LookupError(p, self.indexfile,
+                                            _("unknown parent"))
+            if linkmapper is None:
+                link = n
+            else:
+                link = linkmapper(cs)
+
+            if not prev:
+                prev = p1
+            # 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] = prev
+            self.index.insert(-1, e)
+            self.nodemap[node] = n
+            prev = 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)
+        bundlerevlog.__init__(self, opener, self.indexfile, bundle)
+
+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):
+        filelog.filelog.__init__(self, opener, path)
+        bundlerevlog.__init__(self, opener, self.indexfile, bundle,
+                              linkmapper)
+
+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 = open(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 1:
+                    chunk = self.bundle.read(2**18)
+                    if not chunk:
+                        break
+                    fptemp.write(chunk)
+            finally:
+                fptemp.close()
+
+            f = open(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):
+        c = bundlechangelog(self.sopener, self.bundle)
+        self.manstart = self.bundle.tell()
+        return c
+
+    @util.propertycache
+    def manifest(self):
+        self.bundle.seek(self.manstart)
+        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 1:
+                chunk = self.bundle.chunk()
+                if not chunk:
+                    break
+                self.bundlefilespos[chunk] = self.bundle.tell()
+                while 1:
+                    c = self.bundle.chunk()
+                    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)
+        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)
+
+    def __del__(self):
+        del self.bundle
+        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 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):]
+    path = util.drop_scheme('file', path)
+    if path.startswith('bundle:'):
+        path = util.drop_scheme('bundle', path)
+        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, revs=None, bundlename=None, force=False):
+    tmp = discovery.findcommonincoming(repo, other, heads=revs, force=force)
+    common, incoming, rheads = tmp
+    if not incoming:
+        try:
+            os.unlink(bundlename)
+        except:
+            pass
+        return other, None, None
+
+    bundle = None
+    if bundlename or not other.local():
+        # create a bundle (uncompressed if other repo is not local)
+
+        if revs is None and other.capable('changegroupsubset'):
+            revs = rheads
+
+        if revs is None:
+            cg = other.changegroup(incoming, "incoming")
+        else:
+            cg = other.changegroupsubset(incoming, revs, '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
+            other = bundlerepository(ui, repo.root, fname)
+    return (other, incoming, bundle)
+