comparison mercurial/subrepo.py @ 29021:92d37fb3f1aa stable

verify: don't init subrepo when missing one is referenced (issue5128) (API) Initializing a subrepo when one doesn't exist is the right thing to do when the parent is being updated, but in few other cases. Unfortunately, there isn't enough context in the subrepo module to distinguish this case. This same issue can be caused with other subrepo aware commands, so there is a general issue here beyond the scope of this fix. A simpler attempt I tried was to add an '_updating' boolean to localrepo, and set/clear it around the call to mergemod.update() in hg.updaterepo(). That mostly worked, but doesn't handle the case where archive will clone the subrepo if it is missing. (I vaguely recall that there may be other commands that will clone if needed like this, but certainly not all do. It seems both handy, and a bit surprising for what should be a read only operation. It might be nice if all commands did this consistently, but we probably need Angel's subrepo caching first, to not make a mess of the working directory.) I originally handled 'Exception' in order to pick up the Aborts raised in subrepo.state(), but this turns out to be unnecessary because that is called once and cached by ctx.sub() when iterating the subrepos. It was suggested in the bug discussion to skip looking at the subrepo links unless -S is specified. I don't really like that idea because missing a subrepo or (less likely, but worse) a corrupt .hgsubstate is a problem of the parent repo when checking out a revision. The -S option seems like a better fit for functionality that would recurse into each subrepo and do a full verification. Ultimately, the default value for 'allowcreate' should probably be flipped, but since the default behavior was to allow creation, this is less risky for now.
author Matt Harbison <matt_harbison@yahoo.com>
date Wed, 27 Apr 2016 22:45:52 -0400
parents 9d3e280864fb
children 28dc0030228a
comparison
equal deleted inserted replaced
29020:ee2e4a2c3690 29021:92d37fb3f1aa
338 if f.lower() == 'hgrc': 338 if f.lower() == 'hgrc':
339 ui.warn(_("warning: removing potentially hostile 'hgrc' " 339 ui.warn(_("warning: removing potentially hostile 'hgrc' "
340 "in '%s'\n") % vfs.join(dirname)) 340 "in '%s'\n") % vfs.join(dirname))
341 vfs.unlink(vfs.reljoin(dirname, f)) 341 vfs.unlink(vfs.reljoin(dirname, f))
342 342
343 def subrepo(ctx, path, allowwdir=False): 343 def subrepo(ctx, path, allowwdir=False, allowcreate=True):
344 """return instance of the right subrepo class for subrepo in path""" 344 """return instance of the right subrepo class for subrepo in path"""
345 # subrepo inherently violates our import layering rules 345 # subrepo inherently violates our import layering rules
346 # because it wants to make repo objects from deep inside the stack 346 # because it wants to make repo objects from deep inside the stack
347 # so we manually delay the circular imports to not break 347 # so we manually delay the circular imports to not break
348 # scripts that don't use our demand-loading 348 # scripts that don't use our demand-loading
354 state = ctx.substate[path] 354 state = ctx.substate[path]
355 if state[2] not in types: 355 if state[2] not in types:
356 raise error.Abort(_('unknown subrepo type %s') % state[2]) 356 raise error.Abort(_('unknown subrepo type %s') % state[2])
357 if allowwdir: 357 if allowwdir:
358 state = (state[0], ctx.subrev(path), state[2]) 358 state = (state[0], ctx.subrev(path), state[2])
359 return types[state[2]](ctx, path, state[:2]) 359 return types[state[2]](ctx, path, state[:2], allowcreate)
360 360
361 def nullsubrepo(ctx, path, pctx): 361 def nullsubrepo(ctx, path, pctx):
362 """return an empty subrepo in pctx for the extant subrepo in ctx""" 362 """return an empty subrepo in pctx for the extant subrepo in ctx"""
363 # subrepo inherently violates our import layering rules 363 # subrepo inherently violates our import layering rules
364 # because it wants to make repo objects from deep inside the stack 364 # because it wants to make repo objects from deep inside the stack
373 if state[2] not in types: 373 if state[2] not in types:
374 raise error.Abort(_('unknown subrepo type %s') % state[2]) 374 raise error.Abort(_('unknown subrepo type %s') % state[2])
375 subrev = '' 375 subrev = ''
376 if state[2] == 'hg': 376 if state[2] == 'hg':
377 subrev = "0" * 40 377 subrev = "0" * 40
378 return types[state[2]](pctx, path, (state[0], subrev)) 378 return types[state[2]](pctx, path, (state[0], subrev), True)
379 379
380 def newcommitphase(ui, ctx): 380 def newcommitphase(ui, ctx):
381 commitphase = phases.newcommitphase(ui) 381 commitphase = phases.newcommitphase(ui)
382 substate = getattr(ctx, "substate", None) 382 substate = getattr(ctx, "substate", None)
383 if not substate: 383 if not substate:
609 """return path to this subrepository as seen from outermost repository 609 """return path to this subrepository as seen from outermost repository
610 """ 610 """
611 return self.wvfs.reljoin(reporelpath(self._ctx.repo()), self._path) 611 return self.wvfs.reljoin(reporelpath(self._ctx.repo()), self._path)
612 612
613 class hgsubrepo(abstractsubrepo): 613 class hgsubrepo(abstractsubrepo):
614 def __init__(self, ctx, path, state): 614 def __init__(self, ctx, path, state, allowcreate):
615 super(hgsubrepo, self).__init__(ctx, path) 615 super(hgsubrepo, self).__init__(ctx, path)
616 self._state = state 616 self._state = state
617 r = ctx.repo() 617 r = ctx.repo()
618 root = r.wjoin(path) 618 root = r.wjoin(path)
619 create = not r.wvfs.exists('%s/.hg' % path) 619 create = allowcreate and not r.wvfs.exists('%s/.hg' % path)
620 self._repo = hg.repository(r.baseui, root, create=create) 620 self._repo = hg.repository(r.baseui, root, create=create)
621 621
622 # Propagate the parent's --hidden option 622 # Propagate the parent's --hidden option
623 if r is r.unfiltered(): 623 if r is r.unfiltered():
624 self._repo = self._repo.unfiltered() 624 self._repo = self._repo.unfiltered()
1062 """ 1062 """
1063 # Keep consistent dir separators by avoiding vfs.join(self._path) 1063 # Keep consistent dir separators by avoiding vfs.join(self._path)
1064 return reporelpath(self._repo) 1064 return reporelpath(self._repo)
1065 1065
1066 class svnsubrepo(abstractsubrepo): 1066 class svnsubrepo(abstractsubrepo):
1067 def __init__(self, ctx, path, state): 1067 def __init__(self, ctx, path, state, allowcreate):
1068 super(svnsubrepo, self).__init__(ctx, path) 1068 super(svnsubrepo, self).__init__(ctx, path)
1069 self._state = state 1069 self._state = state
1070 self._exe = util.findexe('svn') 1070 self._exe = util.findexe('svn')
1071 if not self._exe: 1071 if not self._exe:
1072 raise error.Abort(_("'svn' executable not found for subrepo '%s'") 1072 raise error.Abort(_("'svn' executable not found for subrepo '%s'")
1282 def filedata(self, name): 1282 def filedata(self, name):
1283 return self._svncommand(['cat'], name)[0] 1283 return self._svncommand(['cat'], name)[0]
1284 1284
1285 1285
1286 class gitsubrepo(abstractsubrepo): 1286 class gitsubrepo(abstractsubrepo):
1287 def __init__(self, ctx, path, state): 1287 def __init__(self, ctx, path, state, allowcreate):
1288 super(gitsubrepo, self).__init__(ctx, path) 1288 super(gitsubrepo, self).__init__(ctx, path)
1289 self._state = state 1289 self._state = state
1290 self._abspath = ctx.repo().wjoin(path) 1290 self._abspath = ctx.repo().wjoin(path)
1291 self._subparent = ctx.repo() 1291 self._subparent = ctx.repo()
1292 self._ensuregit() 1292 self._ensuregit()