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