# HG changeset patch
# User Mateusz Kwapich <mitrandir@fb.com>
# Date 1458691511 25200
#      Tue Mar 22 17:05:11 2016 -0700
# Branch stable
# Node ID cdda7b96afff3433eafdeeb83ded83a5b25b7a5b
# Parent  197eed39e3d5e9a8cadfd9ba5839eb14cc265caa
convert: rewrite calls to Git to use the new shelling mechanism (SEC)

CVE-2016-3069 (2/5)

One test output changed because we were ignoring git return code in numcommits
before.

[jcristau: no numcommits in 2.2, so no test change]

--- mercurial-2.2.2.orig/hgext/convert/git.py
+++ mercurial-2.2.2/hgext/convert/git.py
@@ -72,23 +72,23 @@ class convert_git(converter_source, comm
 
         self.path = path
 
     def getheads(self):
         if not self.rev:
-            heads, ret = self.gitread('git rev-parse --branches --remotes')
-            heads = heads.splitlines()
+            output, status = self.gitrun('rev-parse', '--branches', '--remotes')
+            heads = output.splitlines()
         else:
-            heads, ret = self.gitread("git rev-parse --verify %s" % self.rev)
-            heads = [heads[:-1]]
-        if ret:
+            rawhead, status = self.gitrun('rev-parse', '--verify', self.rev)
+            heads = [rawhead[:-1]]
+        if status:
             raise util.Abort(_('cannot retrieve git heads'))
         return heads
 
     def catfile(self, rev, type):
         if rev == hex(nullid):
             raise IOError()
-        data, ret = self.gitread("git cat-file %s %s" % (type, rev))
+        data, ret = self.gitrun('cat-file', type, rev)
         if ret:
             raise util.Abort(_('cannot read %r object at %s') % (type, rev))
         return data
 
     def getfile(self, name, rev):
@@ -96,15 +96,17 @@ class convert_git(converter_source, comm
         mode = self.modecache[(name, rev)]
         return data, mode
 
     def getchanges(self, version):
         self.modecache = {}
-        fh = self.gitopen("git diff-tree -z --root -m -r %s" % version)
+        output, status = self.gitrun('diff-tree', '-z', '--root', '-m', '-r', version)
+        if status:
+            raise util.Abort(_('cannot read changes in %s') % version)
         changes = []
         seen = set()
         entry = None
-        for l in fh.read().split('\x00'):
+        for l in output.split('\x00'):
             if not entry:
                 if not l.startswith(':'):
                     continue
                 entry = l
                 continue
@@ -118,12 +120,10 @@ class convert_git(converter_source, comm
                 p = (entry[1] == "100755")
                 s = (entry[1] == "120000")
                 self.modecache[(f, h)] = (p and "x") or (s and "l") or ""
                 changes.append((f, h))
             entry = None
-        if fh.close():
-            raise util.Abort(_('cannot read changes in %s') % version)
         return (changes, {})
 
     def getcommit(self, version):
         c = self.catfile(version, "commit") # read the commit hash
         end = c.find("\n\n")
@@ -160,22 +160,23 @@ class convert_git(converter_source, comm
         return c
 
     def gettags(self):
         tags = {}
         alltags = {}
-        fh = self.gitopen('git ls-remote --tags "%s"' % self.path)
+        output, status = self.gitrunlines('ls-remote', '--tags', self.path)
+
+        if status:
+            raise util.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()
             node, tag = line.split(None, 1)
             if not tag.startswith(prefix):
                 continue
             alltags[tag[len(prefix):]] = node
-        if fh.close():
-            raise util.Abort(_('cannot read tags from %s') % self.path)
 
         # Filter out tag objects for annotated tag refs
         for tag in alltags:
             if tag.endswith('^{}'):
                 tags[tag[:-3]] = alltags[tag]
@@ -188,22 +189,26 @@ class convert_git(converter_source, comm
         return tags
 
     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 util.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 util.Abort(_('cannot read changes in %s') % version)
+            output, status = self.gitrunlines('diff-tree', '--name-only',
+                                              '--root', '-r', version,
+                                              '%s^%s' % (version, i + 1), '--')
+            if status:
+                raise util.Abort(_('cannot read changes in %s') % version)
+            changes = [f.rstrip('\n') for f in output]
 
         return changes
 
     def getbookmarks(self):
         bookmarks = {}
@@ -211,18 +216,18 @@ class convert_git(converter_source, comm
         # Interesting references in git are prefixed
         prefix = 'refs/heads/'
         prefixlen = len(prefix)
 
         # factor two commands
-        gitcmd = { 'remote/': 'git ls-remote --heads origin',
-                          '': 'git show-ref'}
+        gitcmd = { 'remote/': ['ls-remote', '--heads', 'origin'],
+                          '': ['show-ref']}
 
         # Origin heads
         for reftype in gitcmd:
             try:
-                fh = self.gitopen(gitcmd[reftype], noerr=True)
-                for line in fh:
+                output, status = self.gitrunlines(*gitcmd[reftype])
+                for line in output:
                     line = line.strip()
                     rev, name = line.split(None, 1)
                     if not name.startswith(prefix):
                         continue
                     name = '%s%s' % (reftype, name[prefixlen:])
