diff hgext/convert/git.py @ 28670:ff0d3b6b287f

merge with stable
author Matt Mackall <mpm@selenic.com>
date Tue, 29 Mar 2016 12:29:00 -0500
parents cd599bc179fb 80cac1de6aea
children 96ed01f6514b
line wrap: on
line diff
--- a/hgext/convert/git.py	Fri Mar 25 23:05:32 2016 -0700
+++ b/hgext/convert/git.py	Tue Mar 29 12:29:00 2016 -0500
@@ -32,61 +32,28 @@
     def hgsubstate(self):
         return "%s %s" % (self.node, self.path)
 
-class convert_git(common.converter_source):
+class convert_git(common.converter_source, common.commandline):
     # Windows does not support GIT_DIR= construct while other systems
     # cannot remove environment variable. Just assume none have
     # both issues.
-    if util.safehasattr(os, 'unsetenv'):
-        def gitopen(self, s, err=None):
-            prevgitdir = os.environ.get('GIT_DIR')
-            os.environ['GIT_DIR'] = self.path
-            try:
-                if err == subprocess.PIPE:
-                    (stdin, stdout, stderr) = util.popen3(s)
-                    return stdout
-                elif err == subprocess.STDOUT:
-                    return self.popen_with_stderr(s)
-                else:
-                    return util.popen(s, 'rb')
-            finally:
-                if prevgitdir is None:
-                    del os.environ['GIT_DIR']
-                else:
-                    os.environ['GIT_DIR'] = prevgitdir
+
+    def _gitcmd(self, cmd, *args, **kwargs):
+        return cmd('--git-dir=%s' % self.path, *args, **kwargs)
+
+    def gitrun0(self, *args, **kwargs):
+        return self._gitcmd(self.run0, *args, **kwargs)
 
-        def gitpipe(self, s):
-            prevgitdir = os.environ.get('GIT_DIR')
-            os.environ['GIT_DIR'] = self.path
-            try:
-                return util.popen3(s)
-            finally:
-                if prevgitdir is None:
-                    del os.environ['GIT_DIR']
-                else:
-                    os.environ['GIT_DIR'] = prevgitdir
+    def gitrun(self, *args, **kwargs):
+        return self._gitcmd(self.run, *args, **kwargs)
+
+    def gitrunlines0(self, *args, **kwargs):
+        return self._gitcmd(self.runlines0, *args, **kwargs)
 
-    else:
-        def gitopen(self, s, err=None):
-            if err == subprocess.PIPE:
-                (sin, so, se) = util.popen3('GIT_DIR=%s %s' % (self.path, s))
-                return so
-            elif err == subprocess.STDOUT:
-                    return self.popen_with_stderr(s)
-            else:
-                return util.popen('GIT_DIR=%s %s' % (self.path, s), 'rb')
+    def gitrunlines(self, *args, **kwargs):
+        return self._gitcmd(self.runlines, *args, **kwargs)
 
-        def gitpipe(self, s):
-            return util.popen3('GIT_DIR=%s %s' % (self.path, s))
-
-    def popen_with_stderr(self, s):
-        p = subprocess.Popen(s, shell=True, bufsize=-1,
-                             close_fds=util.closefds,
-                             stdin=subprocess.PIPE,
-                             stdout=subprocess.PIPE,
-                             stderr=subprocess.STDOUT,
-                             universal_newlines=False,
-                             env=None)
-        return p.stdout
+    def gitpipe(self, *args, **kwargs):
+        return self._gitcmd(self._run3, *args, **kwargs)
 
     def gitread(self, s):
         fh = self.gitopen(s)
@@ -95,6 +62,7 @@
 
     def __init__(self, ui, path, revs=None):
         super(convert_git, self).__init__(ui, path, revs=revs)
+        common.commandline.__init__(self, ui, 'git')
 
         if os.path.isdir(path + "/.git"):
             path += "/.git"
@@ -107,20 +75,20 @@
         if similarity < 0 or similarity > 100:
             raise error.Abort(_('similarity must be between 0 and 100'))
         if similarity > 0:
-            self.simopt = '-C%d%%' % similarity
+            self.simopt = ['-C%d%%' % similarity]
             findcopiesharder = ui.configbool('convert', 'git.findcopiesharder',
                                              False)
             if findcopiesharder:
-                self.simopt += ' --find-copies-harder'
+                self.simopt.append('--find-copies-harder')
         else:
-            self.simopt = ''
+            self.simopt = []
 
         common.checktool('git', 'git')
 
         self.path = path
         self.submodules = []
 
-        self.catfilepipe = self.gitpipe('git cat-file --batch')
+        self.catfilepipe = self.gitpipe('cat-file', '--batch')
 
     def after(self):
         for f in self.catfilepipe:
@@ -128,14 +96,14 @@
 
     def getheads(self):
         if not self.revs:
