mercurial/exchange.py
changeset 26640 b13fdcc4e700
parent 26639 92d67e5729b9
child 26643 d2e16419d3f4
--- a/mercurial/exchange.py	Tue Oct 13 11:43:21 2015 -0700
+++ b/mercurial/exchange.py	Tue Oct 13 10:57:54 2015 -0700
@@ -15,58 +15,83 @@
 import tags
 import url as urlmod
 
-_bundlecompspecs = {'none': None,
-                    'bzip2': 'BZ',
-                    'gzip': 'GZ',
-                   }
+# Maps bundle compression human names to internal representation.
+_bundlespeccompressions = {'none': None,
+                           'bzip2': 'BZ',
+                           'gzip': 'GZ',
+                          }
 
-_bundleversionspecs = {'v1': '01',
-                       'v2': '02',
-                       'bundle2': '02', #legacy
-                      }
+# Maps bundle version human names to changegroup versions.
+_bundlespeccgversions = {'v1': '01',
+                         'v2': '02',
+                         'bundle2': '02', #legacy
+                        }
+
+def parsebundlespec(repo, spec, strict=True):
+    """Parse a bundle string specification into parts.
+
+    Bundle specifications denote a well-defined bundle/exchange format.
+    The content of a given specification should not change over time in
+    order to ensure that bundles produced by a newer version of Mercurial are
+    readable from an older version.
+
+    The string currently has the form:
 
-def parsebundlespec(repo, spec):
-    """return the internal bundle type to use from a user input
+       <compression>-<type>
+
+    Where <compression> is one of the supported compression formats
+    and <type> is (currently) a version string.
 
-    This is parsing user specified bundle type as accepted in:
+    If ``strict`` is True (the default) <compression> is required. Otherwise,
+    it is optional.
 
-        'hg bundle --type TYPE'.
+    Returns a 2-tuple of (compression, version). Compression will be ``None``
+    if not in strict mode and a compression isn't defined.
 
-    It accept format in the form [compression][-version]|[version]
+    An ``InvalidBundleSpecification`` is raised when the specification is
+    not syntactically well formed.
+
+    An ``UnsupportedBundleSpecification`` is raised when the compression or
+    bundle type/version is not recognized.
 
-    Consensus about extensions of the format for various bundle2 feature
-    is to prefix any feature with "+". eg "+treemanifest" or "gzip+phases"
+    Note: this function will likely eventually return a more complex data
+    structure, including bundle2 part information.
     """
-    comp, version = None, None
+    if strict and '-' not in spec:
+        raise error.InvalidBundleSpecification(
+                _('invalid bundle specification; '
+                  'must be prefixed with compression: %s') % spec)
 
     if '-' in spec:
-        comp, version = spec.split('-', 1)
-    elif spec in _bundlecompspecs:
-        comp = spec
-    elif spec in _bundleversionspecs:
-        version = spec
-    else:
-        raise error.Abort(_('unknown bundle type specified with --type'))
+        compression, version = spec.split('-', 1)
 
-    if comp is None:
-        comp = 'BZ'
+        if compression not in _bundlespeccompressions:
+            raise error.UnsupportedBundleSpecification(
+                    _('%s compression is not supported') % compression)
+
+        if version not in _bundlespeccgversions:
+            raise error.UnsupportedBundleSpecification(
+                    _('%s is not a recognized bundle version') % version)
     else:
-        try:
-            comp = _bundlecompspecs[comp]
-        except KeyError:
-            raise error.Abort(_('unknown bundle type specified with --type'))
+        # Value could be just the compression or just the version, in which
+        # case some defaults are assumed (but only when not in strict mode).
+        assert not strict
 
-    if version is None:
-        version = '01'
-        if 'generaldelta' in repo.requirements:
-            version = '02'
-    else:
-        try:
-            version = _bundleversionspecs[version]
-        except KeyError:
-            raise error.Abort(_('unknown bundle type specified with --type'))
+        if spec in _bundlespeccompressions:
+            compression = spec
+            version = 'v1'
+            if 'generaldelta' in repo.requirements:
+                version = 'v2'
+        elif spec in _bundlespeccgversions:
+            compression = 'bzip2'
+            version = spec
+        else:
+            raise error.UnsupportedBundleSpecification(
+                    _('%s is not a recognized bundle specification') % spec)
 
-    return version, comp
+    compression = _bundlespeccompressions[compression]
+    version = _bundlespeccgversions[version]
+    return compression, version
 
 def readbundle(ui, fh, fname, vfs=None):
     header = changegroup.readexactly(fh, 4)