Add preliminary support for the bundle and unbundle commands
authormpm@selenic.com
Thu, 08 Sep 2005 01:27:25 -0700
changeset 1218 cde6818e082a
parent 1217 f1895785c79e
child 1219 56582bb2b869
Add preliminary support for the bundle and unbundle commands
doc/hg.1.txt
mercurial/commands.py
mercurial/revlog.py
--- a/doc/hg.1.txt	Thu Sep 08 00:13:52 2005 -0700
+++ b/doc/hg.1.txt	Thu Sep 08 01:27:25 2005 -0700
@@ -106,6 +106,21 @@
     -c, --changeset       list the changeset
     -n, --number          list the revision number (default)
 
+bundle <file> <other>::
+    (EXPERIMENTAL)
+
+    Generate a compressed changegroup file collecting all changesets
+    not found in the other repository.
+
+    This file can then be transferred using conventional means and
+    applied to another repository with the unbundle command. This is
+    useful when native push and pull are not available or when
+    exporting an entire repository is undesirable. The standard file
+    extension is ".hg".
+
+    Unlike import/export, this exactly preserves all changeset
+    contents including permissions, rename data, and revision history.
+
 cat <file> [revision]::
     Output to stdout the given revision for the specified file.
 
@@ -512,6 +527,12 @@
 tip::
     Show the tip revision.
 
+unbundle <file>::
+    (EXPERIMENTAL)
+
+    Apply a compressed changegroup file generated by the bundle
+    command.
+
 undo::
     Undo the last commit or pull transaction.
 
--- a/mercurial/commands.py	Thu Sep 08 00:13:52 2005 -0700
+++ b/mercurial/commands.py	Thu Sep 08 01:27:25 2005 -0700
@@ -7,10 +7,10 @@
 
 from demandload import demandload
 from node import *
-demandload(globals(), "os re sys signal shutil imp")
+demandload(globals(), "os re sys signal shutil imp urllib")
 demandload(globals(), "fancyopts ui hg util lock revlog")
 demandload(globals(), "fnmatch hgweb mdiff random signal time traceback")
-demandload(globals(), "errno socket version struct atexit sets")
+demandload(globals(), "errno socket version struct atexit sets bz2")
 
 class UnknownCommand(Exception):
     """Exception raised if command is not in the command table."""
@@ -549,6 +549,26 @@
             for p, l in zip(zip(*pieces), lines):
                 ui.write("%s: %s" % (" ".join(p), l[1]))
 
+def bundle(ui, repo, fname, dest="default-push", **opts):
+    """create a changegroup file"""
+    f = open(fname, "wb")
+    dest = ui.expandpath(dest)
+    other = hg.repository(ui, dest)
+    o = repo.findoutgoing(other)
+    cg = repo.changegroup(o)
+
+    try:
+        f.write("HG10")
+        z = bz2.BZ2Compressor(9)
+        while 1:
+            chunk = cg.read(4096)
+            if not chunk:
+                break
+            f.write(z.compress(chunk))
+        f.write(z.flush())
+    except:
+        os.unlink(fname)
+
 def cat(ui, repo, file1, rev=None, **opts):
     """output the latest or given revision of a file"""
     r = repo.file(relpath(repo, [file1])[0])
@@ -1534,6 +1554,30 @@
     n = repo.changelog.tip()
     show_changeset(ui, repo, changenode=n)
 
+def unbundle(ui, repo, fname):
+    f = urllib.urlopen(fname)
+
+    if f.read(4) != "HG10":
+        ui.warn("abort: not a Mercurial bundle file!\n")
+        return -1
+
+    class bzread:
+        def __init__(self, f):
+            self.zd = bz2.BZ2Decompressor()
+            self.f = f
+            self.buf = ""
+        def read(self, l):
+            while l > len(self.buf):
+                r = self.f.read(4096)
+                if r:
+                    self.buf += self.zd.decompress(r)
+                else:
+                    break
+            d, self.buf = self.buf[:l], self.buf[l:]
+            return d
+
+    repo.addchangegroup(bzread(f))
+
 def undo(ui, repo):
     """undo the last commit or pull
 
@@ -1610,6 +1654,10 @@
           ('I', 'include', [], 'include path in search'),
           ('X', 'exclude', [], 'exclude path from search')],
          'hg annotate [OPTION]... FILE...'),
+    "bundle":
+        (bundle,
+         [],
+         'hg bundle FILE DEST'),
     "cat":
         (cat,
          [('o', 'output', "", 'output to file')],
@@ -1776,6 +1824,10 @@
          'hg tag [OPTION]... NAME [REV]'),
     "tags": (tags, [], 'hg tags'),
     "tip": (tip, [], 'hg tip'),
+    "unbundle":
+        (unbundle,
+         [],
+         'hg unbundle FILE'),
     "undo": (undo, [], 'hg undo'),
     "^update|up|checkout|co":
         (update,
--- a/mercurial/revlog.py	Thu Sep 08 00:13:52 2005 -0700
+++ b/mercurial/revlog.py	Thu Sep 08 01:27:25 2005 -0700
@@ -604,8 +604,8 @@
             link = linkmapper(cs)
             if node in self.nodemap:
                 # this can happen if two branches make the same change
-                if unique:
-                    raise RevlogError("already have %s" % hex(node[:4]))
+                # if unique:
+                #    raise RevlogError("already have %s" % hex(node[:4]))
                 chain = node
                 continue
             delta = chunk[80:]