Mercurial > public > mercurial-scm > hg-stable
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() |