comparison hgext/convert/git.py @ 28660:cdda7b96afff stable

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.
author Mateusz Kwapich <mitrandir@fb.com>
date Tue, 22 Mar 2016 17:05:11 -0700
parents 197eed39e3d5
children b732e7f2aba4
comparison
equal deleted inserted replaced
28659:197eed39e3d5 28660:cdda7b96afff
113 # The default value (50) is based on the default for 'git diff'. 113 # The default value (50) is based on the default for 'git diff'.
114 similarity = ui.configint('convert', 'git.similarity', default=50) 114 similarity = ui.configint('convert', 'git.similarity', default=50)
115 if similarity < 0 or similarity > 100: 115 if similarity < 0 or similarity > 100:
116 raise error.Abort(_('similarity must be between 0 and 100')) 116 raise error.Abort(_('similarity must be between 0 and 100'))
117 if similarity > 0: 117 if similarity > 0:
118 self.simopt = '-C%d%%' % similarity 118 self.simopt = ['-C%d%%' % similarity]
119 findcopiesharder = ui.configbool('convert', 'git.findcopiesharder', 119 findcopiesharder = ui.configbool('convert', 'git.findcopiesharder',
120 False) 120 False)
121 if findcopiesharder: 121 if findcopiesharder:
122 self.simopt += ' --find-copies-harder' 122 self.simopt.append('--find-copies-harder')
123 else: 123 else:
124 self.simopt = '' 124 self.simopt = []
125 125
126 checktool('git', 'git') 126 checktool('git', 'git')
127 127
128 self.path = path 128 self.path = path
129 self.submodules = [] 129 self.submodules = []
134 for f in self.catfilepipe: 134 for f in self.catfilepipe:
135 f.close() 135 f.close()
136 136
137 def getheads(self): 137 def getheads(self):
138 if not self.revs: 138 if not self.revs:
139 heads, ret = self.gitread('git rev-parse --branches --remotes') 139 output, status = self.gitrun('rev-parse', '--branches', '--remotes')
140 heads = heads.splitlines() 140 heads = output.splitlines()
141 if ret: 141 if status:
142 raise error.Abort(_('cannot retrieve git heads')) 142 raise error.Abort(_('cannot retrieve git heads'))
143 else: 143 else:
144 heads = [] 144 heads = []
145 for rev in self.revs: 145 for rev in self.revs:
146 rawhead, ret = self.gitread("git rev-parse --verify %s" % rev) 146 rawhead, ret = self.gitrun('rev-parse', '--verify', rev)
147 heads.append(rawhead[:-1]) 147 heads.append(rawhead[:-1])
148 if ret: 148 if ret:
149 raise error.Abort(_('cannot retrieve git head "%s"') % rev) 149 raise error.Abort(_('cannot retrieve git head "%s"') % rev)
150 return heads 150 return heads
151 151
201 s = c[sec] 201 s = c[sec]
202 if 'url' in s and 'path' in s: 202 if 'url' in s and 'path' in s:
203 self.submodules.append(submodule(s['path'], '', s['url'])) 203 self.submodules.append(submodule(s['path'], '', s['url']))
204 204
205 def retrievegitmodules(self, version): 205 def retrievegitmodules(self, version):
206 modules, ret = self.gitread("git show %s:%s" % (version, '.gitmodules')) 206 modules, ret = self.gitrun('show', '%s:%s' % (version, '.gitmodules'))
207 if ret: 207 if ret:
208 # This can happen if a file is in the repo that has permissions 208 # This can happen if a file is in the repo that has permissions
209 # 160000, but there is no .gitmodules file. 209 # 160000, but there is no .gitmodules file.
210 self.ui.warn(_("warning: cannot read submodules config file in " 210 self.ui.warn(_("warning: cannot read submodules config file in "
211 "%s\n") % version) 211 "%s\n") % version)
217 self.ui.warn(_("warning: unable to parse .gitmodules in %s\n") 217 self.ui.warn(_("warning: unable to parse .gitmodules in %s\n")
218 % version) 218 % version)
219 return 219 return
220 220
221 for m in self.submodules: 221 for m in self.submodules:
222 node, ret = self.gitread("git rev-parse %s:%s" % (version, m.path)) 222 node, ret = self.gitrun('rev-parse', '%s:%s' % (version, m.path))
223 if ret: 223 if ret:
224 continue 224 continue
225 m.node = node.strip() 225 m.node = node.strip()
226 226
227 def getchanges(self, version, full): 227 def getchanges(self, version, full):
228 if full: 228 if full:
229 raise error.Abort(_("convert from git does not support --full")) 229 raise error.Abort(_("convert from git does not support --full"))
230 self.modecache = {} 230 self.modecache = {}
231 fh = self.gitopen("git diff-tree -z --root -m -r %s %s" % ( 231 cmd = ['diff-tree','-z', '--root', '-m', '-r'] + self.simopt + [version]
232 self.simopt, version)) 232 output, status = self.gitrun(*cmd)
233 if status:
234 raise error.Abort(_('cannot read changes in %s') % version)
233 changes = [] 235 changes = []
234 copies = {} 236 copies = {}
235 seen = set() 237 seen = set()
236 entry = None 238 entry = None
237 subexists = [False] 239 subexists = [False]
238 subdeleted = [False] 240 subdeleted = [False]
239 difftree = fh.read().split('\x00') 241 difftree = output.split('\x00')
240 lcount = len(difftree) 242 lcount = len(difftree)
241 i = 0 243 i = 0
242 244
243 skipsubmodules = self.ui.configbool('convert', 'git.skipsubmodules', 245 skipsubmodules = self.ui.configbool('convert', 'git.skipsubmodules',
244 False) 246 False)
296 # .gitmodules isn't imported at all, so it being copied to 298 # .gitmodules isn't imported at all, so it being copied to
297 # and fro doesn't really make sense 299 # and fro doesn't really make sense
298 if f != '.gitmodules' and fdest != '.gitmodules': 300 if f != '.gitmodules' and fdest != '.gitmodules':
299 copies[fdest] = f 301 copies[fdest] = f
300 entry = None 302 entry = None
301 if fh.close():
302 raise error.Abort(_('cannot read changes in %s') % version)
303 303
304 if subexists[0]: 304 if subexists[0]:
305 if subdeleted[0]: 305 if subdeleted[0]:
306 changes.append(('.hgsubstate', hex(nullid))) 306 changes.append(('.hgsubstate', hex(nullid)))
307 else: 307 else:
343 c = commit(parents=parents, date=date, author=author, desc=message, 343 c = commit(parents=parents, date=date, author=author, desc=message,
344 rev=version) 344 rev=version)
345 return c 345 return c
346 346
347 def numcommits(self): 347 def numcommits(self):
348 return len([None for _ in self.gitopen('git rev-list --all')]) 348 output, ret = self.gitrunlines('rev-list', '--all')
349 if ret:
350 raise error.Abort(_('cannot retrieve number of commits in %s') \
351 % self.path)
352 return len(output)
349 353
350 def gettags(self): 354 def gettags(self):
351 tags = {} 355 tags = {}
352 alltags = {} 356 alltags = {}
353 fh = self.gitopen('git ls-remote --tags "%s"' % self.path, 357 output, status = self.gitrunlines('ls-remote', '--tags', self.path)
354 err=subprocess.STDOUT) 358
359 if status:
360 raise error.Abort(_('cannot read tags from %s') % self.path)
355 prefix = 'refs/tags/' 361 prefix = 'refs/tags/'
356 362
357 # Build complete list of tags, both annotated and bare ones 363 # Build complete list of tags, both annotated and bare ones
358 for line in fh: 364 for line in output:
359 line = line.strip() 365 line = line.strip()
360 if line.startswith("error:") or line.startswith("fatal:"): 366 if line.startswith("error:") or line.startswith("fatal:"):
361 raise error.Abort(_('cannot read tags from %s') % self.path) 367 raise error.Abort(_('cannot read tags from %s') % self.path)
362 node, tag = line.split(None, 1) 368 node, tag = line.split(None, 1)
363 if not tag.startswith(prefix): 369 if not tag.startswith(prefix):
364 continue 370 continue
365 alltags[tag[len(prefix):]] = node 371 alltags[tag[len(prefix):]] = node
366 if fh.close():
367 raise error.Abort(_('cannot read tags from %s') % self.path)
368 372
369 # Filter out tag objects for annotated tag refs 373 # Filter out tag objects for annotated tag refs
370 for tag in alltags: 374 for tag in alltags:
371 if tag.endswith('^{}'): 375 if tag.endswith('^{}'):
372 tags[tag[:-3]] = alltags[tag] 376 tags[tag[:-3]] = alltags[tag]
379 return tags 383 return tags
380 384
381 def getchangedfiles(self, version, i): 385 def getchangedfiles(self, version, i):
382 changes = [] 386 changes = []
383 if i is None: 387 if i is None:
384 fh = self.gitopen("git diff-tree --root -m -r %s" % version) 388 output, status = self.gitrunlines('diff-tree', '--root', '-m',
385 for l in fh: 389 '-r', version)
390 if status:
391 raise error.Abort(_('cannot read changes in %s') % version)
392 for l in output:
386 if "\t" not in l: 393 if "\t" not in l:
387 continue 394 continue
388 m, f = l[:-1].split("\t") 395 m, f = l[:-1].split("\t")
389 changes.append(f) 396 changes.append(f)
390 else: 397 else:
391 fh = self.gitopen('git diff-tree --name-only --root -r %s ' 398 output, status = self.gitrunlines('diff-tree', '--name-only',
392 '"%s^%s" --' % (version, version, i + 1)) 399 '--root', '-r', version,
393 changes = [f.rstrip('\n') for f in fh] 400 '%s^%s' % (version, i + 1), '--')
394 if fh.close(): 401 changes = [f.rstrip('\n') for f in output]
395 raise error.Abort(_('cannot read changes in %s') % version)
396 402
397 return changes 403 return changes
398 404
399 def getbookmarks(self): 405 def getbookmarks(self):
400 bookmarks = {} 406 bookmarks = {}
410 exclude = set([ 416 exclude = set([
411 'refs/remotes/origin/HEAD', 417 'refs/remotes/origin/HEAD',
412 ]) 418 ])
413 419
414 try: 420 try:
415 fh = self.gitopen('git show-ref', err=subprocess.PIPE) 421 output, status = self.gitrunlines('show-ref')
416 for line in fh: 422 for line in output:
417 line = line.strip() 423 line = line.strip()
418 rev, name = line.split(None, 1) 424 rev, name = line.split(None, 1)
419 # Process each type of branch 425 # Process each type of branch
420 for gitprefix, hgprefix in reftypes: 426 for gitprefix, hgprefix in reftypes:
421 if not name.startswith(gitprefix) or name in exclude: 427 if not name.startswith(gitprefix) or name in exclude: