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