diff mercurial/bundle2.py @ 28666:ae53ecc47414

bundle: move writebundle() from changegroup.py to bundle2.py (API) writebundle() writes a bundle2 bundle or a plain changegroup1. Imagine away the "2" in "bundle2.py" for a moment and this change should makes sense. The bundle wraps the changegroup, so it makes sense that it knows about it. Another sign that this is correct is that the delayed import of bundle2 in changegroup goes away. I'll leave it for another time to remove the "2" in "bundle2.py" (alternatively, extract a new bundle.py from it).
author Martin von Zweigbergk <martinvonz@google.com>
date Mon, 28 Mar 2016 14:41:29 -0700
parents 88609cfa3745
children ca4896110011
line wrap: on
line diff
--- a/mercurial/bundle2.py	Mon Mar 28 17:16:00 2016 -0500
+++ b/mercurial/bundle2.py	Mon Mar 28 14:41:29 2016 -0700
@@ -474,6 +474,19 @@
         chunks.append(ca)
     return '\n'.join(chunks)
 
+bundletypes = {
+    "": ("", None),       # only when using unbundle on ssh and old http servers
+                          # since the unification ssh accepts a header but there
+                          # is no capability signaling it.
+    "HG20": (), # special-cased below
+    "HG10UN": ("HG10UN", None),
+    "HG10BZ": ("HG10", 'BZ'),
+    "HG10GZ": ("HG10GZ", 'GZ'),
+}
+
+# hgweb uses this list to communicate its preferred type
+bundlepriority = ['HG10GZ', 'HG10BZ', 'HG10UN']
+
 class bundle20(object):
     """represent an outgoing bundle2 container
 
@@ -1265,6 +1278,48 @@
     obscaps = caps.get('obsmarkers', ())
     return [int(c[1:]) for c in obscaps if c.startswith('V')]
 
+def writebundle(ui, cg, filename, bundletype, vfs=None, compression=None):
+    """Write a bundle file and return its filename.
+
+    Existing files will not be overwritten.
+    If no filename is specified, a temporary file is created.
+    bz2 compression can be turned off.
+    The bundle file will be deleted in case of errors.
+    """
+
+    if bundletype == "HG20":
+        bundle = bundle20(ui)
+        bundle.setcompression(compression)
+        part = bundle.newpart('changegroup', data=cg.getchunks())
+        part.addparam('version', cg.version)
+        chunkiter = bundle.getchunks()
+    else:
+        # compression argument is only for the bundle2 case
+        assert compression is None
+        if cg.version != '01':
+            raise error.Abort(_('old bundle types only supports v1 '
+                                'changegroups'))
+        header, comp = bundletypes[bundletype]
+        if comp not in util.compressors:
+            raise error.Abort(_('unknown stream compression type: %s')
+                              % comp)
+        z = util.compressors[comp]()
+        subchunkiter = cg.getchunks()
+        def chunkiter():
+            yield header
+            for chunk in subchunkiter:
+                yield z.compress(chunk)
+            yield z.flush()
+        chunkiter = chunkiter()
+
+    # parse the changegroup data, otherwise we will block
+    # in case of sshrepo because we don't know the end of the stream
+
+    # an empty chunkgroup is the end of the changegroup
+    # a changegroup has at least 2 chunkgroups (changelog and manifest).
+    # after that, an empty chunkgroup is the end of the changegroup
+    return changegroup.writechunks(ui, chunkiter, filename, vfs=vfs)
+
 @parthandler('changegroup', ('version', 'nbchanges', 'treemanifest'))
 def handlechangegroup(op, inpart):
     """apply a changegroup part on the repo