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