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