mercurial/changegroup.py
changeset 38893 23d582caae30
parent 38892 eb022ce9e505
child 38894 19344024a8e1
--- a/mercurial/changegroup.py	Thu Aug 02 16:36:40 2018 -0700
+++ b/mercurial/changegroup.py	Fri Aug 03 10:05:26 2018 -0700
@@ -19,6 +19,10 @@
     short,
 )
 
+from .thirdparty import (
+    attr,
+)
+
 from . import (
     dagutil,
     error,
@@ -494,6 +498,26 @@
             return d
         return readexactly(self._fh, n)
 
+@attr.s(slots=True, frozen=True)
+class revisiondelta(object):
+    """Describes a delta entry in a changegroup.
+
+    Captured data is sufficient to serialize the delta into multiple
+    formats.
+    """
+    # 20 byte node of this revision.
+    node = attr.ib()
+    # 20 byte nodes of parent revisions.
+    p1node = attr.ib()
+    p2node = attr.ib()
+    # 20 byte node of node this delta is against.
+    basenode = attr.ib()
+    # 20 byte node of changeset revision this delta is associated with.
+    linknode = attr.ib()
+    # 2 bytes of flags to apply to revision data.
+    flags = attr.ib()
+    # Iterable of chunks holding raw delta data.
+    deltachunks = attr.ib()
 
 class cg1packer(object):
     deltaheader = _CHANGEGROUPV1_DELTA_HEADER
@@ -899,13 +923,25 @@
 
     def revchunk(self, store, rev, prev, linknode):
         if util.safehasattr(self, 'full_nodes'):
-            fn = self._revchunknarrow
+            fn = self._revisiondeltanarrow
         else:
-            fn = self._revchunknormal
+            fn = self._revisiondeltanormal
+
+        delta = fn(store, rev, prev, linknode)
+        if not delta:
+            return
 
-        return fn(store, rev, prev, linknode)
+        meta = self.builddeltaheader(delta.node, delta.p1node, delta.p2node,
+                                     delta.basenode, delta.linknode,
+                                     delta.flags)
+        l = len(meta) + sum(len(x) for x in delta.deltachunks)
 
-    def _revchunknormal(self, store, rev, prev, linknode):
+        yield chunkheader(l)
+        yield meta
+        for x in delta.deltachunks:
+            yield x
+
+    def _revisiondeltanormal(self, store, rev, prev, linknode):
         node = store.node(rev)
         p1, p2 = store.parentrevs(rev)
         base = self.deltaparent(store, rev, p1, p2, prev)
@@ -927,16 +963,18 @@
         else:
             delta = store.revdiff(base, rev)
         p1n, p2n = store.parents(node)
-        basenode = store.node(base)
-        flags = store.flags(rev)
-        meta = self.builddeltaheader(node, p1n, p2n, basenode, linknode, flags)
-        meta += prefix
-        l = len(meta) + len(delta)
-        yield chunkheader(l)
-        yield meta
-        yield delta
 
-    def _revchunknarrow(self, store, rev, prev, linknode):
+        return revisiondelta(
+            node=node,
+            p1node=p1n,
+            p2node=p2n,
+            basenode=store.node(base),
+            linknode=linknode,
+            flags=store.flags(rev),
+            deltachunks=(prefix, delta),
+        )
+
+    def _revisiondeltanarrow(self, store, rev, prev, linknode):
         # build up some mapping information that's useful later. See
         # the local() nested function below.
         if not self.changelog_done:
@@ -950,9 +988,7 @@
         # This is a node to send in full, because the changeset it
         # corresponds to was a full changeset.
         if linknode in self.full_nodes:
-            for x in self._revchunknormal(store, rev, prev, linknode):
-                yield x
-            return
+            return self._revisiondeltanormal(store, rev, prev, linknode)
 
         # At this point, a node can either be one we should skip or an
         # ellipsis. If it's not an ellipsis, bail immediately.
@@ -1043,16 +1079,20 @@
         p1n, p2n = store.node(p1), store.node(p2)
         flags = store.flags(rev)
         flags |= revlog.REVIDX_ELLIPSIS
-        meta = self.builddeltaheader(
-            n, p1n, p2n, nullid, linknode, flags)
+
         # TODO: try and actually send deltas for ellipsis data blocks
         data = store.revision(n)
         diffheader = mdiff.trivialdiffheader(len(data))
-        l = len(meta) + len(diffheader) + len(data)
-        yield ''.join((chunkheader(l),
-                       meta,
-                       diffheader,
-                       data))
+
+        return revisiondelta(
+            node=n,
+            p1node=p1n,
+            p2node=p2n,
+            basenode=nullid,
+            linknode=linknode,
+            flags=flags,
+            deltachunks=(diffheader, data),
+        )
 
     def builddeltaheader(self, node, p1n, p2n, basenode, linknode, flags):
         # do nothing with basenode, it is implicitly the previous one in HG10