changegroup: cg3 has two empty groups *after* manifests stable
authorMartin von Zweigbergk <martinvonz@google.com>
Tue, 19 Jan 2016 17:44:25 -0800
branchstable
changeset 27920 da5f23362517
parent 27919 db24d6888896
child 27921 158bdc896572
changegroup: cg3 has two empty groups *after* manifests changegroup.getchunks() determines the end of the stream by looking for an empty chunk group (two consecutive empty chunks). It ignores empty groups in the first two groups. Changegroup 3 introduced an empty chunk between the manifests and the files, which confuses getchunks(). Since it comes after the first two, getchunks() will stop there. Fix by rewriting getchunks so it first counts two groups (empty or not) and then keeps antostarts counting empty groups. With this counting, changegroup 1 and 2 have exactly one empty group after the first two groups, while changegroup 3 has two (one for directories and one for files). It's a little hard to test this at this point, but I have verified that this patch fixes narrowhg (which was broken before this patch). Also, future patches will fix "hg strip" with treemanifests, and once that's done, getchunks() will be tested through tests of "hg strip".
mercurial/changegroup.py
--- a/mercurial/changegroup.py	Tue Jan 19 06:00:59 2016 +0100
+++ b/mercurial/changegroup.py	Tue Jan 19 17:44:25 2016 -0800
@@ -189,6 +189,8 @@
     deltaheader = _CHANGEGROUPV1_DELTA_HEADER
     deltaheadersize = struct.calcsize(deltaheader)
     version = '01'
+    _grouplistcount = 1 # One list of files after the manifests
+
     def __init__(self, fh, alg):
         if alg == 'UN':
             alg = None # get more modern without breaking too much
@@ -270,15 +272,19 @@
         """
         # 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
+        # after that, changegroup versions 1 and 2 have a series of groups
+        # with one group per file. changegroup 3 has a series of directory
+        # manifests before the files.
         count = 0
-        while not empty or count <= 2:
+        emptycount = 0
+        while emptycount < self._grouplistcount:
             empty = True
             count += 1
             while True:
                 chunk = getchunk(self)
                 if not chunk:
+                    if empty and count > 2:
+                        emptycount += 1
                     break
                 empty = False
                 yield chunkheader(len(chunk))
@@ -515,6 +521,7 @@
     deltaheader = _CHANGEGROUPV3_DELTA_HEADER
     deltaheadersize = struct.calcsize(deltaheader)
     version = '03'
+    _grouplistcount = 2 # One list of manifests and one list of files
 
     def _deltaheader(self, headertuple, prevnode):
         node, p1, p2, deltabase, cs, flags = headertuple