mercurial/wireprotov2server.py
changeset 39630 9c2c77c73f23
parent 39559 07b58266bce3
child 39632 c1aacb0d76ff
--- a/mercurial/wireprotov2server.py	Wed Sep 12 09:58:23 2018 -0700
+++ b/mercurial/wireprotov2server.py	Wed Sep 12 10:01:16 2018 -0700
@@ -9,7 +9,11 @@
 import contextlib
 
 from .i18n import _
+from .node import (
+    nullid,
+)
 from . import (
+    discovery,
     encoding,
     error,
     pycompat,
@@ -460,6 +464,81 @@
 def capabilitiesv2(repo, proto):
     yield _capabilitiesv2(repo, proto)
 
+@wireprotocommand('changesetdata',
+                  args={
+                      'noderange': [[b'0123456...'], [b'abcdef...']],
+                      'nodes': [b'0123456...'],
+                      'fields': {b'parents', b'revision'},
+                  },
+                  permission='pull')
+def changesetdata(repo, proto, noderange=None, nodes=None, fields=None):
+    fields = fields or set()
+
+    if noderange is None and nodes is None:
+        raise error.WireprotoCommandError(
+            'noderange or nodes must be defined')
+
+    if noderange is not None:
+        if len(noderange) != 2:
+            raise error.WireprotoCommandError(
+                'noderange must consist of 2 elements')
+
+        if not noderange[1]:
+            raise error.WireprotoCommandError(
+                'heads in noderange request cannot be empty')
+
+    cl = repo.changelog
+    hasnode = cl.hasnode
+
+    seen = set()
+    outgoing = []
+
+    if nodes is not None:
+        outgoing.extend(n for n in nodes if hasnode(n))
+        seen |= set(outgoing)
+
+    if noderange is not None:
+        if noderange[0]:
+            common = [n for n in noderange[0] if hasnode(n)]
+        else:
+            common = [nullid]
+
+        for n in discovery.outgoing(repo, common, noderange[1]).missing:
+            if n not in seen:
+                outgoing.append(n)
+            # Don't need to add to seen here because this is the final
+            # source of nodes and there should be no duplicates in this
+            # list.
+
+    seen.clear()
+
+    if outgoing:
+        repo.hook('preoutgoing', throw=True, source='serve')
+
+    yield {
+        b'totalitems': len(outgoing),
+    }
+
+    # It is already topologically sorted by revision number.
+    for node in outgoing:
+        d = {
+            b'node': node,
+        }
+
+        if b'parents' in fields:
+            d[b'parents'] = cl.parents(node)
+
+        revisiondata = None
+
+        if b'revision' in fields:
+            revisiondata = cl.revision(node, raw=True)
+            d[b'revisionsize'] = len(revisiondata)
+
+        yield d
+
+        if revisiondata is not None:
+            yield revisiondata
+
 @wireprotocommand('heads',
                   args={
                       'publiconly': False,