streamclone: refactor code for deciding to stream clone
authorGregory Szorc <gregory.szorc@gmail.com>
Fri, 02 Oct 2015 22:22:11 -0700
changeset 26446 3ea10bb761ce
parent 26445 f134fb33c906
child 26447 591088f7028a
streamclone: refactor code for deciding to stream clone Having this in a standalone function will eventually enable bundle2 to share code with the bundle1 code path. While I was here, I also added some comments to add clarity.
mercurial/streamclone.py
--- a/mercurial/streamclone.py	Fri Oct 02 21:39:04 2015 -0700
+++ b/mercurial/streamclone.py	Fri Oct 02 22:22:11 2015 -0700
@@ -17,31 +17,58 @@
     util,
 )
 
-def maybeperformstreamclone(repo, remote, heads, stream):
-    # now, all clients that can request uncompressed clones can
-    # read repo formats supported by all servers that can serve
-    # them.
+def canperformstreamclone(repo, remote, heads, streamrequested=None):
+    """Whether it is possible to perform a streaming clone as part of pull.
 
-    # if revlog format changes, client will have to check version
-    # and format flags on "stream" capability, and use
-    # uncompressed only if compatible.
+    Returns a tuple of (supported, requirements). ``supported`` is True if
+    streaming clone is supported and False otherwise. ``requirements`` is
+    a set of repo requirements from the remote, or ``None`` if stream clone
+    isn't supported.
+    """
+    # Streaming clone only works if all data is being requested.
+    if heads:
+        return False, None
 
-    if stream is None:
-        # if the server explicitly prefers to stream (for fast LANs)
-        stream = remote.capable('stream-preferred')
+    # If we don't have a preference, let the server decide for us. This
+    # likely only comes into play in LANs.
+    if streamrequested is None:
+        # The server can advertise whether to prefer streaming clone.
+        streamrequested = remote.capable('stream-preferred')
+
+    if not streamrequested:
+        return False, None
 
-    if stream and not heads:
-        # 'stream' means remote revlog format is revlogv1 only
-        if remote.capable('stream'):
-            streamin(repo, remote, set(('revlogv1',)))
-        else:
-            # otherwise, 'streamreqs' contains the remote revlog format
-            streamreqs = remote.capable('streamreqs')
-            if streamreqs:
-                streamreqs = set(streamreqs.split(','))
-                # if we support it, stream in and adjust our requirements
-                if not streamreqs - repo.supportedformats:
-                    streamin(repo, remote, streamreqs)
+    # In order for stream clone to work, the client has to support all the
+    # requirements advertised by the server.
+    #
+    # The server advertises its requirements via the "stream" and "streamreqs"
+    # capability. "stream" (a value-less capability) is advertised if and only
+    # if the only requirement is "revlogv1." Else, the "streamreqs" capability
+    # is advertised and contains a comma-delimited list of requirements.
+    requirements = set()
+    if remote.capable('stream'):
+        requirements.add('revlogv1')
+    else:
+        streamreqs = remote.capable('streamreqs')
+        # This is weird and shouldn't happen with modern servers.
+        if not streamreqs:
+            return False, None
+
+        streamreqs = set(streamreqs.split(','))
+        # Server requires something we don't support. Bail.
+        if streamreqs - repo.supportedformats:
+            return False, None
+        requirements = streamreqs
+
+    return True, requirements
+
+def maybeperformstreamclone(repo, remote, heads, stream):
+    supported, requirements = canperformstreamclone(repo, remote, heads,
+                                                     streamrequested=stream)
+    if not supported:
+        return
+
+    streamin(repo, remote, requirements)
 
 def allowservergeneration(ui):
     """Whether streaming clones are allowed from the server."""