eggs/mercurial-1.7.3-py2.6-linux-x86_64.egg/mercurial/changegroup.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/changegroup.py	Sat Jan 08 11:20:57 2011 +0530
@@ -0,0 +1,204 @@
+# changegroup.py - Mercurial changegroup manipulation functions
+#
+#  Copyright 2006 Matt Mackall <mpm@selenic.com>
+#
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2 or any later version.
+
+from i18n import _
+import util
+import struct, os, bz2, zlib, tempfile
+
+def getchunk(source):
+    """return the next chunk from changegroup 'source' as a string"""
+    d = source.read(4)
+    if not d:
+        return ""
+    l = struct.unpack(">l", d)[0]
+    if l <= 4:
+        return ""
+    d = source.read(l - 4)
+    if len(d) < l - 4:
+        raise util.Abort(_("premature EOF reading chunk"
+                           " (got %d bytes, expected %d)")
+                          % (len(d), l - 4))
+    return d
+
+def chunkheader(length):
+    """return a changegroup chunk header (string)"""
+    return struct.pack(">l", length + 4)
+
+def closechunk():
+    """return a changegroup chunk header (string) for a zero-length chunk"""
+    return struct.pack(">l", 0)
+
+class nocompress(object):
+    def compress(self, x):
+        return x
+    def flush(self):
+        return ""
+
+bundletypes = {
+    "": ("", nocompress),
+    "HG10UN": ("HG10UN", nocompress),
+    "HG10BZ": ("HG10", lambda: bz2.BZ2Compressor()),
+    "HG10GZ": ("HG10GZ", lambda: zlib.compressobj()),
+}
+
+def collector(cl, mmfs, files):
+    # Gather information about changeset nodes going out in a bundle.
+    # We want to gather manifests needed and filelogs affected.
+    def collect(node):
+        c = cl.read(node)
+        files.update(c[3])
+        mmfs.setdefault(c[0], node)
+    return collect
+
+# hgweb uses this list to communicate its preferred type
+bundlepriority = ['HG10GZ', 'HG10BZ', 'HG10UN']
+
+def writebundle(cg, filename, bundletype):
+    """Write a bundle file and return its filename.
+
+    Existing files will not be overwritten.
+    If no filename is specified, a temporary file is created.
+    bz2 compression can be turned off.
+    The bundle file will be deleted in case of errors.
+    """
+
+    fh = None
+    cleanup = None
+    try:
+        if filename:
+            fh = open(filename, "wb")
+        else:
+            fd, filename = tempfile.mkstemp(prefix="hg-bundle-", suffix=".hg")
+            fh = os.fdopen(fd, "wb")
+        cleanup = filename
+
+        header, compressor = bundletypes[bundletype]
+        fh.write(header)
+        z = compressor()
+
+        # parse the changegroup data, otherwise we will block
+        # in case of sshrepo because we don't know the end of the stream
+
+        # an empty chunkgroup is the end of the changegroup
+        # a changegroup has at least 2 chunkgroups (changelog and manifest).
+        # after that, an empty chunkgroup is the end of the changegroup
+        empty = False
+        count = 0
+        while not empty or count <= 2:
+            empty = True
+            count += 1
+            while 1:
+                chunk = getchunk(cg)
+                if not chunk:
+                    break
+                empty = False
+                fh.write(z.compress(chunkheader(len(chunk))))
+                pos = 0
+                while pos < len(chunk):
+                    next = pos + 2**20
+                    fh.write(z.compress(chunk[pos:next]))
+                    pos = next
+            fh.write(z.compress(closechunk()))
+        fh.write(z.flush())
+        cleanup = None
+        return filename
+    finally:
+        if fh is not None:
+            fh.close()
+        if cleanup is not None:
+            os.unlink(cleanup)
+
+def decompressor(fh, alg):
+    if alg == 'UN':
+        return fh
+    elif alg == 'GZ':
+        def generator(f):
+            zd = zlib.decompressobj()
+            for chunk in f:
+                yield zd.decompress(chunk)
+    elif alg == 'BZ':
+        def generator(f):
+            zd = bz2.BZ2Decompressor()
+            zd.decompress("BZ")
+            for chunk in util.filechunkiter(f, 4096):
+                yield zd.decompress(chunk)
+    else:
+        raise util.Abort("unknown bundle compression '%s'" % alg)
+    return util.chunkbuffer(generator(fh))
+
+class unbundle10(object):
+    def __init__(self, fh, alg):
+        self._stream = decompressor(fh, alg)
+        self._type = alg
+        self.callback = None
+    def compressed(self):
+        return self._type != 'UN'
+    def read(self, l):
+        return self._stream.read(l)
+    def seek(self, pos):
+        return self._stream.seek(pos)
+    def tell(self):
+        return self._stream.tell()
+    def close(self):
+        return self._stream.close()
+
+    def chunklength(self):
+        d = self.read(4)
+        if not d:
+            return 0
+        l = max(0, struct.unpack(">l", d)[0] - 4)
+        if l and self.callback:
+            self.callback()
+        return l
+
+    def chunk(self):
+        """return the next chunk from changegroup 'source' as a string"""
+        l = self.chunklength()
+        d = self.read(l)
+        if len(d) < l:
+            raise util.Abort(_("premature EOF reading chunk"
+                               " (got %d bytes, expected %d)")
+                             % (len(d), l))
+        return d
+
+    def parsechunk(self):
+        l = self.chunklength()
+        if not l:
+            return {}
+        h = self.read(80)
+        node, p1, p2, cs = struct.unpack("20s20s20s20s", h)
+        data = self.read(l - 80)
+        return dict(node=node, p1=p1, p2=p2, cs=cs, data=data)
+
+class headerlessfixup(object):
+    def __init__(self, fh, h):
+        self._h = h
+        self._fh = fh
+    def read(self, n):
+        if self._h:
+            d, self._h = self._h[:n], self._h[n:]
+            if len(d) < n:
+                d += self._fh.read(n - len(d))
+            return d
+        return self._fh.read(n)
+
+def readbundle(fh, fname):
+    header = fh.read(6)
+
+    if not fname:
+        fname = "stream"
+        if not header.startswith('HG') and header.startswith('\0'):
+            fh = headerlessfixup(fh, header)
+            header = "HG10UN"
+
+    magic, version, alg = header[0:2], header[2:4], header[4:6]
+
+    if magic != 'HG':
+        raise util.Abort(_('%s: not a Mercurial bundle') % fname)
+    if version != '10':
+        raise util.Abort(_('%s: unknown bundle version %s') % (fname, version))
+    return unbundle10(fh, alg)