subrepo: archive git subrepos
authorEric Eisner <ede@mit.edu>
Thu, 18 Nov 2010 19:20:21 -0500
changeset 13027 7f2ecb64140d
parent 13026 53391819f195
child 13028 687006f06e00
subrepo: archive git subrepos
mercurial/subrepo.py
tests/test-subrepo-git.t
--- a/mercurial/subrepo.py	Sun Nov 21 11:52:27 2010 +0100
+++ b/mercurial/subrepo.py	Thu Nov 18 19:20:21 2010 -0500
@@ -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
@@ -613,15 +613,15 @@
         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
@@ -633,8 +633,11 @@
 
         # 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()
@@ -795,6 +798,20 @@
             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,
--- a/tests/test-subrepo-git.t	Sun Nov 21 11:52:27 2010 +0100
+++ b/tests/test-subrepo-git.t	Thu Nov 18 19:20:21 2010 -0500
@@ -193,3 +193,17 @@
   ..
   .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
+