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