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