[PATCH] subrepo: archive git subrepos

Eric Eisner ede at MIT.EDU
Thu Nov 18 18:46:59 CST 2010


# HG changeset patch
# User Eric Eisner <ede at mit.edu>
# Date 1290126021 18000
# Node ID bf15f3eed02ea6af83c1be1d18404eb12d02b51e
# Parent  9efc316a67160831684bea85101616d971ed06b4
subrepo: archive git subrepos

diff --git a/mercurial/subrepo.py b/mercurial/subrepo.py
--- a/mercurial/subrepo.py
+++ b/mercurial/subrepo.py
@@ -6,7 +6,7 @@
 # GNU General Public License version 2 or any later version.
 
 import errno, os, re, xml.dom.minidom, shutil, urlparse, posixpath
-import stat, subprocess
+import stat, subprocess, tarfile
 from i18n import _
 import config, util, node, error, cmdutil
 hg = None
@@ -605,15 +605,15 @@ class gitsubrepo(object):
         self._path = ctx._repo.wjoin(path)
         self._ui = ctx._repo.ui
 
-    def _gitcommand(self, commands):
-        return self._gitdir(commands)[0]
+    def _gitcommand(self, commands, stream=False):
+        return self._gitdir(commands, stream=stream)[0]
 
-    def _gitdir(self, commands):
+    def _gitdir(self, commands, stream=False):
         commands = ['--no-pager', '--git-dir=%s/.git' % self._path,
                     '--work-tree=%s' % self._path] + commands
-        return self._gitnodir(commands)
+        return self._gitnodir(commands, stream=stream)
 
-    def _gitnodir(self, commands):
+    def _gitnodir(self, commands, stream=False):
         """Calls the git command
 
         The methods tries to call the git command. versions previor to 1.6.0
@@ -625,8 +625,11 @@ class gitsubrepo(object):
 
         # print git's stderr, which is mostly progress and useful info
         p = subprocess.Popen(cmd, shell=True, bufsize=-1,
-                             close_fds=(os.name == 'posix'),
+                             close_fds=util.closefds,
                              stdout=subprocess.PIPE)
+        if stream:
+            return p.stdout, None
+
         retdata = p.stdout.read()
         # wait for the child to exit to avoid race condition.
         p.wait()
@@ -787,6 +790,20 @@ class gitsubrepo(object):
             else:
                 os.remove(path)
 
+    def archive(self, archiver, prefix):
+        source, revision = self._state
+        self._fetch(source, revision)
+
+        # Parse git's native archive command.
+        # This should be much faster than manually traversing the trees
+        # and objects with many subprocess calls.
+        tarstream = self._gitcommand(['archive', revision], stream=True)
+        tar = tarfile.open(fileobj=tarstream, mode='r|')
+        for info in tar:
+            archiver.addfile(os.path.join(prefix, self._relpath, info.name),
+                             info.mode, info.issym(),
+                             tar.extractfile(info).read())
+
 types = {
     'hg': hgsubrepo,
     'svn': svnsubrepo,
diff --git a/tests/test-subrepo-git.t b/tests/test-subrepo-git.t
--- a/tests/test-subrepo-git.t
+++ b/tests/test-subrepo-git.t
@@ -193,3 +193,17 @@ update to a revision without the subrepo
   ..
   .git
   g
+
+archive subrepos
+
+  $ cd ../t
+  $ hg archive --subrepos -r tip ../archive
+  pulling subrepo s
+  $ cd ../archive
+  $ cat s/f
+  f
+  $ cat s/g
+  g
+  gg
+  ggg
+


More information about the Mercurial-devel mailing list