mercurial/repair.py
changeset 30776 3997edc4a86d
parent 30775 513d68a90398
child 30777 7de7afd8bdd9
equal deleted inserted replaced
30775:513d68a90398 30776:3997edc4a86d
   429         'dotencode',
   429         'dotencode',
   430         'fncache',
   430         'fncache',
   431         'generaldelta',
   431         'generaldelta',
   432     ])
   432     ])
   433 
   433 
       
   434 deficiency = 'deficiency'
       
   435 optimisation = 'optimization'
       
   436 
       
   437 class upgradeimprovement(object):
       
   438     """Represents an improvement that can be made as part of an upgrade.
       
   439 
       
   440     The following attributes are defined on each instance:
       
   441 
       
   442     name
       
   443        Machine-readable string uniquely identifying this improvement. It
       
   444        will be mapped to an action later in the upgrade process.
       
   445 
       
   446     type
       
   447        Either ``deficiency`` or ``optimisation``. A deficiency is an obvious
       
   448        problem. An optimization is an action (sometimes optional) that
       
   449        can be taken to further improve the state of the repository.
       
   450 
       
   451     description
       
   452        Message intended for humans explaining the improvement in more detail,
       
   453        including the implications of it. For ``deficiency`` types, should be
       
   454        worded in the present tense. For ``optimisation`` types, should be
       
   455        worded in the future tense.
       
   456 
       
   457     upgrademessage
       
   458        Message intended for humans explaining what an upgrade addressing this
       
   459        issue will do. Should be worded in the future tense.
       
   460 
       
   461     fromdefault (``deficiency`` types only)
       
   462        Boolean indicating whether the current (deficient) state deviates
       
   463        from Mercurial's default configuration.
       
   464 
       
   465     fromconfig (``deficiency`` types only)
       
   466        Boolean indicating whether the current (deficient) state deviates
       
   467        from the current Mercurial configuration.
       
   468     """
       
   469     def __init__(self, name, type, description, upgrademessage, **kwargs):
       
   470         self.name = name
       
   471         self.type = type
       
   472         self.description = description
       
   473         self.upgrademessage = upgrademessage
       
   474 
       
   475         for k, v in kwargs.items():
       
   476             setattr(self, k, v)
       
   477 
       
   478 def upgradefindimprovements(repo):
       
   479     """Determine improvements that can be made to the repo during upgrade.
       
   480 
       
   481     Returns a list of ``upgradeimprovement`` describing repository deficiencies
       
   482     and optimizations.
       
   483     """
       
   484     # Avoid cycle: cmdutil -> repair -> localrepo -> cmdutil
       
   485     from . import localrepo
       
   486 
       
   487     newreporeqs = localrepo.newreporequirements(repo)
       
   488 
       
   489     improvements = []
       
   490 
       
   491     # We could detect lack of revlogv1 and store here, but they were added
       
   492     # in 0.9.2 and we don't support upgrading repos without these
       
   493     # requirements, so let's not bother.
       
   494 
       
   495     if 'fncache' not in repo.requirements:
       
   496         improvements.append(upgradeimprovement(
       
   497             name='fncache',
       
   498             type=deficiency,
       
   499             description=_('long and reserved filenames may not work correctly; '
       
   500                           'repository performance is sub-optimal'),
       
   501             upgrademessage=_('repository will be more resilient to storing '
       
   502                              'certain paths and performance of certain '
       
   503                              'operations should be improved'),
       
   504             fromdefault=True,
       
   505             fromconfig='fncache' in newreporeqs))
       
   506 
       
   507     if 'dotencode' not in repo.requirements:
       
   508         improvements.append(upgradeimprovement(
       
   509             name='dotencode',
       
   510             type=deficiency,
       
   511             description=_('storage of filenames beginning with a period or '
       
   512                           'space may not work correctly'),
       
   513             upgrademessage=_('repository will be better able to store files '
       
   514                              'beginning with a space or period'),
       
   515             fromdefault=True,
       
   516             fromconfig='dotencode' in newreporeqs))
       
   517 
       
   518     if 'generaldelta' not in repo.requirements:
       
   519         improvements.append(upgradeimprovement(
       
   520             name='generaldelta',
       
   521             type=deficiency,
       
   522             description=_('deltas within internal storage are unable to '
       
   523                           'choose optimal revisions; repository is larger and '
       
   524                           'slower than it could be; interaction with other '
       
   525                           'repositories may require extra network and CPU '
       
   526                           'resources, making "hg push" and "hg pull" slower'),
       
   527             upgrademessage=_('repository storage will be able to create '
       
   528                              'optimal deltas; new repository data will be '
       
   529                              'smaller and read times should decrease; '
       
   530                              'interacting with other repositories using this '
       
   531                              'storage model should require less network and '
       
   532                              'CPU resources, making "hg push" and "hg pull" '
       
   533                              'faster'),
       
   534             fromdefault=True,
       
   535             fromconfig='generaldelta' in newreporeqs))
       
   536 
       
   537     # Mercurial 4.0 changed changelogs to not use delta chains. Search for
       
   538     # changelogs with deltas.
       
   539     cl = repo.changelog
       
   540     for rev in cl:
       
   541         chainbase = cl.chainbase(rev)
       
   542         if chainbase != rev:
       
   543             improvements.append(upgradeimprovement(
       
   544                 name='removecldeltachain',
       
   545                 type=deficiency,
       
   546                 description=_('changelog storage is using deltas instead of '
       
   547                               'raw entries; changelog reading and any '
       
   548                               'operation relying on changelog data are slower '
       
   549                               'than they could be'),
       
   550                 upgrademessage=_('changelog storage will be reformated to '
       
   551                                  'store raw entries; changelog reading will be '
       
   552                                  'faster; changelog size may be reduced'),
       
   553                 fromdefault=True,
       
   554                 fromconfig=True))
       
   555             break
       
   556 
       
   557     # Now for the optimizations.
       
   558 
       
   559     # These are unconditionally added. There is logic later that figures out
       
   560     # which ones to apply.
       
   561 
       
   562     improvements.append(upgradeimprovement(
       
   563         name='redeltaparent',
       
   564         type=optimisation,
       
   565         description=_('deltas within internal storage will be recalculated to '
       
   566                       'choose an optimal base revision where this was not '
       
   567                       'already done; the size of the repository may shrink and '
       
   568                       'various operations may become faster; the first time '
       
   569                       'this optimization is performed could slow down upgrade '
       
   570                       'execution considerably; subsequent invocations should '
       
   571                       'not run noticeably slower'),
       
   572         upgrademessage=_('deltas within internal storage will choose a new '
       
   573                          'base revision if needed')))
       
   574 
       
   575     improvements.append(upgradeimprovement(
       
   576         name='redeltamultibase',
       
   577         type=optimisation,
       
   578         description=_('deltas within internal storage will be recalculated '
       
   579                       'against multiple base revision and the smallest '
       
   580                       'difference will be used; the size of the repository may '
       
   581                       'shrink significantly when there are many merges; this '
       
   582                       'optimization will slow down execution in proportion to '
       
   583                       'the number of merges in the repository and the amount '
       
   584                       'of files in the repository; this slow down should not '
       
   585                       'be significant unless there are tens of thousands of '
       
   586                       'files and thousands of merges'),
       
   587         upgrademessage=_('deltas within internal storage will choose an '
       
   588                          'optimal delta by computing deltas against multiple '
       
   589                          'parents; may slow down execution time '
       
   590                          'significantly')))
       
   591 
       
   592     improvements.append(upgradeimprovement(
       
   593         name='redeltaall',
       
   594         type=optimisation,
       
   595         description=_('deltas within internal storage will always be '
       
   596                       'recalculated without reusing prior deltas; this will '
       
   597                       'likely make execution run several times slower; this '
       
   598                       'optimization is typically not needed'),
       
   599         upgrademessage=_('deltas within internal storage will be fully '
       
   600                          'recomputed; this will likely drastically slow down '
       
   601                          'execution time')))
       
   602 
       
   603     return improvements
       
   604 
       
   605 def upgradedetermineactions(repo, improvements, sourcereqs, destreqs,
       
   606                             optimize):
       
   607     """Determine upgrade actions that will be performed.
       
   608 
       
   609     Given a list of improvements as returned by ``upgradefindimprovements``,
       
   610     determine the list of upgrade actions that will be performed.
       
   611 
       
   612     The role of this function is to filter improvements if needed, apply
       
   613     recommended optimizations from the improvements list that make sense,
       
   614     etc.
       
   615 
       
   616     Returns a list of action names.
       
   617     """
       
   618     newactions = []
       
   619 
       
   620     knownreqs = upgradesupporteddestrequirements(repo)
       
   621 
       
   622     for i in improvements:
       
   623         name = i.name
       
   624 
       
   625         # If the action is a requirement that doesn't show up in the
       
   626         # destination requirements, prune the action.
       
   627         if name in knownreqs and name not in destreqs:
       
   628             continue
       
   629 
       
   630         if i.type == deficiency:
       
   631             newactions.append(name)
       
   632 
       
   633     newactions.extend(o for o in sorted(optimize) if o not in newactions)
       
   634 
       
   635     # FUTURE consider adding some optimizations here for certain transitions.
       
   636     # e.g. adding generaldelta could schedule parent redeltas.
       
   637 
       
   638     return newactions
       
   639 
   434 def upgraderepo(ui, repo, run=False, optimize=None):
   640 def upgraderepo(ui, repo, run=False, optimize=None):
   435     """Upgrade a repository in place."""
   641     """Upgrade a repository in place."""
   436     # Avoid cycle: cmdutil -> repair -> localrepo -> cmdutil
   642     # Avoid cycle: cmdutil -> repair -> localrepo -> cmdutil
   437     from . import localrepo
   643     from . import localrepo
   438 
   644 
       
   645     optimize = set(optimize or [])
   439     repo = repo.unfiltered()
   646     repo = repo.unfiltered()
   440 
   647 
   441     # Ensure the repository can be upgraded.
   648     # Ensure the repository can be upgraded.
   442     missingreqs = upgraderequiredsourcerequirements(repo) - repo.requirements
   649     missingreqs = upgraderequiredsourcerequirements(repo) - repo.requirements
   443     if missingreqs:
   650     if missingreqs:
   471     if unsupportedreqs:
   678     if unsupportedreqs:
   472         raise error.Abort(_('cannot upgrade repository; do not support '
   679         raise error.Abort(_('cannot upgrade repository; do not support '
   473                             'destination requirement: %s') %
   680                             'destination requirement: %s') %
   474                           _(', ').join(sorted(unsupportedreqs)))
   681                           _(', ').join(sorted(unsupportedreqs)))
   475 
   682 
       
   683     # Find and validate all improvements that can be made.
       
   684     improvements = upgradefindimprovements(repo)
       
   685     for i in improvements:
       
   686         if i.type not in (deficiency, optimisation):
       
   687             raise error.Abort(_('unexpected improvement type %s for %s') % (
       
   688                 i.type, i.name))
       
   689 
       
   690     # Validate arguments.
       
   691     unknownoptimize = optimize - set(i.name for i in improvements
       
   692                                      if i.type == optimisation)
       
   693     if unknownoptimize:
       
   694         raise error.Abort(_('unknown optimization action requested: %s') %
       
   695                           ', '.join(sorted(unknownoptimize)),
       
   696                           hint=_('run without arguments to see valid '
       
   697                                  'optimizations'))
       
   698 
       
   699     actions = upgradedetermineactions(repo, improvements, repo.requirements,
       
   700                                       newreqs, optimize)
       
   701 
   476     def printrequirements():
   702     def printrequirements():
   477         ui.write(_('requirements\n'))
   703         ui.write(_('requirements\n'))
   478         ui.write(_('   preserved: %s\n') %
   704         ui.write(_('   preserved: %s\n') %
   479                  _(', ').join(sorted(newreqs & repo.requirements)))
   705                  _(', ').join(sorted(newreqs & repo.requirements)))
   480 
   706 
   486             ui.write(_('   added: %s\n') %
   712             ui.write(_('   added: %s\n') %
   487                      _(', ').join(sorted(newreqs - repo.requirements)))
   713                      _(', ').join(sorted(newreqs - repo.requirements)))
   488 
   714 
   489         ui.write('\n')
   715         ui.write('\n')
   490 
   716 
       
   717     def printupgradeactions():
       
   718         for action in actions:
       
   719             for i in improvements:
       
   720                 if i.name == action:
       
   721                     ui.write('%s\n   %s\n\n' %
       
   722                              (i.name, i.upgrademessage))
       
   723 
   491     if not run:
   724     if not run:
       
   725         fromdefault = []
       
   726         fromconfig = []
       
   727         optimizations = []
       
   728 
       
   729         for i in improvements:
       
   730             assert i.type in (deficiency, optimisation)
       
   731             if i.type == deficiency:
       
   732                 if i.fromdefault:
       
   733                     fromdefault.append(i)
       
   734                 if i.fromconfig:
       
   735                     fromconfig.append(i)
       
   736             else:
       
   737                 optimizations.append(i)
       
   738 
       
   739         if fromdefault or fromconfig:
       
   740             fromconfignames = set(x.name for x in fromconfig)
       
   741             onlydefault = [i for i in fromdefault
       
   742                            if i.name not in fromconfignames]
       
   743 
       
   744             if fromconfig:
       
   745                 ui.write(_('repository lacks features recommended by '
       
   746                            'current config options:\n\n'))
       
   747                 for i in fromconfig:
       
   748                     ui.write('%s\n   %s\n\n' % (i.name, i.description))
       
   749 
       
   750             if onlydefault:
       
   751                 ui.write(_('repository lacks features used by the default '
       
   752                            'config options:\n\n'))
       
   753                 for i in onlydefault:
       
   754                     ui.write('%s\n   %s\n\n' % (i.name, i.description))
       
   755 
       
   756             ui.write('\n')
       
   757         else:
       
   758             ui.write(_('(no feature deficiencies found in existing '
       
   759                        'repository)\n'))
       
   760 
   492         ui.write(_('performing an upgrade with "--run" will make the following '
   761         ui.write(_('performing an upgrade with "--run" will make the following '
   493                    'changes:\n\n'))
   762                    'changes:\n\n'))
   494 
   763 
   495         printrequirements()
   764         printrequirements()
       
   765         printupgradeactions()
       
   766 
       
   767         unusedoptimize = [i for i in improvements
       
   768                           if i.name not in actions and i.type == optimisation]
       
   769         if unusedoptimize:
       
   770             ui.write(_('additional optimizations are available by specifying '
       
   771                      '"--optimize <name>":\n\n'))
       
   772             for i in unusedoptimize:
       
   773                 ui.write(_('%s\n   %s\n\n') % (i.name, i.description))