comparison mercurial/upgrade.py @ 46047:4b89cf08d8dc

upgrade: split definition and management of the actions from the main code This is a second step to clarify and clean up this code. The code responsible for definition which action exist, are possible and their compatibility if moved into a sub module. This clarify the main code and prepare further cleanup. Differential Revision: https://phab.mercurial-scm.org/D9477
author Pierre-Yves David <pierre-yves.david@octobus.net>
date Tue, 01 Dec 2020 15:11:06 +0100
parents f105c49e89cd
children f4f956342cf1
comparison
equal deleted inserted replaced
46046:f105c49e89cd 46047: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'