Comments
Patch
@@ -58,10 +58,14 @@ Binary format is as follow
(size = 0) this is interpreted as the end of stream marker.
Currently forced to 0 in the current state of the implementation
"""
+import util
+import changegroup
+
+
_magicstring = 'HG20'
class bundler(object):
"""represent an outgoing bundle2 container
@@ -80,5 +84,50 @@ class bundler(object):
yield '\0\0'
# no support for parts
# to be obviously fixed soon.
assert not self._parts
yield '\0\0'
+
+class unbundler(object):
+ """interpret a bundle2 stream
+
+ (this will eventually yield parts)"""
+
+ def __init__(self, fp):
+ # assume the magic string is ok and drop it
+ # to be obviously fixed soon.
+ self._fp = fp
+ self._readexact(4)
+
+ def _unpack(self, format):
+ """unpack this struct format from the stream"""
+ data = self._readexact(struct.calcsize(format))
+ return _unpack(format, data)
+
+ def _readexact(self, size):
+ """read exactly <size> bytes from the stream"""
+ return changegroup.readexactly(self._fp, size)
+
+ @util.propertycache
+ def params(self):
+ """dictionnary of stream level parameters"""
+ paramsize = self._readexact(2)
+ assert paramsize == '\0\0'
+ return {}
+
+ def __iter__(self):
+ """yield all parts contained in the stream"""
+ # make sure param have been loaded
+ self.params
+ part = self._readpart()
+ while part is not None:
+ yield part
+ part = self._readpart()
+
+ def _readpart(self):
+ """return None when an end of stream markers is reach"""
+ headersize = self._readexact(2)
+ assert headersize == '\0\0'
+ return None
+
+
+
@@ -6,10 +6,11 @@ Create an extension to test bundle2 API
>
> Current bundle2 implementation is far too limited to be used in any core
> code. We still need to be able to test it while it grow up.
> """
>
+ > import sys
> from mercurial import cmdutil
> from mercurial import bundle2
> cmdtable = {}
> command = cmdutil.command(cmdtable)
>
@@ -17,10 +18,18 @@ Create an extension to test bundle2 API
> def cmdbundle2(ui, repo):
> """write a bundle2 container on standard ouput"""
> bundler = bundle2.bundler()
> for chunk in bundler.getchunks():
> ui.write(chunk)
+ >
+ > @command('unbundle2', [], '')
+ > def cmdunbundle2(ui, repo):
+ > """read a bundle2 container from standard input"""
+ > unbundler = bundle2.unbundler(sys.stdin)
+ > ui.write('options count: %i\n' % len(unbundler.params))
+ > parts = list(unbundler)
+ > ui.write('parts count: %i\n' % len(parts))
> EOF
$ cat >> $HGRCPATH << EOF
> [extensions]
> bundle2=$TESTTMP/bundle2.py
> EOF
@@ -32,5 +41,11 @@ The extension requires a repo (currently
Test simple generation of empty bundle
$ hg bundle2
HG20\x00\x00\x00\x00 (no-eol) (esc)
+
+Test parsing of an empty bundle
+
+ $ hg bundle2 | hg unbundle2
+ options count: 0
+ parts count: 0