changegroup: don't send empty subdirectory manifest groups
authorMartin von Zweigbergk <martinvonz@google.com>
Thu, 16 Jun 2016 15:15:33 -0700
changeset 31842 1b699c7eb2b7341724e3efe289f376a954573e19
parent 31841 3ddf4d0c41702904a4966edba78765582f35bea5
child 31843 0b5e9a6250422f3458d7e81168f65904b7aaedc1
push id240
push usergszorc@mozilla.com
push dateFri, 24 Jun 2016 00:48:06 +0000
changegroup: don't send empty subdirectory manifest groups When grafting/rebasing, it is common for multiple changesets to make the same change to a subdirectory. When writing the revlog for the directory, the revlog code already takes care of not writing the entry again. In 0c2a088ffcc5 (changegroup: prune subdirectory dirlogs too, 2016-02-12), I added the corresponding code in changegroup (not sending entries the client already has), but I forgot to avoid sending the entire changegroup if no nodes remained in the pruned set. Although that's harmless besides the wasted network traffic, the receiving side was checking for it (copied from the changegroup code for handling files). This resulted in the client crashing with: abort: received dir revlog group is empty Fix by simply not emitting a changegroup for the directory if there were no changes is it. This matches how files are handled.
mercurial/changegroup.py
tests/test-treemanifest.t
--- a/mercurial/changegroup.py
+++ b/mercurial/changegroup.py
@@ -723,20 +723,21 @@ class cg1packer(object):
                 return clnode
             return lookupmflinknode
 
         size = 0
         while tmfnodes:
             dir = min(tmfnodes)
             nodes = tmfnodes[dir]
             prunednodes = self.prune(dirlog(dir), nodes, commonrevs)
-            for x in self._packmanifests(dir, prunednodes,
-                                         makelookupmflinknode(dir)):
-                size += len(x)
-                yield x
+            if not dir or prunednodes:
+                for x in self._packmanifests(dir, prunednodes,
+                                             makelookupmflinknode(dir)):
+                    size += len(x)
+                    yield x
             del tmfnodes[dir]
         self._verbosenote(_('%8.i (manifests)\n') % size)
         yield self._manifestsdone()
 
     # The 'source' parameter is useful for extensions
     def generatefiles(self, changedfiles, linknodes, commonrevs, source):
         repo = self._repo
         progress = self._progress
--- a/tests/test-treemanifest.t
+++ b/tests/test-treemanifest.t
@@ -737,8 +737,50 @@ Packed bundle
   $ hg debugbundle --spec repo-packed.hg
   none-packed1;requirements%3Dgeneraldelta%2Crevlogv1%2Ctreemanifest
 
 Bundle with changegroup2 is not supported
 
   $ hg -R deeprepo bundle --all -t v2 deeprepo.bundle
   abort: repository does not support bundle version 02
   [255]
+
+Pull does not include changegroup for manifest the client already has from
+other branch
+
+  $ mkdir grafted-dir-repo
+  $ cd grafted-dir-repo
+  $ hg --config experimental.treemanifest=1 init
+  $ mkdir dir
+  $ echo a > dir/file
+  $ echo a > file
+  $ hg ci -Am initial
+  adding dir/file
+  adding file
+  $ echo b > dir/file
+  $ hg ci -m updated
+  $ hg co '.^'
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg revert -r tip dir/
+  reverting dir/file (glob)
+  $ echo b > file # to make sure root manifest is sent
+  $ hg ci -m grafted
+  created new head
+  $ cd ..
+
+  $ hg --config experimental.treemanifest=1 clone --pull -r 1 \
+  >   grafted-dir-repo grafted-dir-repo-clone
+  adding changesets
+  adding manifests
+  adding file changes
+  added 2 changesets with 3 changes to 2 files
+  updating to branch default
+  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ cd grafted-dir-repo-clone
+  $ hg pull -r 2
+  pulling from $TESTTMP/grafted-dir-repo (glob)
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files (+1 heads)
+  (run 'hg heads' to see heads, 'hg merge' to merge)
+