mercurial/upgrade.py
changeset 46051 4b89cf08d8dc
parent 46050 f105c49e89cd
child 46052 f4f956342cf1
equal deleted inserted replaced
46050:f105c49e89cd 46051:4b89cf08d8dc
    11 from . import (
    11 from . import (
    12     error,
    12     error,
    13     hg,
    13     hg,
    14     localrepo,
    14     localrepo,
    15     pycompat,
    15     pycompat,
    16     requirements,
       
    17     util,
       
    18 )
    16 )
    19 
    17 
    20 from .upgrade_utils import (
    18 from .upgrade_utils import (
       
    19     actions as upgrade_actions,
    21     engine as upgrade_engine,
    20     engine as upgrade_engine,
    22 )
    21 )
    23 
    22 
    24 from .utils import compression
    23 allformatvariant = upgrade_actions.allformatvariant
    25 
       
    26 # list of requirements that request a clone of all revlog if added/removed
       
    27 RECLONES_REQUIREMENTS = {
       
    28     b'generaldelta',
       
    29     requirements.SPARSEREVLOG_REQUIREMENT,
       
    30 }
       
    31 
       
    32 
       
    33 def requiredsourcerequirements(repo):
       
    34     """Obtain requirements required to be present to upgrade a repo.
       
    35 
       
    36     An upgrade will not be allowed if the repository doesn't have the
       
    37     requirements returned by this function.
       
    38     """
       
    39     return {
       
    40         # Introduced in Mercurial 0.9.2.
       
    41         b'revlogv1',
       
    42         # Introduced in Mercurial 0.9.2.
       
    43         b'store',
       
    44     }
       
    45 
       
    46 
       
    47 def blocksourcerequirements(repo):
       
    48     """Obtain requirements that will prevent an upgrade from occurring.
       
    49 
       
    50     An upgrade cannot be performed if the source repository contains a
       
    51     requirements in the returned set.
       
    52     """
       
    53     return {
       
    54         # The upgrade code does not yet support these experimental features.
       
    55         # This is an artificial limitation.
       
    56         requirements.TREEMANIFEST_REQUIREMENT,
       
    57         # This was a precursor to generaldelta and was never enabled by default.
       
    58         # It should (hopefully) not exist in the wild.
       
    59         b'parentdelta',
       
    60         # Upgrade should operate on the actual store, not the shared link.
       
    61         requirements.SHARED_REQUIREMENT,
       
    62     }
       
    63 
       
    64 
       
    65 def supportremovedrequirements(repo):
       
    66     """Obtain requirements that can be removed during an upgrade.
       
    67 
       
    68     If an upgrade were to create a repository that dropped a requirement,
       
    69     the dropped requirement must appear in the returned set for the upgrade
       
    70     to be allowed.
       
    71     """
       
    72     supported = {
       
    73         requirements.SPARSEREVLOG_REQUIREMENT,
       
    74         requirements.SIDEDATA_REQUIREMENT,
       
    75         requirements.COPIESSDC_REQUIREMENT,
       
    76         requirements.NODEMAP_REQUIREMENT,
       
    77         requirements.SHARESAFE_REQUIREMENT,
       
    78     }
       
    79     for name in compression.compengines:
       
    80         engine = compression.compengines[name]
       
    81         if engine.available() and engine.revlogheader():
       
    82             supported.add(b'exp-compression-%s' % name)
       
    83             if engine.name() == b'zstd':
       
    84                 supported.add(b'revlog-compression-zstd')
       
    85     return supported
       
    86 
       
    87 
       
    88 def supporteddestrequirements(repo):
       
    89     """Obtain requirements that upgrade supports in the destination.
       
    90 
       
    91     If the result of the upgrade would create requirements not in this set,
       
    92     the upgrade is disallowed.
       
    93 
       
    94     Extensions should monkeypatch this to add their custom requirements.
       
    95     """
       
    96     supported = {
       
    97         b'dotencode',
       
    98         b'fncache',
       
    99         b'generaldelta',
       
   100         b'revlogv1',
       
   101         b'store',
       
   102         requirements.SPARSEREVLOG_REQUIREMENT,
       
   103         requirements.SIDEDATA_REQUIREMENT,
       
   104         requirements.COPIESSDC_REQUIREMENT,
       
   105         requirements.NODEMAP_REQUIREMENT,
       
   106         requirements.SHARESAFE_REQUIREMENT,
       
   107     }
       
   108     for name in compression.compengines:
       
   109         engine = compression.compengines[name]
       
   110         if engine.available() and engine.revlogheader():
       
   111             supported.add(b'exp-compression-%s' % name)
       
   112             if engine.name() == b'zstd':
       
   113                 supported.add(b'revlog-compression-zstd')
       
   114     return supported
       
   115 
       
   116 
       
   117 def allowednewrequirements(repo):
       
   118     """Obtain requirements that can be added to a repository during upgrade.
       
   119 
       
   120     This is used to disallow proposed requirements from being added when
       
   121     they weren't present before.
       
   122 
       
   123     We use a list of allowed requirement additions instead of a list of known
       
   124     bad additions because the whitelist approach is safer and will prevent
       
   125     future, unknown requirements from accidentally being added.
       
   126     """
       
   127     supported = {
       
   128         b'dotencode',
       
   129         b'fncache',
       
   130         b'generaldelta',
       
   131         requirements.SPARSEREVLOG_REQUIREMENT,
       
   132         requirements.SIDEDATA_REQUIREMENT,
       
   133         requirements.COPIESSDC_REQUIREMENT,
       
   134         requirements.NODEMAP_REQUIREMENT,
       
   135         requirements.SHARESAFE_REQUIREMENT,
       
   136     }
       
   137     for name in compression.compengines:
       
   138         engine = compression.compengines[name]
       
   139         if engine.available() and engine.revlogheader():
       
   140             supported.add(b'exp-compression-%s' % name)
       
   141             if engine.name() == b'zstd':
       
   142                 supported.add(b'revlog-compression-zstd')
       
   143     return supported
       
   144 
       
   145 
       
   146 def preservedrequirements(repo):
       
   147     return set()
       
   148 
       
   149 
       
   150 DEFICIENCY = b'deficiency'
       
   151 OPTIMISATION = b'optimization'
       
   152 
       
   153 
       
   154 class improvement(object):
       
   155     """Represents an improvement that can be made as part of an upgrade.
       
   156 
       
   157     The following attributes are defined on each instance:
       
   158 
       
   159     name
       
   160        Machine-readable string uniquely identifying this improvement. It
       
   161        will be mapped to an action later in the upgrade process.
       
   162 
       
   163     type
       
   164        Either ``DEFICIENCY`` or ``OPTIMISATION``. A deficiency is an obvious
       
   165        problem. An optimization is an action (sometimes optional) that
       
   166        can be taken to further improve the state of the repository.
       
   167 
       
   168     description
       
   169        Message intended for humans explaining the improvement in more detail,
       
   170        including the implications of it. For ``DEFICIENCY`` types, should be
       
   171        worded in the present tense. For ``OPTIMISATION`` types, should be
       
   172        worded in the future tense.
       
   173 
       
   174     upgrademessage
       
   175        Message intended for humans explaining what an upgrade addressing this
       
   176        issue will do. Should be worded in the future tense.
       
   177     """
       
   178 
       
   179     def __init__(self, name, type, description, upgrademessage):
       
   180         self.name = name
       
   181         self.type = type
       
   182         self.description = description
       
   183         self.upgrademessage = upgrademessage
       
   184 
       
   185     def __eq__(self, other):
       
   186         if not isinstance(other, improvement):
       
   187             # This is what python tell use to do
       
   188             return NotImplemented
       
   189         return self.name == other.name
       
   190 
       
   191     def __ne__(self, other):
       
   192         return not (self == other)
       
   193 
       
   194     def __hash__(self):
       
   195         return hash(self.name)
       
   196 
       
   197 
       
   198 allformatvariant = []
       
   199 
       
   200 
       
   201 def registerformatvariant(cls):
       
   202     allformatvariant.append(cls)
       
   203     return cls
       
   204 
       
   205 
       
   206 class formatvariant(improvement):
       
   207     """an improvement subclass dedicated to repository format"""
       
   208 
       
   209     type = DEFICIENCY
       
   210     ### The following attributes should be defined for each class:
       
   211 
       
   212     # machine-readable string uniquely identifying this improvement. it will be
       
   213     # mapped to an action later in the upgrade process.
       
   214     name = None
       
   215 
       
   216     # message intended for humans explaining the improvement in more detail,
       
   217     # including the implications of it ``DEFICIENCY`` types, should be worded
       
   218     # in the present tense.
       
   219     description = None
       
   220 
       
   221     # message intended for humans explaining what an upgrade addressing this
       
   222     # issue will do. should be worded in the future tense.
       
   223     upgrademessage = None
       
   224 
       
   225     # value of current Mercurial default for new repository
       
   226     default = None
       
   227 
       
   228     def __init__(self):
       
   229         raise NotImplementedError()
       
   230 
       
   231     @staticmethod
       
   232     def fromrepo(repo):
       
   233         """current value of the variant in the repository"""
       
   234         raise NotImplementedError()
       
   235 
       
   236     @staticmethod
       
   237     def fromconfig(repo):
       
   238         """current value of the variant in the configuration"""
       
   239         raise NotImplementedError()
       
   240 
       
   241 
       
   242 class requirementformatvariant(formatvariant):
       
   243     """formatvariant based on a 'requirement' name.
       
   244 
       
   245     Many format variant are controlled by a 'requirement'. We define a small
       
   246     subclass to factor the code.
       
   247     """
       
   248 
       
   249     # the requirement that control this format variant
       
   250     _requirement = None
       
   251 
       
   252     @staticmethod
       
   253     def _newreporequirements(ui):
       
   254         return localrepo.newreporequirements(
       
   255             ui, localrepo.defaultcreateopts(ui)
       
   256         )
       
   257 
       
   258     @classmethod
       
   259     def fromrepo(cls, repo):
       
   260         assert cls._requirement is not None
       
   261         return cls._requirement in repo.requirements
       
   262 
       
   263     @classmethod
       
   264     def fromconfig(cls, repo):
       
   265         assert cls._requirement is not None
       
   266         return cls._requirement in cls._newreporequirements(repo.ui)
       
   267 
       
   268 
       
   269 @registerformatvariant
       
   270 class fncache(requirementformatvariant):
       
   271     name = b'fncache'
       
   272 
       
   273     _requirement = b'fncache'
       
   274 
       
   275     default = True
       
   276 
       
   277     description = _(
       
   278         b'long and reserved filenames may not work correctly; '
       
   279         b'repository performance is sub-optimal'
       
   280     )
       
   281 
       
   282     upgrademessage = _(
       
   283         b'repository will be more resilient to storing '
       
   284         b'certain paths and performance of certain '
       
   285         b'operations should be improved'
       
   286     )
       
   287 
       
   288 
       
   289 @registerformatvariant
       
   290 class dotencode(requirementformatvariant):
       
   291     name = b'dotencode'
       
   292 
       
   293     _requirement = b'dotencode'
       
   294 
       
   295     default = True
       
   296 
       
   297     description = _(
       
   298         b'storage of filenames beginning with a period or '
       
   299         b'space may not work correctly'
       
   300     )
       
   301 
       
   302     upgrademessage = _(
       
   303         b'repository will be better able to store files '
       
   304         b'beginning with a space or period'
       
   305     )
       
   306 
       
   307 
       
   308 @registerformatvariant
       
   309 class generaldelta(requirementformatvariant):
       
   310     name = b'generaldelta'
       
   311 
       
   312     _requirement = b'generaldelta'
       
   313 
       
   314     default = True
       
   315 
       
   316     description = _(
       
   317         b'deltas within internal storage are unable to '
       
   318         b'choose optimal revisions; repository is larger and '
       
   319         b'slower than it could be; interaction with other '
       
   320         b'repositories may require extra network and CPU '
       
   321         b'resources, making "hg push" and "hg pull" slower'
       
   322     )
       
   323 
       
   324     upgrademessage = _(
       
   325         b'repository storage will be able to create '
       
   326         b'optimal deltas; new repository data will be '
       
   327         b'smaller and read times should decrease; '
       
   328         b'interacting with other repositories using this '
       
   329         b'storage model should require less network and '
       
   330         b'CPU resources, making "hg push" and "hg pull" '
       
   331         b'faster'
       
   332     )
       
   333 
       
   334 
       
   335 @registerformatvariant
       
   336 class sharedsafe(requirementformatvariant):
       
   337     name = b'exp-sharesafe'
       
   338     _requirement = requirements.SHARESAFE_REQUIREMENT
       
   339 
       
   340     default = False
       
   341 
       
   342     description = _(
       
   343         b'old shared repositories do not share source repository '
       
   344         b'requirements and config. This leads to various problems '
       
   345         b'when the source repository format is upgraded or some new '
       
   346         b'extensions are enabled.'
       
   347     )
       
   348 
       
   349     upgrademessage = _(
       
   350         b'Upgrades a repository to share-safe format so that future '
       
   351         b'shares of this repository share its requirements and configs.'
       
   352     )
       
   353 
       
   354 
       
   355 @registerformatvariant
       
   356 class sparserevlog(requirementformatvariant):
       
   357     name = b'sparserevlog'
       
   358 
       
   359     _requirement = requirements.SPARSEREVLOG_REQUIREMENT
       
   360 
       
   361     default = True
       
   362 
       
   363     description = _(
       
   364         b'in order to limit disk reading and memory usage on older '
       
   365         b'version, the span of a delta chain from its root to its '
       
   366         b'end is limited, whatever the relevant data in this span. '
       
   367         b'This can severly limit Mercurial ability to build good '
       
   368         b'chain of delta resulting is much more storage space being '
       
   369         b'taken and limit reusability of on disk delta during '
       
   370         b'exchange.'
       
   371     )
       
   372 
       
   373     upgrademessage = _(
       
   374         b'Revlog supports delta chain with more unused data '
       
   375         b'between payload. These gaps will be skipped at read '
       
   376         b'time. This allows for better delta chains, making a '
       
   377         b'better compression and faster exchange with server.'
       
   378     )
       
   379 
       
   380 
       
   381 @registerformatvariant
       
   382 class sidedata(requirementformatvariant):
       
   383     name = b'sidedata'
       
   384 
       
   385     _requirement = requirements.SIDEDATA_REQUIREMENT
       
   386 
       
   387     default = False
       
   388 
       
   389     description = _(
       
   390         b'Allows storage of extra data alongside a revision, '
       
   391         b'unlocking various caching options.'
       
   392     )
       
   393 
       
   394     upgrademessage = _(b'Allows storage of extra data alongside a revision.')
       
   395 
       
   396 
       
   397 @registerformatvariant
       
   398 class persistentnodemap(requirementformatvariant):
       
   399     name = b'persistent-nodemap'
       
   400 
       
   401     _requirement = requirements.NODEMAP_REQUIREMENT
       
   402 
       
   403     default = False
       
   404 
       
   405     description = _(
       
   406         b'persist the node -> rev mapping on disk to speedup lookup'
       
   407     )
       
   408 
       
   409     upgrademessage = _(b'Speedup revision lookup by node id.')
       
   410 
       
   411 
       
   412 @registerformatvariant
       
   413 class copiessdc(requirementformatvariant):
       
   414     name = b'copies-sdc'
       
   415 
       
   416     _requirement = requirements.COPIESSDC_REQUIREMENT
       
   417 
       
   418     default = False
       
   419 
       
   420     description = _(b'Stores copies information alongside changesets.')
       
   421 
       
   422     upgrademessage = _(
       
   423         b'Allows to use more efficient algorithm to deal with ' b'copy tracing.'
       
   424     )
       
   425 
       
   426 
       
   427 @registerformatvariant
       
   428 class removecldeltachain(formatvariant):
       
   429     name = b'plain-cl-delta'
       
   430 
       
   431     default = True
       
   432 
       
   433     description = _(
       
   434         b'changelog storage is using deltas instead of '
       
   435         b'raw entries; changelog reading and any '
       
   436         b'operation relying on changelog data are slower '
       
   437         b'than they could be'
       
   438     )
       
   439 
       
   440     upgrademessage = _(
       
   441         b'changelog storage will be reformated to '
       
   442         b'store raw entries; changelog reading will be '
       
   443         b'faster; changelog size may be reduced'
       
   444     )
       
   445 
       
   446     @staticmethod
       
   447     def fromrepo(repo):
       
   448         # Mercurial 4.0 changed changelogs to not use delta chains. Search for
       
   449         # changelogs with deltas.
       
   450         cl = repo.changelog
       
   451         chainbase = cl.chainbase
       
   452         return all(rev == chainbase(rev) for rev in cl)
       
   453 
       
   454     @staticmethod
       
   455     def fromconfig(repo):
       
   456         return True
       
   457 
       
   458 
       
   459 @registerformatvariant
       
   460 class compressionengine(formatvariant):
       
   461     name = b'compression'
       
   462     default = b'zlib'
       
   463 
       
   464     description = _(
       
   465         b'Compresion algorithm used to compress data. '
       
   466         b'Some engine are faster than other'
       
   467     )
       
   468 
       
   469     upgrademessage = _(
       
   470         b'revlog content will be recompressed with the new algorithm.'
       
   471     )
       
   472 
       
   473     @classmethod
       
   474     def fromrepo(cls, repo):
       
   475         # we allow multiple compression engine requirement to co-exist because
       
   476         # strickly speaking, revlog seems to support mixed compression style.
       
   477         #
       
   478         # The compression used for new entries will be "the last one"
       
   479         compression = b'zlib'
       
   480         for req in repo.requirements:
       
   481             prefix = req.startswith
       
   482             if prefix(b'revlog-compression-') or prefix(b'exp-compression-'):
       
   483                 compression = req.split(b'-', 2)[2]
       
   484         return compression
       
   485 
       
   486     @classmethod
       
   487     def fromconfig(cls, repo):
       
   488         compengines = repo.ui.configlist(b'format', b'revlog-compression')
       
   489         # return the first valid value as the selection code would do
       
   490         for comp in compengines:
       
   491             if comp in util.compengines:
       
   492                 return comp
       
   493 
       
   494         # no valide compression found lets display it all for clarity
       
   495         return b','.join(compengines)
       
   496 
       
   497 
       
   498 @registerformatvariant
       
   499 class compressionlevel(formatvariant):
       
   500     name = b'compression-level'
       
   501     default = b'default'
       
   502 
       
   503     description = _(b'compression level')
       
   504 
       
   505     upgrademessage = _(b'revlog content will be recompressed')
       
   506 
       
   507     @classmethod
       
   508     def fromrepo(cls, repo):
       
   509         comp = compressionengine.fromrepo(repo)
       
   510         level = None
       
   511         if comp == b'zlib':
       
   512             level = repo.ui.configint(b'storage', b'revlog.zlib.level')
       
   513         elif comp == b'zstd':
       
   514             level = repo.ui.configint(b'storage', b'revlog.zstd.level')
       
   515         if level is None:
       
   516             return b'default'
       
   517         return bytes(level)
       
   518 
       
   519     @classmethod
       
   520     def fromconfig(cls, repo):
       
   521         comp = compressionengine.fromconfig(repo)
       
   522         level = None
       
   523         if comp == b'zlib':
       
   524             level = repo.ui.configint(b'storage', b'revlog.zlib.level')
       
   525         elif comp == b'zstd':
       
   526             level = repo.ui.configint(b'storage', b'revlog.zstd.level')
       
   527         if level is None:
       
   528             return b'default'
       
   529         return bytes(level)
       
   530 
       
   531 
       
   532 def finddeficiencies(repo):
       
   533     """returns a list of deficiencies that the repo suffer from"""
       
   534     deficiencies = []
       
   535 
       
   536     # We could detect lack of revlogv1 and store here, but they were added
       
   537     # in 0.9.2 and we don't support upgrading repos without these
       
   538     # requirements, so let's not bother.
       
   539 
       
   540     for fv in allformatvariant:
       
   541         if not fv.fromrepo(repo):
       
   542             deficiencies.append(fv)
       
   543 
       
   544     return deficiencies
       
   545 
       
   546 
    24 
   547 # search without '-' to support older form on newer client.
    25 # search without '-' to support older form on newer client.
   548 #
    26 #
   549 # We don't enforce backward compatibility for debug command so this
    27 # We don't enforce backward compatibility for debug command so this
   550 # might eventually be dropped. However, having to use two different
    28 # might eventually be dropped. However, having to use two different
   554     b'redeltaparent': b're-delta-parent',
    32     b'redeltaparent': b're-delta-parent',
   555     b'redeltamultibase': b're-delta-multibase',
    33     b'redeltamultibase': b're-delta-multibase',
   556     b'redeltaall': b're-delta-all',
    34     b'redeltaall': b're-delta-all',
   557     b'redeltafulladd': b're-delta-fulladd',
    35     b'redeltafulladd': b're-delta-fulladd',
   558 }
    36 }
   559 
       
   560 ALL_OPTIMISATIONS = []
       
   561 
       
   562 
       
   563 def register_optimization(obj):
       
   564     ALL_OPTIMISATIONS.append(obj)
       
   565     return obj
       
   566 
       
   567 
       
   568 register_optimization(
       
   569     improvement(
       
   570         name=b're-delta-parent',
       
   571         type=OPTIMISATION,
       
   572         description=_(
       
   573             b'deltas within internal storage will be recalculated to '
       
   574             b'choose an optimal base revision where this was not '
       
   575             b'already done; the size of the repository may shrink and '
       
   576             b'various operations may become faster; the first time '
       
   577             b'this optimization is performed could slow down upgrade '
       
   578             b'execution considerably; subsequent invocations should '
       
   579             b'not run noticeably slower'
       
   580         ),
       
   581         upgrademessage=_(
       
   582             b'deltas within internal storage will choose a new '
       
   583             b'base revision if needed'
       
   584         ),
       
   585     )
       
   586 )
       
   587 
       
   588 register_optimization(
       
   589     improvement(
       
   590         name=b're-delta-multibase',
       
   591         type=OPTIMISATION,
       
   592         description=_(
       
   593             b'deltas within internal storage will be recalculated '
       
   594             b'against multiple base revision and the smallest '
       
   595             b'difference will be used; the size of the repository may '
       
   596             b'shrink significantly when there are many merges; this '
       
   597             b'optimization will slow down execution in proportion to '
       
   598             b'the number of merges in the repository and the amount '
       
   599             b'of files in the repository; this slow down should not '
       
   600             b'be significant unless there are tens of thousands of '
       
   601             b'files and thousands of merges'
       
   602         ),
       
   603         upgrademessage=_(
       
   604             b'deltas within internal storage will choose an '
       
   605             b'optimal delta by computing deltas against multiple '
       
   606             b'parents; may slow down execution time '
       
   607             b'significantly'
       
   608         ),
       
   609     )
       
   610 )
       
   611 
       
   612 register_optimization(
       
   613     improvement(
       
   614         name=b're-delta-all',
       
   615         type=OPTIMISATION,
       
   616         description=_(
       
   617             b'deltas within internal storage will always be '
       
   618             b'recalculated without reusing prior deltas; this will '
       
   619             b'likely make execution run several times slower; this '
       
   620             b'optimization is typically not needed'
       
   621         ),
       
   622         upgrademessage=_(
       
   623             b'deltas within internal storage will be fully '
       
   624             b'recomputed; this will likely drastically slow down '
       
   625             b'execution time'
       
   626         ),
       
   627     )
       
   628 )
       
   629 
       
   630 register_optimization(
       
   631     improvement(
       
   632         name=b're-delta-fulladd',
       
   633         type=OPTIMISATION,
       
   634         description=_(
       
   635             b'every revision will be re-added as if it was new '
       
   636             b'content. It will go through the full storage '
       
   637             b'mechanism giving extensions a chance to process it '
       
   638             b'(eg. lfs). This is similar to "re-delta-all" but even '
       
   639             b'slower since more logic is involved.'
       
   640         ),
       
   641         upgrademessage=_(
       
   642             b'each revision will be added as new content to the '
       
   643             b'internal storage; this will likely drastically slow '
       
   644             b'down execution time, but some extensions might need '
       
   645             b'it'
       
   646         ),
       
   647     )
       
   648 )
       
   649 
       
   650 
       
   651 def findoptimizations(repo):
       
   652     """Determine optimisation that could be used during upgrade"""
       
   653     # These are unconditionally added. There is logic later that figures out
       
   654     # which ones to apply.
       
   655     return list(ALL_OPTIMISATIONS)
       
   656 
       
   657 
       
   658 def determineactions(repo, deficiencies, sourcereqs, destreqs):
       
   659     """Determine upgrade actions that will be performed.
       
   660 
       
   661     Given a list of improvements as returned by ``finddeficiencies`` and
       
   662     ``findoptimizations``, determine the list of upgrade actions that
       
   663     will be performed.
       
   664 
       
   665     The role of this function is to filter improvements if needed, apply
       
   666     recommended optimizations from the improvements list that make sense,
       
   667     etc.
       
   668 
       
   669     Returns a list of action names.
       
   670     """
       
   671     newactions = []
       
   672 
       
   673     for d in deficiencies:
       
   674         name = d._requirement
       
   675 
       
   676         # If the action is a requirement that doesn't show up in the
       
   677         # destination requirements, prune the action.
       
   678         if name is not None and name not in destreqs:
       
   679             continue
       
   680 
       
   681         newactions.append(d)
       
   682 
       
   683     # FUTURE consider adding some optimizations here for certain transitions.
       
   684     # e.g. adding generaldelta could schedule parent redeltas.
       
   685 
       
   686     return newactions
       
   687 
    37 
   688 
    38 
   689 def upgraderepo(
    39 def upgraderepo(
   690     ui,
    40     ui,
   691     repo,
    41     repo,
   720             # none are enabled
    70             # none are enabled
   721             for upgrade, __ in specified:
    71             for upgrade, __ in specified:
   722                 revlogs.discard(upgrade)
    72                 revlogs.discard(upgrade)
   723 
    73 
   724     # Ensure the repository can be upgraded.
    74     # Ensure the repository can be upgraded.
   725     missingreqs = requiredsourcerequirements(repo) - repo.requirements
    75     missingreqs = (
       
    76         upgrade_actions.requiredsourcerequirements(repo) - repo.requirements
       
    77     )
   726     if missingreqs:
    78     if missingreqs:
   727         raise error.Abort(
    79         raise error.Abort(
   728             _(b'cannot upgrade repository; requirement missing: %s')
    80             _(b'cannot upgrade repository; requirement missing: %s')
   729             % _(b', ').join(sorted(missingreqs))
    81             % _(b', ').join(sorted(missingreqs))
   730         )
    82         )
   731 
    83 
   732     blockedreqs = blocksourcerequirements(repo) & repo.requirements
    84     blockedreqs = (
       
    85         upgrade_actions.blocksourcerequirements(repo) & repo.requirements
       
    86     )
   733     if blockedreqs:
    87     if blockedreqs:
   734         raise error.Abort(
    88         raise error.Abort(
   735             _(
    89             _(
   736                 b'cannot upgrade repository; unsupported source '
    90                 b'cannot upgrade repository; unsupported source '
   737                 b'requirement: %s'
    91                 b'requirement: %s'
   742     # FUTURE there is potentially a need to control the wanted requirements via
    96     # FUTURE there is potentially a need to control the wanted requirements via
   743     # command arguments or via an extension hook point.
    97     # command arguments or via an extension hook point.
   744     newreqs = localrepo.newreporequirements(
    98     newreqs = localrepo.newreporequirements(
   745         repo.ui, localrepo.defaultcreateopts(repo.ui)
    99         repo.ui, localrepo.defaultcreateopts(repo.ui)
   746     )
   100     )
   747     newreqs.update(preservedrequirements(repo))
   101     newreqs.update(upgrade_actions.preservedrequirements(repo))
   748 
   102 
   749     noremovereqs = (
   103     noremovereqs = (
   750         repo.requirements - newreqs - supportremovedrequirements(repo)
   104         repo.requirements
       
   105         - newreqs
       
   106         - upgrade_actions.supportremovedrequirements(repo)
   751     )
   107     )
   752     if noremovereqs:
   108     if noremovereqs:
   753         raise error.Abort(
   109         raise error.Abort(
   754             _(
   110             _(
   755                 b'cannot upgrade repository; requirement would be '
   111                 b'cannot upgrade repository; requirement would be '
   756                 b'removed: %s'
   112                 b'removed: %s'
   757             )
   113             )
   758             % _(b', ').join(sorted(noremovereqs))
   114             % _(b', ').join(sorted(noremovereqs))
   759         )
   115         )
   760 
   116 
   761     noaddreqs = newreqs - repo.requirements - allowednewrequirements(repo)
   117     noaddreqs = (
       
   118         newreqs
       
   119         - repo.requirements
       
   120         - upgrade_actions.allowednewrequirements(repo)
       
   121     )
   762     if noaddreqs:
   122     if noaddreqs:
   763         raise error.Abort(
   123         raise error.Abort(
   764             _(
   124             _(
   765                 b'cannot upgrade repository; do not support adding '
   125                 b'cannot upgrade repository; do not support adding '
   766                 b'requirement: %s'
   126                 b'requirement: %s'
   767             )
   127             )
   768             % _(b', ').join(sorted(noaddreqs))
   128             % _(b', ').join(sorted(noaddreqs))
   769         )
   129         )
   770 
   130 
   771     unsupportedreqs = newreqs - supporteddestrequirements(repo)
   131     unsupportedreqs = newreqs - upgrade_actions.supporteddestrequirements(repo)
   772     if unsupportedreqs:
   132     if unsupportedreqs:
   773         raise error.Abort(
   133         raise error.Abort(
   774             _(
   134             _(
   775                 b'cannot upgrade repository; do not support '
   135                 b'cannot upgrade repository; do not support '
   776                 b'destination requirement: %s'
   136                 b'destination requirement: %s'
   777             )
   137             )
   778             % _(b', ').join(sorted(unsupportedreqs))
   138             % _(b', ').join(sorted(unsupportedreqs))
   779         )
   139         )
   780 
   140 
   781     # Find and validate all improvements that can be made.
   141     # Find and validate all improvements that can be made.
   782     alloptimizations = findoptimizations(repo)
   142     alloptimizations = upgrade_actions.findoptimizations(repo)
   783 
   143 
   784     # Apply and Validate arguments.
   144     # Apply and Validate arguments.
   785     optimizations = []
   145     optimizations = []
   786     for o in alloptimizations:
   146     for o in alloptimizations:
   787         if o.name in optimize:
   147         if o.name in optimize:
   793             _(b'unknown optimization action requested: %s')
   153             _(b'unknown optimization action requested: %s')
   794             % b', '.join(sorted(optimize)),
   154             % b', '.join(sorted(optimize)),
   795             hint=_(b'run without arguments to see valid optimizations'),
   155             hint=_(b'run without arguments to see valid optimizations'),
   796         )
   156         )
   797 
   157 
   798     deficiencies = finddeficiencies(repo)
   158     deficiencies = upgrade_actions.finddeficiencies(repo)
   799     actions = determineactions(repo, deficiencies, repo.requirements, newreqs)
   159     actions = upgrade_actions.determineactions(
       
   160         repo, deficiencies, repo.requirements, newreqs
       
   161     )
   800     actions.extend(
   162     actions.extend(
   801         o
   163         o
   802         for o in sorted(optimizations)
   164         for o in sorted(optimizations)
   803         # determineactions could have added optimisation
   165         # determineactions could have added optimisation
   804         if o not in actions
   166         if o not in actions
   806 
   168 
   807     removedreqs = repo.requirements - newreqs
   169     removedreqs = repo.requirements - newreqs
   808     addedreqs = newreqs - repo.requirements
   170     addedreqs = newreqs - repo.requirements
   809 
   171 
   810     if revlogs != upgrade_engine.UPGRADE_ALL_REVLOGS:
   172     if revlogs != upgrade_engine.UPGRADE_ALL_REVLOGS:
   811         incompatible = RECLONES_REQUIREMENTS & (removedreqs | addedreqs)
   173         incompatible = upgrade_actions.RECLONES_REQUIREMENTS & (
       
   174             removedreqs | addedreqs
       
   175         )
   812         if incompatible:
   176         if incompatible:
   813             msg = _(
   177             msg = _(
   814                 b'ignoring revlogs selection flags, format requirements '
   178                 b'ignoring revlogs selection flags, format requirements '
   815                 b'change: %s\n'
   179                 b'change: %s\n'
   816             )
   180             )
   843             write_labeled(added, "upgrade-repo.requirement.added")
   207             write_labeled(added, "upgrade-repo.requirement.added")
   844             ui.write((b'\n'))
   208             ui.write((b'\n'))
   845         ui.write(b'\n')
   209         ui.write(b'\n')
   846 
   210 
   847     def printoptimisations():
   211     def printoptimisations():
   848         optimisations = [a for a in actions if a.type == OPTIMISATION]
   212         optimisations = [
       
   213             a for a in actions if a.type == upgrade_actions.OPTIMISATION
       
   214         ]
   849         optimisations.sort(key=lambda a: a.name)
   215         optimisations.sort(key=lambda a: a.name)
   850         if optimisations:
   216         if optimisations:
   851             ui.write(_(b'optimisations: '))
   217             ui.write(_(b'optimisations: '))
   852             write_labeled(
   218             write_labeled(
   853                 [a.name for a in optimisations],
   219                 [a.name for a in optimisations],
   991                         b'it to free up disk space once the upgraded '
   357                         b'it to free up disk space once the upgraded '
   992                         b'repository is verified\n'
   358                         b'repository is verified\n'
   993                     )
   359                     )
   994                 )
   360                 )
   995 
   361 
   996             if sharedsafe.name in addedreqs:
   362             if upgrade_actions.sharesafe.name in addedreqs:
   997                 ui.warn(
   363                 ui.warn(
   998                     _(
   364                     _(
   999                         b'repository upgraded to share safe mode, existing'
   365                         b'repository upgraded to share safe mode, existing'
  1000                         b' shares will still work in old non-safe mode. '
   366                         b' shares will still work in old non-safe mode. '
  1001                         b'Re-share existing shares to use them in safe mode'
   367                         b'Re-share existing shares to use them in safe mode'
  1002                         b' New shares will be created in safe mode.\n'
   368                         b' New shares will be created in safe mode.\n'
  1003                     )
   369                     )
  1004                 )
   370                 )
  1005             if sharedsafe.name in removedreqs:
   371             if upgrade_actions.sharesafe.name in removedreqs:
  1006                 ui.warn(
   372                 ui.warn(
  1007                     _(
   373                     _(
  1008                         b'repository downgraded to not use share safe mode, '
   374                         b'repository downgraded to not use share safe mode, '
  1009                         b'existing shares will not work and needs to'
   375                         b'existing shares will not work and needs to'
  1010                         b' be reshared.\n'
   376                         b' be reshared.\n'