comparison mercurial/localrepo.py @ 39693:5b8e9b2060ef

localrepo: validate supported requirements in makelocalrepository() This should be a glorified code move. I did take the opportunity to refactor things. We now have a separate function for gathering requirements and one for validating them. I also mode cosmetic changes to the code, such as not using abbreviations and using a set instead of list to model missing requirements. Differential Revision: https://phab.mercurial-scm.org/D4569
author Gregory Szorc <gregory.szorc@gmail.com>
date Wed, 12 Sep 2018 14:54:17 -0700
parents 6a3162ed881d
children cb2dcfa5cade
comparison
equal deleted inserted replaced
39692:6a3162ed881d 39693:5b8e9b2060ef
428 except IOError: 428 except IOError:
429 pass 429 pass
430 else: 430 else:
431 extensions.loadall(ui) 431 extensions.loadall(ui)
432 432
433 supportedrequirements = gathersupportedrequirements(ui)
434 ensurerequirementsrecognized(requirements, supportedrequirements)
435
436 # At this point, we know we should be capable of opening the repository.
437 # Now get on with doing that.
438
433 return localrepository( 439 return localrepository(
434 baseui=baseui, 440 baseui=baseui,
435 ui=ui, 441 ui=ui,
436 origroot=path, 442 origroot=path,
437 wdirvfs=wdirvfs, 443 wdirvfs=wdirvfs,
438 hgvfs=hgvfs, 444 hgvfs=hgvfs,
439 requirements=requirements, 445 requirements=requirements,
446 supportedrequirements=supportedrequirements,
440 intents=intents) 447 intents=intents)
448
449 def gathersupportedrequirements(ui):
450 """Determine the complete set of recognized requirements."""
451 # Start with all requirements supported by this file.
452 supported = set(localrepository._basesupported)
453
454 # Execute ``featuresetupfuncs`` entries if they belong to an extension
455 # relevant to this ui instance.
456 modules = {m.__name__ for n, m in extensions.extensions(ui)}
457
458 for fn in featuresetupfuncs:
459 if fn.__module__ in modules:
460 fn(ui, supported)
461
462 # Add derived requirements from registered compression engines.
463 for name in util.compengines:
464 engine = util.compengines[name]
465 if engine.revlogheader():
466 supported.add(b'exp-compression-%s' % name)
467
468 return supported
469
470 def ensurerequirementsrecognized(requirements, supported):
471 """Validate that a set of local requirements is recognized.
472
473 Receives a set of requirements. Raises an ``error.RepoError`` if there
474 exists any requirement in that set that currently loaded code doesn't
475 recognize.
476
477 Returns a set of supported requirements.
478 """
479 missing = set()
480
481 for requirement in requirements:
482 if requirement in supported:
483 continue
484
485 if not requirement or not requirement[0:1].isalnum():
486 raise error.RequirementError(_(b'.hg/requires file is corrupt'))
487
488 missing.add(requirement)
489
490 if missing:
491 raise error.RequirementError(
492 _(b'repository requires features unknown to this Mercurial: %s') %
493 b' '.join(sorted(missing)),
494 hint=_(b'see https://mercurial-scm.org/wiki/MissingRequirement '
495 b'for more information'))
441 496
442 @interfaceutil.implementer(repository.completelocalrepository) 497 @interfaceutil.implementer(repository.completelocalrepository)
443 class localrepository(object): 498 class localrepository(object):
444 499
445 # obsolete experimental requirements: 500 # obsolete experimental requirements:
488 # the remainig bit and drop this line 543 # the remainig bit and drop this line
489 'bisect.state', 544 'bisect.state',
490 } 545 }
491 546
492 def __init__(self, baseui, ui, origroot, wdirvfs, hgvfs, requirements, 547 def __init__(self, baseui, ui, origroot, wdirvfs, hgvfs, requirements,
493 intents=None): 548 supportedrequirements, intents=None):
494 """Create a new local repository instance. 549 """Create a new local repository instance.
495 550
496 Most callers should use ``hg.repository()``, ``localrepo.instance()``, 551 Most callers should use ``hg.repository()``, ``localrepo.instance()``,
497 or ``localrepo.makelocalrepository()`` for obtaining a new repository 552 or ``localrepo.makelocalrepository()`` for obtaining a new repository
498 object. 553 object.
514 hgvfs 569 hgvfs
515 ``vfs.vfs`` rooted at .hg/ 570 ``vfs.vfs`` rooted at .hg/
516 571
517 requirements 572 requirements
518 ``set`` of bytestrings representing repository opening requirements. 573 ``set`` of bytestrings representing repository opening requirements.
574
575 supportedrequirements
576 ``set`` of bytestrings representing repository requirements that we
577 know how to open. May be a supetset of ``requirements``.
519 578
520 intents 579 intents
521 ``set`` of system strings indicating what this repo will be used 580 ``set`` of system strings indicating what this repo will be used
522 for. 581 for.
523 """ 582 """
528 self.wvfs = wdirvfs 587 self.wvfs = wdirvfs
529 self.root = wdirvfs.base 588 self.root = wdirvfs.base
530 # vfs rooted at .hg/. Used to access most non-store paths. 589 # vfs rooted at .hg/. Used to access most non-store paths.
531 self.vfs = hgvfs 590 self.vfs = hgvfs
532 self.path = hgvfs.base 591 self.path = hgvfs.base
592 self.requirements = requirements
593 self.supported = supportedrequirements
533 594
534 self.filtername = None 595 self.filtername = None
535 # svfs: usually rooted at .hg/store, used to access repository history 596 # svfs: usually rooted at .hg/store, used to access repository history
536 # If this is a shared repository, this vfs may point to another 597 # If this is a shared repository, this vfs may point to another
537 # repository's .hg/store directory. 598 # repository's .hg/store directory.
543 # A list of callback to shape the phase if no data were found. 604 # A list of callback to shape the phase if no data were found.
544 # Callback are in the form: func(repo, roots) --> processed root. 605 # Callback are in the form: func(repo, roots) --> processed root.
545 # This list it to be filled by extension during repo setup 606 # This list it to be filled by extension during repo setup
546 self._phasedefaults = [] 607 self._phasedefaults = []
547 608
548 if featuresetupfuncs:
549 self.supported = set(self._basesupported) # use private copy
550 extmods = set(m.__name__ for n, m
551 in extensions.extensions(self.ui))
552 for setupfunc in featuresetupfuncs:
553 if setupfunc.__module__ in extmods:
554 setupfunc(self.ui, self.supported)
555 else:
556 self.supported = self._basesupported
557 color.setup(self.ui) 609 color.setup(self.ui)
558
559 # Add compression engines.
560 for name in util.compengines:
561 engine = util.compengines[name]
562 if engine.revlogheader():
563 self.supported.add('exp-compression-%s' % name)
564
565 # Validate that all seen repository requirements are supported.
566 missingrequirements = []
567 for r in requirements:
568 if r not in self.supported:
569 if not r or not r[0:1].isalnum():
570 raise error.RequirementError(
571 _(".hg/requires file is corrupt"))
572 missingrequirements.append(r)
573 missingrequirements.sort()
574 if missingrequirements:
575 raise error.RequirementError(
576 _("repository requires features unknown to this Mercurial: %s")
577 % " ".join(missingrequirements),
578 hint=_("see https://mercurial-scm.org/wiki/MissingRequirement"
579 " for more information"))
580
581 self.requirements = requirements
582 610
583 cachepath = self.vfs.join('cache') 611 cachepath = self.vfs.join('cache')
584 self.sharedpath = self.path 612 self.sharedpath = self.path
585 try: 613 try:
586 sharedpath = self.vfs.read("sharedpath").rstrip('\n') 614 sharedpath = self.vfs.read("sharedpath").rstrip('\n')