-            heads, ret = self.gitread('git rev-parse --branches --remotes')
-            heads = heads.splitlines()
-            if ret:
+            output, status = self.gitrun('rev-parse', '--branches', '--remotes')
+            heads = output.splitlines()
+            if status:
                 raise error.Abort(_('cannot retrieve git heads'))
         else:
             heads = []
             for rev in self.revs:
-                rawhead, ret = self.gitread("git rev-parse --verify %s" % rev)
+                rawhead, ret = self.gitrun('rev-parse', '--verify', rev)
                 heads.append(rawhead[:-1])
                 if ret:
                     raise error.Abort(_('cannot retrieve git head "%s"') % rev)
@@ -195,7 +163,7 @@
                 self.submodules.append(submodule(s['path'], '', s['url']))
 
     def retrievegitmodules(self, version):
-        modules, ret = self.gitread("git show %s:%s" % (version, '.gitmodules'))
+        modules, ret = self.gitrun('show', '%s:%s' % (version, '.gitmodules'))
         if ret:
             # This can happen if a file is in the repo that has permissions
             # 160000, but there is no .gitmodules file.
@@ -211,7 +179,7 @@
             return
 
         for m in self.submodules:
-            node, ret = self.gitread("git rev-parse %s:%s" % (version, m.path))
+            node, ret = self.gitrun('rev-parse', '%s:%s' % (version, m.path))
             if ret:
                 continue
             m.node = node.strip()
@@ -220,15 +188,17 @@
         if full:
             raise error.Abort(_("convert from git does not support --full"))
         self.modecache = {}
-        fh = self.gitopen("git diff-tree -z --root -m -r %s %s" % (
-            self.simopt, version))
+        cmd = ['diff-tree','-z', '--root', '-m', '-r'] + self.simopt + [version]
+        output, status = self.gitrun(*cmd)
+        if status:
+            raise error.Abort(_('cannot read changes in %s') % version)
         changes = []
         copies = {}
         seen = set()
         entry = None
         subexists = [False]
         subdeleted = [False]
-        difftree = fh.read().split('\x00')
+        difftree = output.split('\x00')
         lcount = len(difftree)
         i = 0
 
@@ -290,8 +260,6 @@
                     if f != '.gitmodules' and fdest != '.gitmodules':
                         copies[fdest] = f
             entry = None
-        if fh.close():
-            raise error.Abort(_('cannot read changes in %s') % version)
 
         if subexists[0]:
             if subdeleted[0]:
@@ -338,17 +306,23 @@
         return c
 
     def numcommits(self):
-        return len([None for _ in self.gitopen('git rev-list --all')])
+        output, ret = self.gitrunlines('rev-list', '--all')
+        if ret:
+            raise error.Abort(_('cannot retrieve number of commits in %s') \
+                              % self.path)
+        return len(output)
 
     def gettags(self):
         tags = {}
         alltags = {}
-        fh = self.gitopen('git ls-remote --tags "%s"' % self.path,
-                          err=subprocess.STDOUT)
+        output, status = self.gitrunlines('ls-remote', '--tags', self.path)
+
+        if status:
+            raise error.Abort(_('cannot read tags from %s') % self.path)
         prefix = 'refs/tags/'
 
         # Build complete list of tags, both annotated and bare ones
-        for line in fh:
+        for line in output:
             line = line.strip()
             if line.startswith("error:") or line.startswith("fatal:"):
                 raise error.Abort(_('cannot read tags from %s') % self.path)
@@ -356,8 +330,6 @@
             if not tag.startswith(prefix):
                 continue
             alltags[tag[len(prefix):]] = node
-        if fh.close():
-            raise error.Abort(_('cannot read tags from %s') % self.path)
 
         # Filter out tag objects for annotated tag refs
         for tag in alltags:
@@ -374,18 +346,20 @@
     def getchangedfiles(self, version, i):
         changes = []
         if i is None:
-            fh = self.gitopen("git diff-tree --root -m -r %s" % version)
-            for l in fh:
+            output, status = self.gitrunlines('diff-tree', '--root', '-m',
+                                              '-r', version)
+            if status:
+                raise error.Abort(_('cannot read changes in %s') % version)
+            for l in output:
                 if "\t" not in l:
                     continue
                 m, f = l[:-1].split("\t")
                 changes.append(f)
         else:
-            fh = self.gitopen('git diff-tree --name-only --root -r %s '
-                              '"%s^%s" --' % (version, version, i + 1))
-            changes = [f.rstrip('\n') for f in fh]
-        if fh.close():
-            raise error.Abort(_('cannot read changes in %s') % version)
+            output, status = self.gitrunlines('diff-tree', '--name-only',
+                                              '--root', '-r', version,
+                                              '%s^%s' % (version, i + 1), '--')
+            changes = [f.rstrip('\n') for f in output]
 
         return changes
 
@@ -405,8 +379,8 @@
         ])
 
         try:
-            fh = self.gitopen('git show-ref', err=subprocess.PIPE)
-            for line in fh:
+            output, status = self.gitrunlines('show-ref')
+            for line in output:
                 line = line.strip()
                 rev, name = line.split(None, 1)
                 # Process each type of branch