##// END OF EJS Templates
downgrade: if a compression is removed, consider that too...
Pulkit Goyal -
r46847:1fcab88a default
parent child Browse files
Show More
@@ -1,904 +1,911 b''
1 1 # upgrade.py - functions for in place upgrade of Mercurial repository
2 2 #
3 3 # Copyright (c) 2016-present, Gregory Szorc
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import absolute_import
9 9
10 10 from ..i18n import _
11 11 from .. import (
12 12 error,
13 13 localrepo,
14 14 requirements,
15 15 revlog,
16 16 util,
17 17 )
18 18
19 19 from ..utils import compression
20 20
21 21 # list of requirements that request a clone of all revlog if added/removed
22 22 RECLONES_REQUIREMENTS = {
23 23 b'generaldelta',
24 24 requirements.SPARSEREVLOG_REQUIREMENT,
25 25 }
26 26
27 27
28 28 def preservedrequirements(repo):
29 29 return set()
30 30
31 31
32 32 FORMAT_VARIANT = b'deficiency'
33 33 OPTIMISATION = b'optimization'
34 34
35 35
36 36 class improvement(object):
37 37 """Represents an improvement that can be made as part of an upgrade.
38 38
39 39 The following attributes are defined on each instance:
40 40
41 41 name
42 42 Machine-readable string uniquely identifying this improvement. It
43 43 will be mapped to an action later in the upgrade process.
44 44
45 45 type
46 46 Either ``FORMAT_VARIANT`` or ``OPTIMISATION``.
47 47 A format variant is where we change the storage format. Not all format
48 48 variant changes are an obvious problem.
49 49 An optimization is an action (sometimes optional) that
50 50 can be taken to further improve the state of the repository.
51 51
52 52 description
53 53 Message intended for humans explaining the improvement in more detail,
54 54 including the implications of it. For ``FORMAT_VARIANT`` types, should be
55 55 worded in the present tense. For ``OPTIMISATION`` types, should be
56 56 worded in the future tense.
57 57
58 58 upgrademessage
59 59 Message intended for humans explaining what an upgrade addressing this
60 60 issue will do. Should be worded in the future tense.
61 61
62 62 postupgrademessage
63 63 Message intended for humans which will be shown post an upgrade
64 64 operation when the improvement will be added
65 65
66 66 postdowngrademessage
67 67 Message intended for humans which will be shown post an upgrade
68 68 operation in which this improvement was removed
69 69 """
70 70
71 71 def __init__(self, name, type, description, upgrademessage):
72 72 self.name = name
73 73 self.type = type
74 74 self.description = description
75 75 self.upgrademessage = upgrademessage
76 76 self.postupgrademessage = None
77 77 self.postdowngrademessage = None
78 78
79 79 def __eq__(self, other):
80 80 if not isinstance(other, improvement):
81 81 # This is what python tell use to do
82 82 return NotImplemented
83 83 return self.name == other.name
84 84
85 85 def __ne__(self, other):
86 86 return not (self == other)
87 87
88 88 def __hash__(self):
89 89 return hash(self.name)
90 90
91 91
92 92 allformatvariant = []
93 93
94 94
95 95 def registerformatvariant(cls):
96 96 allformatvariant.append(cls)
97 97 return cls
98 98
99 99
100 100 class formatvariant(improvement):
101 101 """an improvement subclass dedicated to repository format"""
102 102
103 103 type = FORMAT_VARIANT
104 104 ### The following attributes should be defined for each class:
105 105
106 106 # machine-readable string uniquely identifying this improvement. it will be
107 107 # mapped to an action later in the upgrade process.
108 108 name = None
109 109
110 110 # message intended for humans explaining the improvement in more detail,
111 111 # including the implications of it ``FORMAT_VARIANT`` types, should be
112 112 # worded
113 113 # in the present tense.
114 114 description = None
115 115
116 116 # message intended for humans explaining what an upgrade addressing this
117 117 # issue will do. should be worded in the future tense.
118 118 upgrademessage = None
119 119
120 120 # value of current Mercurial default for new repository
121 121 default = None
122 122
123 123 # Message intended for humans which will be shown post an upgrade
124 124 # operation when the improvement will be added
125 125 postupgrademessage = None
126 126
127 127 # Message intended for humans which will be shown post an upgrade
128 128 # operation in which this improvement was removed
129 129 postdowngrademessage = None
130 130
131 131 def __init__(self):
132 132 raise NotImplementedError()
133 133
134 134 @staticmethod
135 135 def fromrepo(repo):
136 136 """current value of the variant in the repository"""
137 137 raise NotImplementedError()
138 138
139 139 @staticmethod
140 140 def fromconfig(repo):
141 141 """current value of the variant in the configuration"""
142 142 raise NotImplementedError()
143 143
144 144
145 145 class requirementformatvariant(formatvariant):
146 146 """formatvariant based on a 'requirement' name.
147 147
148 148 Many format variant are controlled by a 'requirement'. We define a small
149 149 subclass to factor the code.
150 150 """
151 151
152 152 # the requirement that control this format variant
153 153 _requirement = None
154 154
155 155 @staticmethod
156 156 def _newreporequirements(ui):
157 157 return localrepo.newreporequirements(
158 158 ui, localrepo.defaultcreateopts(ui)
159 159 )
160 160
161 161 @classmethod
162 162 def fromrepo(cls, repo):
163 163 assert cls._requirement is not None
164 164 return cls._requirement in repo.requirements
165 165
166 166 @classmethod
167 167 def fromconfig(cls, repo):
168 168 assert cls._requirement is not None
169 169 return cls._requirement in cls._newreporequirements(repo.ui)
170 170
171 171
172 172 @registerformatvariant
173 173 class fncache(requirementformatvariant):
174 174 name = b'fncache'
175 175
176 176 _requirement = b'fncache'
177 177
178 178 default = True
179 179
180 180 description = _(
181 181 b'long and reserved filenames may not work correctly; '
182 182 b'repository performance is sub-optimal'
183 183 )
184 184
185 185 upgrademessage = _(
186 186 b'repository will be more resilient to storing '
187 187 b'certain paths and performance of certain '
188 188 b'operations should be improved'
189 189 )
190 190
191 191
192 192 @registerformatvariant
193 193 class dotencode(requirementformatvariant):
194 194 name = b'dotencode'
195 195
196 196 _requirement = b'dotencode'
197 197
198 198 default = True
199 199
200 200 description = _(
201 201 b'storage of filenames beginning with a period or '
202 202 b'space may not work correctly'
203 203 )
204 204
205 205 upgrademessage = _(
206 206 b'repository will be better able to store files '
207 207 b'beginning with a space or period'
208 208 )
209 209
210 210
211 211 @registerformatvariant
212 212 class generaldelta(requirementformatvariant):
213 213 name = b'generaldelta'
214 214
215 215 _requirement = b'generaldelta'
216 216
217 217 default = True
218 218
219 219 description = _(
220 220 b'deltas within internal storage are unable to '
221 221 b'choose optimal revisions; repository is larger and '
222 222 b'slower than it could be; interaction with other '
223 223 b'repositories may require extra network and CPU '
224 224 b'resources, making "hg push" and "hg pull" slower'
225 225 )
226 226
227 227 upgrademessage = _(
228 228 b'repository storage will be able to create '
229 229 b'optimal deltas; new repository data will be '
230 230 b'smaller and read times should decrease; '
231 231 b'interacting with other repositories using this '
232 232 b'storage model should require less network and '
233 233 b'CPU resources, making "hg push" and "hg pull" '
234 234 b'faster'
235 235 )
236 236
237 237
238 238 @registerformatvariant
239 239 class sharesafe(requirementformatvariant):
240 240 name = b'exp-sharesafe'
241 241 _requirement = requirements.SHARESAFE_REQUIREMENT
242 242
243 243 default = False
244 244
245 245 description = _(
246 246 b'old shared repositories do not share source repository '
247 247 b'requirements and config. This leads to various problems '
248 248 b'when the source repository format is upgraded or some new '
249 249 b'extensions are enabled.'
250 250 )
251 251
252 252 upgrademessage = _(
253 253 b'Upgrades a repository to share-safe format so that future '
254 254 b'shares of this repository share its requirements and configs.'
255 255 )
256 256
257 257 postdowngrademessage = _(
258 258 b'repository downgraded to not use share safe mode, '
259 259 b'existing shares will not work and needs to'
260 260 b' be reshared.'
261 261 )
262 262
263 263 postupgrademessage = _(
264 264 b'repository upgraded to share safe mode, existing'
265 265 b' shares will still work in old non-safe mode. '
266 266 b'Re-share existing shares to use them in safe mode'
267 267 b' New shares will be created in safe mode.'
268 268 )
269 269
270 270
271 271 @registerformatvariant
272 272 class sparserevlog(requirementformatvariant):
273 273 name = b'sparserevlog'
274 274
275 275 _requirement = requirements.SPARSEREVLOG_REQUIREMENT
276 276
277 277 default = True
278 278
279 279 description = _(
280 280 b'in order to limit disk reading and memory usage on older '
281 281 b'version, the span of a delta chain from its root to its '
282 282 b'end is limited, whatever the relevant data in this span. '
283 283 b'This can severly limit Mercurial ability to build good '
284 284 b'chain of delta resulting is much more storage space being '
285 285 b'taken and limit reusability of on disk delta during '
286 286 b'exchange.'
287 287 )
288 288
289 289 upgrademessage = _(
290 290 b'Revlog supports delta chain with more unused data '
291 291 b'between payload. These gaps will be skipped at read '
292 292 b'time. This allows for better delta chains, making a '
293 293 b'better compression and faster exchange with server.'
294 294 )
295 295
296 296
297 297 @registerformatvariant
298 298 class sidedata(requirementformatvariant):
299 299 name = b'sidedata'
300 300
301 301 _requirement = requirements.SIDEDATA_REQUIREMENT
302 302
303 303 default = False
304 304
305 305 description = _(
306 306 b'Allows storage of extra data alongside a revision, '
307 307 b'unlocking various caching options.'
308 308 )
309 309
310 310 upgrademessage = _(b'Allows storage of extra data alongside a revision.')
311 311
312 312
313 313 @registerformatvariant
314 314 class persistentnodemap(requirementformatvariant):
315 315 name = b'persistent-nodemap'
316 316
317 317 _requirement = requirements.NODEMAP_REQUIREMENT
318 318
319 319 default = False
320 320
321 321 description = _(
322 322 b'persist the node -> rev mapping on disk to speedup lookup'
323 323 )
324 324
325 325 upgrademessage = _(b'Speedup revision lookup by node id.')
326 326
327 327
328 328 @registerformatvariant
329 329 class copiessdc(requirementformatvariant):
330 330 name = b'copies-sdc'
331 331
332 332 _requirement = requirements.COPIESSDC_REQUIREMENT
333 333
334 334 default = False
335 335
336 336 description = _(b'Stores copies information alongside changesets.')
337 337
338 338 upgrademessage = _(
339 339 b'Allows to use more efficient algorithm to deal with ' b'copy tracing.'
340 340 )
341 341
342 342
343 343 @registerformatvariant
344 344 class removecldeltachain(formatvariant):
345 345 name = b'plain-cl-delta'
346 346
347 347 default = True
348 348
349 349 description = _(
350 350 b'changelog storage is using deltas instead of '
351 351 b'raw entries; changelog reading and any '
352 352 b'operation relying on changelog data are slower '
353 353 b'than they could be'
354 354 )
355 355
356 356 upgrademessage = _(
357 357 b'changelog storage will be reformated to '
358 358 b'store raw entries; changelog reading will be '
359 359 b'faster; changelog size may be reduced'
360 360 )
361 361
362 362 @staticmethod
363 363 def fromrepo(repo):
364 364 # Mercurial 4.0 changed changelogs to not use delta chains. Search for
365 365 # changelogs with deltas.
366 366 cl = repo.changelog
367 367 chainbase = cl.chainbase
368 368 return all(rev == chainbase(rev) for rev in cl)
369 369
370 370 @staticmethod
371 371 def fromconfig(repo):
372 372 return True
373 373
374 374
375 375 @registerformatvariant
376 376 class compressionengine(formatvariant):
377 377 name = b'compression'
378 378 default = b'zlib'
379 379
380 380 description = _(
381 381 b'Compresion algorithm used to compress data. '
382 382 b'Some engine are faster than other'
383 383 )
384 384
385 385 upgrademessage = _(
386 386 b'revlog content will be recompressed with the new algorithm.'
387 387 )
388 388
389 389 @classmethod
390 390 def fromrepo(cls, repo):
391 391 # we allow multiple compression engine requirement to co-exist because
392 392 # strickly speaking, revlog seems to support mixed compression style.
393 393 #
394 394 # The compression used for new entries will be "the last one"
395 395 compression = b'zlib'
396 396 for req in repo.requirements:
397 397 prefix = req.startswith
398 398 if prefix(b'revlog-compression-') or prefix(b'exp-compression-'):
399 399 compression = req.split(b'-', 2)[2]
400 400 return compression
401 401
402 402 @classmethod
403 403 def fromconfig(cls, repo):
404 404 compengines = repo.ui.configlist(b'format', b'revlog-compression')
405 405 # return the first valid value as the selection code would do
406 406 for comp in compengines:
407 407 if comp in util.compengines:
408 408 return comp
409 409
410 410 # no valide compression found lets display it all for clarity
411 411 return b','.join(compengines)
412 412
413 413
414 414 @registerformatvariant
415 415 class compressionlevel(formatvariant):
416 416 name = b'compression-level'
417 417 default = b'default'
418 418
419 419 description = _(b'compression level')
420 420
421 421 upgrademessage = _(b'revlog content will be recompressed')
422 422
423 423 @classmethod
424 424 def fromrepo(cls, repo):
425 425 comp = compressionengine.fromrepo(repo)
426 426 level = None
427 427 if comp == b'zlib':
428 428 level = repo.ui.configint(b'storage', b'revlog.zlib.level')
429 429 elif comp == b'zstd':
430 430 level = repo.ui.configint(b'storage', b'revlog.zstd.level')
431 431 if level is None:
432 432 return b'default'
433 433 return bytes(level)
434 434
435 435 @classmethod
436 436 def fromconfig(cls, repo):
437 437 comp = compressionengine.fromconfig(repo)
438 438 level = None
439 439 if comp == b'zlib':
440 440 level = repo.ui.configint(b'storage', b'revlog.zlib.level')
441 441 elif comp == b'zstd':
442 442 level = repo.ui.configint(b'storage', b'revlog.zstd.level')
443 443 if level is None:
444 444 return b'default'
445 445 return bytes(level)
446 446
447 447
448 448 def find_format_upgrades(repo):
449 449 """returns a list of format upgrades which can be perform on the repo"""
450 450 upgrades = []
451 451
452 452 # We could detect lack of revlogv1 and store here, but they were added
453 453 # in 0.9.2 and we don't support upgrading repos without these
454 454 # requirements, so let's not bother.
455 455
456 456 for fv in allformatvariant:
457 457 if not fv.fromrepo(repo):
458 458 upgrades.append(fv)
459 459
460 460 return upgrades
461 461
462 462
463 463 def find_format_downgrades(repo):
464 464 """returns a list of format downgrades which will be performed on the repo
465 465 because of disabled config option for them"""
466 466
467 467 downgrades = []
468 468
469 469 for fv in allformatvariant:
470 if fv.name == b'compression':
471 # If there is a compression change between repository
472 # and config, destination repository compression will change
473 # and current compression will be removed.
474 if fv.fromrepo(repo) != fv.fromconfig(repo):
475 downgrades.append(fv)
476 continue
470 477 # format variant exist in repo but does not exist in new repository
471 478 # config
472 479 if fv.fromrepo(repo) and not fv.fromconfig(repo):
473 480 downgrades.append(fv)
474 481
475 482 return downgrades
476 483
477 484
478 485 ALL_OPTIMISATIONS = []
479 486
480 487
481 488 def register_optimization(obj):
482 489 ALL_OPTIMISATIONS.append(obj)
483 490 return obj
484 491
485 492
486 493 register_optimization(
487 494 improvement(
488 495 name=b're-delta-parent',
489 496 type=OPTIMISATION,
490 497 description=_(
491 498 b'deltas within internal storage will be recalculated to '
492 499 b'choose an optimal base revision where this was not '
493 500 b'already done; the size of the repository may shrink and '
494 501 b'various operations may become faster; the first time '
495 502 b'this optimization is performed could slow down upgrade '
496 503 b'execution considerably; subsequent invocations should '
497 504 b'not run noticeably slower'
498 505 ),
499 506 upgrademessage=_(
500 507 b'deltas within internal storage will choose a new '
501 508 b'base revision if needed'
502 509 ),
503 510 )
504 511 )
505 512
506 513 register_optimization(
507 514 improvement(
508 515 name=b're-delta-multibase',
509 516 type=OPTIMISATION,
510 517 description=_(
511 518 b'deltas within internal storage will be recalculated '
512 519 b'against multiple base revision and the smallest '
513 520 b'difference will be used; the size of the repository may '
514 521 b'shrink significantly when there are many merges; this '
515 522 b'optimization will slow down execution in proportion to '
516 523 b'the number of merges in the repository and the amount '
517 524 b'of files in the repository; this slow down should not '
518 525 b'be significant unless there are tens of thousands of '
519 526 b'files and thousands of merges'
520 527 ),
521 528 upgrademessage=_(
522 529 b'deltas within internal storage will choose an '
523 530 b'optimal delta by computing deltas against multiple '
524 531 b'parents; may slow down execution time '
525 532 b'significantly'
526 533 ),
527 534 )
528 535 )
529 536
530 537 register_optimization(
531 538 improvement(
532 539 name=b're-delta-all',
533 540 type=OPTIMISATION,
534 541 description=_(
535 542 b'deltas within internal storage will always be '
536 543 b'recalculated without reusing prior deltas; this will '
537 544 b'likely make execution run several times slower; this '
538 545 b'optimization is typically not needed'
539 546 ),
540 547 upgrademessage=_(
541 548 b'deltas within internal storage will be fully '
542 549 b'recomputed; this will likely drastically slow down '
543 550 b'execution time'
544 551 ),
545 552 )
546 553 )
547 554
548 555 register_optimization(
549 556 improvement(
550 557 name=b're-delta-fulladd',
551 558 type=OPTIMISATION,
552 559 description=_(
553 560 b'every revision will be re-added as if it was new '
554 561 b'content. It will go through the full storage '
555 562 b'mechanism giving extensions a chance to process it '
556 563 b'(eg. lfs). This is similar to "re-delta-all" but even '
557 564 b'slower since more logic is involved.'
558 565 ),
559 566 upgrademessage=_(
560 567 b'each revision will be added as new content to the '
561 568 b'internal storage; this will likely drastically slow '
562 569 b'down execution time, but some extensions might need '
563 570 b'it'
564 571 ),
565 572 )
566 573 )
567 574
568 575
569 576 def findoptimizations(repo):
570 577 """Determine optimisation that could be used during upgrade"""
571 578 # These are unconditionally added. There is logic later that figures out
572 579 # which ones to apply.
573 580 return list(ALL_OPTIMISATIONS)
574 581
575 582
576 583 def determine_upgrade_actions(
577 584 repo, format_upgrades, optimizations, sourcereqs, destreqs
578 585 ):
579 586 """Determine upgrade actions that will be performed.
580 587
581 588 Given a list of improvements as returned by ``find_format_upgrades`` and
582 589 ``findoptimizations``, determine the list of upgrade actions that
583 590 will be performed.
584 591
585 592 The role of this function is to filter improvements if needed, apply
586 593 recommended optimizations from the improvements list that make sense,
587 594 etc.
588 595
589 596 Returns a list of action names.
590 597 """
591 598 newactions = []
592 599
593 600 for d in format_upgrades:
594 601 name = d._requirement
595 602
596 603 # If the action is a requirement that doesn't show up in the
597 604 # destination requirements, prune the action.
598 605 if name is not None and name not in destreqs:
599 606 continue
600 607
601 608 newactions.append(d)
602 609
603 610 newactions.extend(o for o in sorted(optimizations) if o not in newactions)
604 611
605 612 # FUTURE consider adding some optimizations here for certain transitions.
606 613 # e.g. adding generaldelta could schedule parent redeltas.
607 614
608 615 return newactions
609 616
610 617
611 618 class UpgradeOperation(object):
612 619 """represent the work to be done during an upgrade"""
613 620
614 621 def __init__(
615 622 self,
616 623 ui,
617 624 new_requirements,
618 625 current_requirements,
619 626 upgrade_actions,
620 627 removed_actions,
621 628 revlogs_to_process,
622 629 ):
623 630 self.ui = ui
624 631 self.new_requirements = new_requirements
625 632 self.current_requirements = current_requirements
626 633 # list of upgrade actions the operation will perform
627 634 self.upgrade_actions = upgrade_actions
628 635 self._upgrade_actions_names = set([a.name for a in upgrade_actions])
629 636 self.removed_actions = removed_actions
630 637 self.revlogs_to_process = revlogs_to_process
631 638 # requirements which will be added by the operation
632 639 self._added_requirements = (
633 640 self.new_requirements - self.current_requirements
634 641 )
635 642 # requirements which will be removed by the operation
636 643 self._removed_requirements = (
637 644 self.current_requirements - self.new_requirements
638 645 )
639 646 # requirements which will be preserved by the operation
640 647 self._preserved_requirements = (
641 648 self.current_requirements & self.new_requirements
642 649 )
643 650 # optimizations which are not used and it's recommended that they
644 651 # should use them
645 652 all_optimizations = findoptimizations(None)
646 653 self.unused_optimizations = [
647 654 i for i in all_optimizations if i not in self.upgrade_actions
648 655 ]
649 656
650 657 # delta reuse mode of this upgrade operation
651 658 self.delta_reuse_mode = revlog.revlog.DELTAREUSEALWAYS
652 659 if b're-delta-all' in self._upgrade_actions_names:
653 660 self.delta_reuse_mode = revlog.revlog.DELTAREUSENEVER
654 661 elif b're-delta-parent' in self._upgrade_actions_names:
655 662 self.delta_reuse_mode = revlog.revlog.DELTAREUSESAMEREVS
656 663 elif b're-delta-multibase' in self._upgrade_actions_names:
657 664 self.delta_reuse_mode = revlog.revlog.DELTAREUSESAMEREVS
658 665 elif b're-delta-fulladd' in self._upgrade_actions_names:
659 666 self.delta_reuse_mode = revlog.revlog.DELTAREUSEFULLADD
660 667
661 668 # should this operation force re-delta of both parents
662 669 self.force_re_delta_both_parents = (
663 670 b're-delta-multibase' in self._upgrade_actions_names
664 671 )
665 672
666 673 def _write_labeled(self, l, label):
667 674 """
668 675 Utility function to aid writing of a list under one label
669 676 """
670 677 first = True
671 678 for r in sorted(l):
672 679 if not first:
673 680 self.ui.write(b', ')
674 681 self.ui.write(r, label=label)
675 682 first = False
676 683
677 684 def print_requirements(self):
678 685 self.ui.write(_(b'requirements\n'))
679 686 self.ui.write(_(b' preserved: '))
680 687 self._write_labeled(
681 688 self._preserved_requirements, "upgrade-repo.requirement.preserved"
682 689 )
683 690 self.ui.write((b'\n'))
684 691 if self._removed_requirements:
685 692 self.ui.write(_(b' removed: '))
686 693 self._write_labeled(
687 694 self._removed_requirements, "upgrade-repo.requirement.removed"
688 695 )
689 696 self.ui.write((b'\n'))
690 697 if self._added_requirements:
691 698 self.ui.write(_(b' added: '))
692 699 self._write_labeled(
693 700 self._added_requirements, "upgrade-repo.requirement.added"
694 701 )
695 702 self.ui.write((b'\n'))
696 703 self.ui.write(b'\n')
697 704
698 705 def print_optimisations(self):
699 706 optimisations = [
700 707 a for a in self.upgrade_actions if a.type == OPTIMISATION
701 708 ]
702 709 optimisations.sort(key=lambda a: a.name)
703 710 if optimisations:
704 711 self.ui.write(_(b'optimisations: '))
705 712 self._write_labeled(
706 713 [a.name for a in optimisations],
707 714 "upgrade-repo.optimisation.performed",
708 715 )
709 716 self.ui.write(b'\n\n')
710 717
711 718 def print_upgrade_actions(self):
712 719 for a in self.upgrade_actions:
713 720 self.ui.status(b'%s\n %s\n\n' % (a.name, a.upgrademessage))
714 721
715 722 def print_affected_revlogs(self):
716 723 if not self.revlogs_to_process:
717 724 self.ui.write((b'no revlogs to process\n'))
718 725 else:
719 726 self.ui.write((b'processed revlogs:\n'))
720 727 for r in sorted(self.revlogs_to_process):
721 728 self.ui.write((b' - %s\n' % r))
722 729 self.ui.write((b'\n'))
723 730
724 731 def print_unused_optimizations(self):
725 732 for i in self.unused_optimizations:
726 733 self.ui.status(_(b'%s\n %s\n\n') % (i.name, i.description))
727 734
728 735 def has_upgrade_action(self, name):
729 736 """ Check whether the upgrade operation will perform this action """
730 737 return name in self._upgrade_actions_names
731 738
732 739 def print_post_op_messages(self):
733 740 """ print post upgrade operation warning messages """
734 741 for a in self.upgrade_actions:
735 742 if a.postupgrademessage is not None:
736 743 self.ui.warn(b'%s\n' % a.postupgrademessage)
737 744 for a in self.removed_actions:
738 745 if a.postdowngrademessage is not None:
739 746 self.ui.warn(b'%s\n' % a.postdowngrademessage)
740 747
741 748
742 749 ### Code checking if a repository can got through the upgrade process at all. #
743 750
744 751
745 752 def requiredsourcerequirements(repo):
746 753 """Obtain requirements required to be present to upgrade a repo.
747 754
748 755 An upgrade will not be allowed if the repository doesn't have the
749 756 requirements returned by this function.
750 757 """
751 758 return {
752 759 # Introduced in Mercurial 0.9.2.
753 760 b'revlogv1',
754 761 # Introduced in Mercurial 0.9.2.
755 762 b'store',
756 763 }
757 764
758 765
759 766 def blocksourcerequirements(repo):
760 767 """Obtain requirements that will prevent an upgrade from occurring.
761 768
762 769 An upgrade cannot be performed if the source repository contains a
763 770 requirements in the returned set.
764 771 """
765 772 return {
766 773 # The upgrade code does not yet support these experimental features.
767 774 # This is an artificial limitation.
768 775 requirements.TREEMANIFEST_REQUIREMENT,
769 776 # This was a precursor to generaldelta and was never enabled by default.
770 777 # It should (hopefully) not exist in the wild.
771 778 b'parentdelta',
772 779 # Upgrade should operate on the actual store, not the shared link.
773 780 requirements.SHARED_REQUIREMENT,
774 781 }
775 782
776 783
777 784 def check_source_requirements(repo):
778 785 """Ensure that no existing requirements prevent the repository upgrade"""
779 786
780 787 required = requiredsourcerequirements(repo)
781 788 missingreqs = required - repo.requirements
782 789 if missingreqs:
783 790 msg = _(b'cannot upgrade repository; requirement missing: %s')
784 791 missingreqs = b', '.join(sorted(missingreqs))
785 792 raise error.Abort(msg % missingreqs)
786 793
787 794 blocking = blocksourcerequirements(repo)
788 795 blockingreqs = blocking & repo.requirements
789 796 if blockingreqs:
790 797 m = _(b'cannot upgrade repository; unsupported source requirement: %s')
791 798 blockingreqs = b', '.join(sorted(blockingreqs))
792 799 raise error.Abort(m % blockingreqs)
793 800
794 801
795 802 ### Verify the validity of the planned requirement changes ####################
796 803
797 804
798 805 def supportremovedrequirements(repo):
799 806 """Obtain requirements that can be removed during an upgrade.
800 807
801 808 If an upgrade were to create a repository that dropped a requirement,
802 809 the dropped requirement must appear in the returned set for the upgrade
803 810 to be allowed.
804 811 """
805 812 supported = {
806 813 requirements.SPARSEREVLOG_REQUIREMENT,
807 814 requirements.SIDEDATA_REQUIREMENT,
808 815 requirements.COPIESSDC_REQUIREMENT,
809 816 requirements.NODEMAP_REQUIREMENT,
810 817 requirements.SHARESAFE_REQUIREMENT,
811 818 }
812 819 for name in compression.compengines:
813 820 engine = compression.compengines[name]
814 821 if engine.available() and engine.revlogheader():
815 822 supported.add(b'exp-compression-%s' % name)
816 823 if engine.name() == b'zstd':
817 824 supported.add(b'revlog-compression-zstd')
818 825 return supported
819 826
820 827
821 828 def supporteddestrequirements(repo):
822 829 """Obtain requirements that upgrade supports in the destination.
823 830
824 831 If the result of the upgrade would create requirements not in this set,
825 832 the upgrade is disallowed.
826 833
827 834 Extensions should monkeypatch this to add their custom requirements.
828 835 """
829 836 supported = {
830 837 b'dotencode',
831 838 b'fncache',
832 839 b'generaldelta',
833 840 b'revlogv1',
834 841 b'store',
835 842 requirements.SPARSEREVLOG_REQUIREMENT,
836 843 requirements.SIDEDATA_REQUIREMENT,
837 844 requirements.COPIESSDC_REQUIREMENT,
838 845 requirements.NODEMAP_REQUIREMENT,
839 846 requirements.SHARESAFE_REQUIREMENT,
840 847 }
841 848 for name in compression.compengines:
842 849 engine = compression.compengines[name]
843 850 if engine.available() and engine.revlogheader():
844 851 supported.add(b'exp-compression-%s' % name)
845 852 if engine.name() == b'zstd':
846 853 supported.add(b'revlog-compression-zstd')
847 854 return supported
848 855
849 856
850 857 def allowednewrequirements(repo):
851 858 """Obtain requirements that can be added to a repository during upgrade.
852 859
853 860 This is used to disallow proposed requirements from being added when
854 861 they weren't present before.
855 862
856 863 We use a list of allowed requirement additions instead of a list of known
857 864 bad additions because the whitelist approach is safer and will prevent
858 865 future, unknown requirements from accidentally being added.
859 866 """
860 867 supported = {
861 868 b'dotencode',
862 869 b'fncache',
863 870 b'generaldelta',
864 871 requirements.SPARSEREVLOG_REQUIREMENT,
865 872 requirements.SIDEDATA_REQUIREMENT,
866 873 requirements.COPIESSDC_REQUIREMENT,
867 874 requirements.NODEMAP_REQUIREMENT,
868 875 requirements.SHARESAFE_REQUIREMENT,
869 876 }
870 877 for name in compression.compengines:
871 878 engine = compression.compengines[name]
872 879 if engine.available() and engine.revlogheader():
873 880 supported.add(b'exp-compression-%s' % name)
874 881 if engine.name() == b'zstd':
875 882 supported.add(b'revlog-compression-zstd')
876 883 return supported
877 884
878 885
879 886 def check_requirements_changes(repo, new_reqs):
880 887 old_reqs = repo.requirements
881 888
882 889 support_removal = supportremovedrequirements(repo)
883 890 no_remove_reqs = old_reqs - new_reqs - support_removal
884 891 if no_remove_reqs:
885 892 msg = _(b'cannot upgrade repository; requirement would be removed: %s')
886 893 no_remove_reqs = b', '.join(sorted(no_remove_reqs))
887 894 raise error.Abort(msg % no_remove_reqs)
888 895
889 896 support_addition = allowednewrequirements(repo)
890 897 no_add_reqs = new_reqs - old_reqs - support_addition
891 898 if no_add_reqs:
892 899 m = _(b'cannot upgrade repository; do not support adding requirement: ')
893 900 no_add_reqs = b', '.join(sorted(no_add_reqs))
894 901 raise error.Abort(m + no_add_reqs)
895 902
896 903 supported = supporteddestrequirements(repo)
897 904 unsupported_reqs = new_reqs - supported
898 905 if unsupported_reqs:
899 906 msg = _(
900 907 b'cannot upgrade repository; do not support destination '
901 908 b'requirement: %s'
902 909 )
903 910 unsupported_reqs = b', '.join(sorted(unsupported_reqs))
904 911 raise error.Abort(msg % unsupported_reqs)
General Comments 0
You need to be logged in to leave comments. Login now