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