Patchwork [2,of,5] clonebundles: filter on bundle type

login
register
mail settings
Submitter Gregory Szorc
Date Sept. 30, 2015, 12:48 a.m.
Message ID <faf364dfab7c2861b712.1443574125@gps-mbp.local>
Download mbox | patch
Permalink /patch/10694/
State Deferred
Delegated to: Augie Fackler
Headers show

Comments

Gregory Szorc - Sept. 30, 2015, 12:48 a.m.
# HG changeset patch
# User Gregory Szorc <gregory.szorc@gmail.com>
# Date 1443573277 25200
#      Tue Sep 29 17:34:37 2015 -0700
# Node ID faf364dfab7c2861b712b7e9548b832f3c222bba
# Parent  f2353a7a0ac6388085300648ce34325c4b7d9030
clonebundles: filter on bundle type

Not all clients support all bundle types. This patch provides a
mechanism to advertise the type of a bundle at a URL. If the type
is unknown to the client, it ignores it.

There is necessary follow-up work to advertise bundle2 requirements as
well, since bundle2 bundles may contain parts that clients don't know
how to process.

Patch

diff --git a/hgext/clonebundles.py b/hgext/clonebundles.py
--- a/hgext/clonebundles.py
+++ b/hgext/clonebundles.py
@@ -31,9 +31,19 @@  can be used by site installations.
 The server operator is responsible for generating the bundle manifest file.
 
 Metadata Attributes:
 
-TBD
+TYPE
+   The type of the bundle. This is one of the strings from
+   mercurial.changegroup.py:bundletypes, which are also the headers of
+   bundle files. Example values include "HG20" and "HG10GZ".
+
+   Clients will automatically filter out bundle types they do not
+   know how to consume.
+
+   The actual value doesn't impact client behavior beyond filtering:
+   clients will still sniff the bundle type from downloaded content.
+   We reuse the file header values for consistency.
 """
 
 from mercurial import (
     extensions,
diff --git a/mercurial/exchange.py b/mercurial/exchange.py
--- a/mercurial/exchange.py
+++ b/mercurial/exchange.py
@@ -1578,17 +1578,28 @@  def maybeapplyclonebundle(repo, remote):
     """Apply a clone bundle from a remote, if possible."""
 
     res = remote._call('clonebundles')
     entries = parseclonebundlesmanifest(res)
-
-    # TODO filter entries by supported features.
-    # TODO sort entries by user preferences.
-
     if not entries:
         repo.ui.note(_('no clone bundles advertised on server; '
                        'falling back to regular clone\n'))
         return
 
+    entries = filterclonebundleentries(repo.ui, entries)
+    if not entries:
+        # There is a thundering herd concern here. However, if a server
+        # operator doesn't advertise bundles appropriate for its clients,
+        # they deserve what's coming. Furthermore, from a client's
+        # perspective, no automatic fallback would mean not being able to
+        # clone!
+        repo.ui.warn(_('no compatible clone bundles available on server; '
+                       'falling back to regular clone\n'))
+        repo.ui.warn(_('(you may want to report this to the server '
+                       'operator)\n'))
+        return
+
+    # TODO sort entries by user preferences.
+
     url = entries[0]['URL']
     repo.ui.status(_('applying clone bundle from %s\n') % url)
     if trypullbundlefromurl(repo.ui, repo, url):
         repo.ui.status(_('finished applying clone bundle\n'))
@@ -1623,8 +1634,20 @@  def parseclonebundlesmanifest(s):
         m.append(attrs)
 
     return m
 
+def filterclonebundleentries(ui, entries):
+    newentries = []
+    for e in entries:
+        if 'TYPE' in e and e['TYPE'] not in changegroup.bundletypes:
+            ui.debug('filtering %s because unknown bundle type %s\n' %
+                       (e['URL'], e['TYPE']))
+            continue
+
+        newentries.append(e)
+
+    return newentries
+
 def trypullbundlefromurl(ui, repo, url):
     """Attempt to apply a bundle from a URL."""
     lock = repo.lock()
     try:
diff --git a/tests/test-clonebundles.t b/tests/test-clonebundles.t
--- a/tests/test-clonebundles.t
+++ b/tests/test-clonebundles.t
@@ -113,4 +113,36 @@  Bundle with full content works
   added 2 changesets with 2 changes to 2 files
   finished applying clone bundle
   searching for changes
   no changes found
+
+Entry with unknown TYPE is filtered and not used
+
+  $ cat > server/.hg/clonebundles.manifest << EOF
+  > http://bad.entry TYPE=UNKNOWN
+  > http://localhost:$HGPORT1/full.hg TYPE=HG10GZ
+  > EOF
+
+  $ hg clone -U http://localhost:$HGPORT filter-unknown-type
+  applying clone bundle from http://localhost:$HGPORT1/full.hg
+  adding changesets
+  adding manifests
+  adding file changes
+  added 2 changesets with 2 changes to 2 files
+  finished applying clone bundle
+  searching for changes
+  no changes found
+
+Automatic fallback when all entries are filtered
+
+  $ cat > server/.hg/clonebundles.manifest << EOF
+  > http://bad.entry TYPE=UNKNOWN
+  > EOF
+
+  $ hg clone -U http://localhost:$HGPORT filter-all
+  no compatible clone bundles available on server; falling back to regular clone
+  (you may want to report this to the server operator)
+  requesting all changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 2 changesets with 2 changes to 2 files