[PATCH 2 of 4 V2] bundle2: support unbundling empty part

pierre-yves.david at ens-lyon.org pierre-yves.david at ens-lyon.org
Thu Mar 27 21:14:41 CDT 2014


# HG changeset patch
# User Pierre-Yves David <pierre-yves.david at fb.com>
# Date 1395295443 25200
#      Wed Mar 19 23:04:03 2014 -0700
# Node ID e7c977d822a8c7921510c034e250ed52233fff41
# Parent  f5be6f95d76a9369b441da680cf2a3dd959da1f7
bundle2: support unbundling empty part

We augment the unbundler to make it able to unbundle the empty part we are now
able to bundle.

diff --git a/mercurial/bundle2.py b/mercurial/bundle2.py
--- a/mercurial/bundle2.py
+++ b/mercurial/bundle2.py
@@ -238,23 +238,44 @@ class unbundle20(object):
             part = self._readpart()
         self.ui.debug('end of bundle2 stream\n')
 
     def _readpart(self):
         """return None when an end of stream markers is reach"""
-        headersize = self._readexact(2)
-        assert headersize == '\0\0'
-        return None
+
+        headersize = self._unpack(_fpartheadersize)[0]
+        self.ui.debug('part header size: %i\n' % headersize)
+        if not headersize:
+            return None
+        headerblock = self._readexact(headersize)
+        # some utility to help reading from the header bloc
+        def fromheader(size, _offset=[0]):
+            """return the next <size> byte from the header"""
+            offset = _offset[0] #small hack to make this mutable
+            data = headerblock[offset:(offset + size)]
+            _offset[0] = offset + size
+            return data
+        typesize = _unpack(_fparttypesize, fromheader(1))[0]
+        parttype = fromheader(typesize)
+        self.ui.debug('part type: "%s"\n' % parttype)
+        current = part(parttype)
+        assert fromheader(2) == '\0\0' # no option for now
+        self.ui.debug('part parameters: 0\n')
+        assert self._readexact(4) == '\0\0\0\0' #empty payload
+        self.ui.debug('payload chunk size: 0\n')
+        return current
+
 
 class part(object):
     """A bundle2 part contains application level payload
 
     The part `type` is used to route the part to the application level
     handler.
     """
 
-    def __init__(self, parttype):
+    def __init__(self, parttype, data=''):
         self.type = parttype
+        self.data = data
 
     def getchunks(self):
         ### header
         header = [_pack(_fparttypesize, len(self.type)),
                   self.type,
diff --git a/tests/test-bundle2.t b/tests/test-bundle2.t
--- a/tests/test-bundle2.t
+++ b/tests/test-bundle2.t
@@ -58,10 +58,12 @@ Create an extension to test bundle2 API
   >         value = params[key]
   >         if value is not None:
   >             ui.write('    %s\n' % value)
   >     parts = list(unbundler)
   >     ui.write('parts count:   %i\n' % len(parts))
+  >     for p in parts:
+  >         ui.write('  :%s:\n' % p.type)
   > EOF
   $ cat >> $HGRCPATH << EOF
   > [extensions]
   > bundle2=$TESTTMP/bundle2.py
   > EOF
@@ -204,10 +206,11 @@ unbundling debug
   options count: 2
   - e|! 7/
       babar%#==tutu
   - simple
   start extraction of bundle2 parts
+  part header size: 0
   end of bundle2 stream
   parts count:   0
 
 
 Test buggy input
@@ -241,5 +244,29 @@ Test part
   HG20\x00\x00\x00\r (esc)
   test:empty\x00\x00\x00\x00\x00\x00\x00\r (esc)
   test:empty\x00\x00\x00\x00\x00\x00\x00\x00 (no-eol) (esc)
 
 
+  $ hg unbundle2 < ../parts.hg2
+  options count: 0
+  parts count:   2
+    :test:empty:
+    :test:empty:
+
+  $ hg unbundle2 --debug < ../parts.hg2
+  start processing of HG20 stream
+  reading bundle2 stream parameters
+  options count: 0
+  start extraction of bundle2 parts
+  part header size: 13
+  part type: "test:empty"
+  part parameters: 0
+  payload chunk size: 0
+  part header size: 13
+  part type: "test:empty"
+  part parameters: 0
+  payload chunk size: 0
+  part header size: 0
+  end of bundle2 stream
+  parts count:   2
+    :test:empty:
+    :test:empty:


More information about the Mercurial-devel mailing list