revlog: add an aggressivemergedelta option
authorDurham Goode <durham@fb.com>
Sun, 30 Aug 2015 14:03:32 -0700
changeset 26118 049005de325e
parent 26117 4dc5b51f38fe
child 26123 bdac264e5ed4
revlog: add an aggressivemergedelta option This adds an option for delta'ing against both p1 and p2 when applying merge revisions and picking whichever is smallest. Some before and after stats on manifest.d size: internal large repo: before: 1.2 GB after: 930 MB mozilla-central: before: 261 MB after: 92 MB
mercurial/localrepo.py
mercurial/revlog.py
tests/test-generaldelta.t
--- a/mercurial/localrepo.py	Sun Aug 30 13:58:11 2015 -0700
+++ b/mercurial/localrepo.py	Sun Aug 30 14:03:32 2015 -0700
@@ -354,6 +354,10 @@
         manifestcachesize = self.ui.configint('format', 'manifestcachesize')
         if manifestcachesize is not None:
             self.svfs.options['manifestcachesize'] = manifestcachesize
+        # experimental config: format.aggressivemergedeltas
+        aggressivemergedeltas = self.ui.configbool('format',
+            'aggressivemergedeltas', False)
+        self.svfs.options['aggressivemergedeltas'] = aggressivemergedeltas
 
     def _writerequirements(self):
         scmutil.writerequires(self.vfs, self.requirements)
--- a/mercurial/revlog.py	Sun Aug 30 13:58:11 2015 -0700
+++ b/mercurial/revlog.py	Sun Aug 30 14:03:32 2015 -0700
@@ -210,6 +210,7 @@
         self._chunkcache = (0, '')
         self._chunkcachesize = 65536
         self._maxchainlen = None
+        self._aggressivemergedeltas = False
         self.index = []
         self._pcache = {}
         self._nodecache = {nullid: nullrev}
@@ -227,6 +228,8 @@
                 self._chunkcachesize = opts['chunkcachesize']
             if 'maxchainlen' in opts:
                 self._maxchainlen = opts['maxchainlen']
+            if 'aggressivemergedeltas' in opts:
+                self._aggressivemergedeltas = opts['aggressivemergedeltas']
 
         if self._chunkcachesize <= 0:
             raise RevlogError(_('revlog chunk cache size %r is not greater '
@@ -1343,15 +1346,34 @@
         # should we try to build a delta?
         if prev != nullrev:
             if self._generaldelta:
-                # Pick whichever parent is closer to us (to minimize the
-                # chance of having to build a fulltext). Since
-                # nullrev == -1, any non-merge commit will always pick p1r.
-                drev = p2r if p2r > p1r else p1r
-                d = builddelta(drev)
-                # If the chosen delta will result in us making a full text,
-                # give it one last try against prev.
-                if drev != prev and not self._isgooddelta(d, textlen):
-                    d = builddelta(prev)
+                if p2r != nullrev and self._aggressivemergedeltas:
+                    d = builddelta(p1r)
+                    d2 = builddelta(p2r)
+                    p1good = self._isgooddelta(d, textlen)
+                    p2good = self._isgooddelta(d2, textlen)
+                    if p1good and p2good:
+                        # If both are good deltas, choose the smallest
+                        if d2[1] < d[1]:
+                            d = d2
+                    elif p2good:
+                        # If only p2 is good, use it
+                        d = d2
+                    elif p1good:
+                        pass
+                    else:
+                        # Neither is good, try against prev to hopefully save us
+                        # a fulltext.
+                        d = builddelta(prev)
+                else:
+                    # Pick whichever parent is closer to us (to minimize the
+                    # chance of having to build a fulltext). Since
+                    # nullrev == -1, any non-merge commit will always pick p1r.
+                    drev = p2r if p2r > p1r else p1r
+                    d = builddelta(drev)
+                    # If the chosen delta will result in us making a full text,
+                    # give it one last try against prev.
+                    if drev != prev and not self._isgooddelta(d, textlen):
+                        d = builddelta(prev)
             else:
                 d = builddelta(prev)
             dist, l, data, base, chainbase, chainlen, compresseddeltalen = d
--- a/tests/test-generaldelta.t	Sun Aug 30 13:58:11 2015 -0700
+++ b/tests/test-generaldelta.t	Sun Aug 30 14:03:32 2015 -0700
@@ -69,3 +69,37 @@
      rev    offset  length   base linkrev nodeid       p1           p2
        0         0       3      0       1 1406e7411862 000000000000 000000000000
 
+  $ cd ..
+
+Test format.aggressivemergedeltas
+
+  $ hg init --config format.generaldelta=1 aggressive
+  $ cd aggressive
+  $ touch a b c d e
+  $ hg commit -Aqm side1
+  $ hg up -q null
+  $ touch x y
+  $ hg commit -Aqm side2
+
+- Verify non-aggressive merge uses p1 (commit 1) as delta parent
+  $ hg merge -q 0
+  $ hg commit -q -m merge
+  $ hg debugindex -m
+     rev    offset  length  delta linkrev nodeid       p1           p2
+       0         0      59     -1       0 8dde941edb6e 000000000000 000000000000
+       1        59      59     -1       1 315c023f341d 000000000000 000000000000
+       2       118      65      1       2 2ab389a983eb 315c023f341d 8dde941edb6e
+
+  $ hg strip -q -r . --config extensions.strip=
+
+- Verify aggressive merge uses p2 (commit 0) as delta parent
+  $ hg up -q -C 1
+  $ hg merge -q 0
+  $ hg commit -q -m merge --config format.aggressivemergedeltas=True
+  $ hg debugindex -m
+     rev    offset  length  delta linkrev nodeid       p1           p2
+       0         0      59     -1       0 8dde941edb6e 000000000000 000000000000
+       1        59      59     -1       1 315c023f341d 000000000000 000000000000
+       2       118      62      0       2 2ab389a983eb 315c023f341d 8dde941edb6e
+
+  $ cd ..