summaryrefslogtreecommitdiff
path: root/sys/src/cmd/hg/mercurial/changegroup.py
diff options
context:
space:
mode:
authorcinap_lenrek <cinap_lenrek@localhost>2011-05-03 11:25:13 +0000
committercinap_lenrek <cinap_lenrek@localhost>2011-05-03 11:25:13 +0000
commit458120dd40db6b4df55a4e96b650e16798ef06a0 (patch)
tree8f82685be24fef97e715c6f5ca4c68d34d5074ee /sys/src/cmd/hg/mercurial/changegroup.py
parent3a742c699f6806c1145aea5149bf15de15a0afd7 (diff)
add hg and python
Diffstat (limited to 'sys/src/cmd/hg/mercurial/changegroup.py')
-rw-r--r--sys/src/cmd/hg/mercurial/changegroup.py140
1 files changed, 140 insertions, 0 deletions
diff --git a/sys/src/cmd/hg/mercurial/changegroup.py b/sys/src/cmd/hg/mercurial/changegroup.py
new file mode 100644
index 000000000..a4ada4eb6
--- /dev/null
+++ b/sys/src/cmd/hg/mercurial/changegroup.py
@@ -0,0 +1,140 @@
+# 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, incorporated herein by reference.
+
+from i18n import _
+import util
+import struct, os, bz2, zlib, tempfile
+
+def getchunk(source):
+ """get a chunk from a changegroup"""
+ 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 chunkiter(source):
+ """iterate through the chunks in source"""
+ while 1:
+ c = getchunk(source)
+ if not c:
+ break
+ yield c
+
+def chunkheader(length):
+ """build a changegroup chunk header"""
+ return struct.pack(">l", length + 4)
+
+def closechunk():
+ 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()),
+}
+
+# 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 chunkiter is the end of the changegroup
+ # a changegroup has at least 2 chunkiters (changelog and manifest).
+ # after that, an empty chunkiter is the end of the changegroup
+ empty = False
+ count = 0
+ while not empty or count <= 2:
+ empty = True
+ count += 1
+ for chunk in chunkiter(cg):
+ 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 unbundle(header, fh):
+ if header == 'HG10UN':
+ return fh
+ elif not header.startswith('HG'):
+ # old client with uncompressed bundle
+ def generator(f):
+ yield header
+ for chunk in f:
+ yield chunk
+ elif header == 'HG10GZ':
+ def generator(f):
+ zd = zlib.decompressobj()
+ for chunk in f:
+ yield zd.decompress(chunk)
+ elif header == 'HG10BZ':
+ def generator(f):
+ zd = bz2.BZ2Decompressor()
+ zd.decompress("BZ")
+ for chunk in util.filechunkiter(f, 4096):
+ yield zd.decompress(chunk)
+ return util.chunkbuffer(generator(fh))
+
+def readbundle(fh, fname):
+ header = fh.read(6)
+ if not header.startswith('HG'):
+ raise util.Abort(_('%s: not a Mercurial bundle file') % fname)
+ if not header.startswith('HG10'):
+ raise util.Abort(_('%s: unknown bundle version') % fname)
+ elif header not in bundletypes:
+ raise util.Abort(_('%s: unknown bundle compression type') % fname)
+ return unbundle(header, fh)