##// END OF EJS Templates
changelog-v2: mark upgrade action as changelog only...
marmoute -
r50081:801ef401 default
parent child Browse files
Show More
@@ -1,1097 +1,1100 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 needs to'
292 b'existing shares will not work and needs to'
293 b' be reshared.'
293 b' be reshared.'
294 )
294 )
295
295
296 postupgrademessage = _(
296 postupgrademessage = _(
297 b'repository upgraded to share safe mode, existing'
297 b'repository upgraded to share safe mode, existing'
298 b' shares will still work in old non-safe mode. '
298 b' shares will still work in old non-safe mode. '
299 b'Re-share existing shares to use them in safe mode'
299 b'Re-share existing shares to use them in safe mode'
300 b' New shares will be created in safe mode.'
300 b' New shares will be created in safe mode.'
301 )
301 )
302
302
303 # upgrade only needs to change the requirements
303 # upgrade only needs to change the requirements
304 touches_filelogs = False
304 touches_filelogs = False
305 touches_manifests = False
305 touches_manifests = False
306 touches_changelog = False
306 touches_changelog = False
307 touches_requirements = True
307 touches_requirements = True
308
308
309
309
310 @registerformatvariant
310 @registerformatvariant
311 class sparserevlog(requirementformatvariant):
311 class sparserevlog(requirementformatvariant):
312 name = b'sparserevlog'
312 name = b'sparserevlog'
313
313
314 _requirement = requirements.SPARSEREVLOG_REQUIREMENT
314 _requirement = requirements.SPARSEREVLOG_REQUIREMENT
315
315
316 default = True
316 default = True
317
317
318 description = _(
318 description = _(
319 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 '
320 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 '
321 b'end is limited, whatever the relevant data in this span. '
321 b'end is limited, whatever the relevant data in this span. '
322 b'This can severly limit Mercurial ability to build good '
322 b'This can severly limit Mercurial ability to build good '
323 b'chain of delta resulting is much more storage space being '
323 b'chain of delta resulting is much more storage space being '
324 b'taken and limit reusability of on disk delta during '
324 b'taken and limit reusability of on disk delta during '
325 b'exchange.'
325 b'exchange.'
326 )
326 )
327
327
328 upgrademessage = _(
328 upgrademessage = _(
329 b'Revlog supports delta chain with more unused data '
329 b'Revlog supports delta chain with more unused data '
330 b'between payload. These gaps will be skipped at read '
330 b'between payload. These gaps will be skipped at read '
331 b'time. This allows for better delta chains, making a '
331 b'time. This allows for better delta chains, making a '
332 b'better compression and faster exchange with server.'
332 b'better compression and faster exchange with server.'
333 )
333 )
334
334
335
335
336 @registerformatvariant
336 @registerformatvariant
337 class persistentnodemap(requirementformatvariant):
337 class persistentnodemap(requirementformatvariant):
338 name = b'persistent-nodemap'
338 name = b'persistent-nodemap'
339
339
340 _requirement = requirements.NODEMAP_REQUIREMENT
340 _requirement = requirements.NODEMAP_REQUIREMENT
341
341
342 default = False
342 default = False
343
343
344 description = _(
344 description = _(
345 b'persist the node -> rev mapping on disk to speedup lookup'
345 b'persist the node -> rev mapping on disk to speedup lookup'
346 )
346 )
347
347
348 upgrademessage = _(b'Speedup revision lookup by node id.')
348 upgrademessage = _(b'Speedup revision lookup by node id.')
349
349
350
350
351 @registerformatvariant
351 @registerformatvariant
352 class copiessdc(requirementformatvariant):
352 class copiessdc(requirementformatvariant):
353 name = b'copies-sdc'
353 name = b'copies-sdc'
354
354
355 _requirement = requirements.COPIESSDC_REQUIREMENT
355 _requirement = requirements.COPIESSDC_REQUIREMENT
356
356
357 default = False
357 default = False
358
358
359 description = _(b'Stores copies information alongside changesets.')
359 description = _(b'Stores copies information alongside changesets.')
360
360
361 upgrademessage = _(
361 upgrademessage = _(
362 b'Allows to use more efficient algorithm to deal with ' b'copy tracing.'
362 b'Allows to use more efficient algorithm to deal with ' b'copy tracing.'
363 )
363 )
364
364
365
365
366 @registerformatvariant
366 @registerformatvariant
367 class revlogv2(requirementformatvariant):
367 class revlogv2(requirementformatvariant):
368 name = b'revlog-v2'
368 name = b'revlog-v2'
369 _requirement = requirements.REVLOGV2_REQUIREMENT
369 _requirement = requirements.REVLOGV2_REQUIREMENT
370 default = False
370 default = False
371 description = _(b'Version 2 of the revlog.')
371 description = _(b'Version 2 of the revlog.')
372 upgrademessage = _(b'very experimental')
372 upgrademessage = _(b'very experimental')
373
373
374
374
375 @registerformatvariant
375 @registerformatvariant
376 class changelogv2(requirementformatvariant):
376 class changelogv2(requirementformatvariant):
377 name = b'changelog-v2'
377 name = b'changelog-v2'
378 _requirement = requirements.CHANGELOGV2_REQUIREMENT
378 _requirement = requirements.CHANGELOGV2_REQUIREMENT
379 default = False
379 default = False
380 description = _(b'An iteration of the revlog focussed on changelog needs.')
380 description = _(b'An iteration of the revlog focussed on changelog needs.')
381 upgrademessage = _(b'quite experimental')
381 upgrademessage = _(b'quite experimental')
382
382
383 touches_filelogs = False
384 touches_manifests = False
385
383
386
384 @registerformatvariant
387 @registerformatvariant
385 class removecldeltachain(formatvariant):
388 class removecldeltachain(formatvariant):
386 name = b'plain-cl-delta'
389 name = b'plain-cl-delta'
387
390
388 default = True
391 default = True
389
392
390 description = _(
393 description = _(
391 b'changelog storage is using deltas instead of '
394 b'changelog storage is using deltas instead of '
392 b'raw entries; changelog reading and any '
395 b'raw entries; changelog reading and any '
393 b'operation relying on changelog data are slower '
396 b'operation relying on changelog data are slower '
394 b'than they could be'
397 b'than they could be'
395 )
398 )
396
399
397 upgrademessage = _(
400 upgrademessage = _(
398 b'changelog storage will be reformated to '
401 b'changelog storage will be reformated to '
399 b'store raw entries; changelog reading will be '
402 b'store raw entries; changelog reading will be '
400 b'faster; changelog size may be reduced'
403 b'faster; changelog size may be reduced'
401 )
404 )
402
405
403 @staticmethod
406 @staticmethod
404 def fromrepo(repo):
407 def fromrepo(repo):
405 # Mercurial 4.0 changed changelogs to not use delta chains. Search for
408 # Mercurial 4.0 changed changelogs to not use delta chains. Search for
406 # changelogs with deltas.
409 # changelogs with deltas.
407 cl = repo.changelog
410 cl = repo.changelog
408 chainbase = cl.chainbase
411 chainbase = cl.chainbase
409 return all(rev == chainbase(rev) for rev in cl)
412 return all(rev == chainbase(rev) for rev in cl)
410
413
411 @staticmethod
414 @staticmethod
412 def fromconfig(repo):
415 def fromconfig(repo):
413 return True
416 return True
414
417
415
418
416 _has_zstd = (
419 _has_zstd = (
417 b'zstd' in util.compengines
420 b'zstd' in util.compengines
418 and util.compengines[b'zstd'].available()
421 and util.compengines[b'zstd'].available()
419 and util.compengines[b'zstd'].revlogheader()
422 and util.compengines[b'zstd'].revlogheader()
420 )
423 )
421
424
422
425
423 @registerformatvariant
426 @registerformatvariant
424 class compressionengine(formatvariant):
427 class compressionengine(formatvariant):
425 name = b'compression'
428 name = b'compression'
426
429
427 if _has_zstd:
430 if _has_zstd:
428 default = b'zstd'
431 default = b'zstd'
429 else:
432 else:
430 default = b'zlib'
433 default = b'zlib'
431
434
432 description = _(
435 description = _(
433 b'Compresion algorithm used to compress data. '
436 b'Compresion algorithm used to compress data. '
434 b'Some engine are faster than other'
437 b'Some engine are faster than other'
435 )
438 )
436
439
437 upgrademessage = _(
440 upgrademessage = _(
438 b'revlog content will be recompressed with the new algorithm.'
441 b'revlog content will be recompressed with the new algorithm.'
439 )
442 )
440
443
441 @classmethod
444 @classmethod
442 def fromrepo(cls, repo):
445 def fromrepo(cls, repo):
443 # we allow multiple compression engine requirement to co-exist because
446 # we allow multiple compression engine requirement to co-exist because
444 # strickly speaking, revlog seems to support mixed compression style.
447 # strickly speaking, revlog seems to support mixed compression style.
445 #
448 #
446 # The compression used for new entries will be "the last one"
449 # The compression used for new entries will be "the last one"
447 compression = b'zlib'
450 compression = b'zlib'
448 for req in repo.requirements:
451 for req in repo.requirements:
449 prefix = req.startswith
452 prefix = req.startswith
450 if prefix(b'revlog-compression-') or prefix(b'exp-compression-'):
453 if prefix(b'revlog-compression-') or prefix(b'exp-compression-'):
451 compression = req.split(b'-', 2)[2]
454 compression = req.split(b'-', 2)[2]
452 return compression
455 return compression
453
456
454 @classmethod
457 @classmethod
455 def fromconfig(cls, repo):
458 def fromconfig(cls, repo):
456 compengines = repo.ui.configlist(b'format', b'revlog-compression')
459 compengines = repo.ui.configlist(b'format', b'revlog-compression')
457 # return the first valid value as the selection code would do
460 # return the first valid value as the selection code would do
458 for comp in compengines:
461 for comp in compengines:
459 if comp in util.compengines:
462 if comp in util.compengines:
460 e = util.compengines[comp]
463 e = util.compengines[comp]
461 if e.available() and e.revlogheader():
464 if e.available() and e.revlogheader():
462 return comp
465 return comp
463
466
464 # no valide compression found lets display it all for clarity
467 # no valide compression found lets display it all for clarity
465 return b','.join(compengines)
468 return b','.join(compengines)
466
469
467
470
468 @registerformatvariant
471 @registerformatvariant
469 class compressionlevel(formatvariant):
472 class compressionlevel(formatvariant):
470 name = b'compression-level'
473 name = b'compression-level'
471 default = b'default'
474 default = b'default'
472
475
473 description = _(b'compression level')
476 description = _(b'compression level')
474
477
475 upgrademessage = _(b'revlog content will be recompressed')
478 upgrademessage = _(b'revlog content will be recompressed')
476
479
477 @classmethod
480 @classmethod
478 def fromrepo(cls, repo):
481 def fromrepo(cls, repo):
479 comp = compressionengine.fromrepo(repo)
482 comp = compressionengine.fromrepo(repo)
480 level = None
483 level = None
481 if comp == b'zlib':
484 if comp == b'zlib':
482 level = repo.ui.configint(b'storage', b'revlog.zlib.level')
485 level = repo.ui.configint(b'storage', b'revlog.zlib.level')
483 elif comp == b'zstd':
486 elif comp == b'zstd':
484 level = repo.ui.configint(b'storage', b'revlog.zstd.level')
487 level = repo.ui.configint(b'storage', b'revlog.zstd.level')
485 if level is None:
488 if level is None:
486 return b'default'
489 return b'default'
487 return bytes(level)
490 return bytes(level)
488
491
489 @classmethod
492 @classmethod
490 def fromconfig(cls, repo):
493 def fromconfig(cls, repo):
491 comp = compressionengine.fromconfig(repo)
494 comp = compressionengine.fromconfig(repo)
492 level = None
495 level = None
493 if comp == b'zlib':
496 if comp == b'zlib':
494 level = repo.ui.configint(b'storage', b'revlog.zlib.level')
497 level = repo.ui.configint(b'storage', b'revlog.zlib.level')
495 elif comp == b'zstd':
498 elif comp == b'zstd':
496 level = repo.ui.configint(b'storage', b'revlog.zstd.level')
499 level = repo.ui.configint(b'storage', b'revlog.zstd.level')
497 if level is None:
500 if level is None:
498 return b'default'
501 return b'default'
499 return bytes(level)
502 return bytes(level)
500
503
501
504
502 def find_format_upgrades(repo):
505 def find_format_upgrades(repo):
503 """returns a list of format upgrades which can be perform on the repo"""
506 """returns a list of format upgrades which can be perform on the repo"""
504 upgrades = []
507 upgrades = []
505
508
506 # We could detect lack of revlogv1 and store here, but they were added
509 # We could detect lack of revlogv1 and store here, but they were added
507 # in 0.9.2 and we don't support upgrading repos without these
510 # in 0.9.2 and we don't support upgrading repos without these
508 # requirements, so let's not bother.
511 # requirements, so let's not bother.
509
512
510 for fv in allformatvariant:
513 for fv in allformatvariant:
511 if not fv.fromrepo(repo):
514 if not fv.fromrepo(repo):
512 upgrades.append(fv)
515 upgrades.append(fv)
513
516
514 return upgrades
517 return upgrades
515
518
516
519
517 def find_format_downgrades(repo):
520 def find_format_downgrades(repo):
518 """returns a list of format downgrades which will be performed on the repo
521 """returns a list of format downgrades which will be performed on the repo
519 because of disabled config option for them"""
522 because of disabled config option for them"""
520
523
521 downgrades = []
524 downgrades = []
522
525
523 for fv in allformatvariant:
526 for fv in allformatvariant:
524 if fv.name == b'compression':
527 if fv.name == b'compression':
525 # If there is a compression change between repository
528 # If there is a compression change between repository
526 # and config, destination repository compression will change
529 # and config, destination repository compression will change
527 # and current compression will be removed.
530 # and current compression will be removed.
528 if fv.fromrepo(repo) != fv.fromconfig(repo):
531 if fv.fromrepo(repo) != fv.fromconfig(repo):
529 downgrades.append(fv)
532 downgrades.append(fv)
530 continue
533 continue
531 # format variant exist in repo but does not exist in new repository
534 # format variant exist in repo but does not exist in new repository
532 # config
535 # config
533 if fv.fromrepo(repo) and not fv.fromconfig(repo):
536 if fv.fromrepo(repo) and not fv.fromconfig(repo):
534 downgrades.append(fv)
537 downgrades.append(fv)
535
538
536 return downgrades
539 return downgrades
537
540
538
541
539 ALL_OPTIMISATIONS = []
542 ALL_OPTIMISATIONS = []
540
543
541
544
542 def register_optimization(obj):
545 def register_optimization(obj):
543 ALL_OPTIMISATIONS.append(obj)
546 ALL_OPTIMISATIONS.append(obj)
544 return obj
547 return obj
545
548
546
549
547 class optimization(improvement):
550 class optimization(improvement):
548 """an improvement subclass dedicated to optimizations"""
551 """an improvement subclass dedicated to optimizations"""
549
552
550 type = OPTIMISATION
553 type = OPTIMISATION
551
554
552
555
553 @register_optimization
556 @register_optimization
554 class redeltaparents(optimization):
557 class redeltaparents(optimization):
555 name = b're-delta-parent'
558 name = b're-delta-parent'
556
559
557 type = OPTIMISATION
560 type = OPTIMISATION
558
561
559 description = _(
562 description = _(
560 b'deltas within internal storage will be recalculated to '
563 b'deltas within internal storage will be recalculated to '
561 b'choose an optimal base revision where this was not '
564 b'choose an optimal base revision where this was not '
562 b'already done; the size of the repository may shrink and '
565 b'already done; the size of the repository may shrink and '
563 b'various operations may become faster; the first time '
566 b'various operations may become faster; the first time '
564 b'this optimization is performed could slow down upgrade '
567 b'this optimization is performed could slow down upgrade '
565 b'execution considerably; subsequent invocations should '
568 b'execution considerably; subsequent invocations should '
566 b'not run noticeably slower'
569 b'not run noticeably slower'
567 )
570 )
568
571
569 upgrademessage = _(
572 upgrademessage = _(
570 b'deltas within internal storage will choose a new '
573 b'deltas within internal storage will choose a new '
571 b'base revision if needed'
574 b'base revision if needed'
572 )
575 )
573
576
574
577
575 @register_optimization
578 @register_optimization
576 class redeltamultibase(optimization):
579 class redeltamultibase(optimization):
577 name = b're-delta-multibase'
580 name = b're-delta-multibase'
578
581
579 type = OPTIMISATION
582 type = OPTIMISATION
580
583
581 description = _(
584 description = _(
582 b'deltas within internal storage will be recalculated '
585 b'deltas within internal storage will be recalculated '
583 b'against multiple base revision and the smallest '
586 b'against multiple base revision and the smallest '
584 b'difference will be used; the size of the repository may '
587 b'difference will be used; the size of the repository may '
585 b'shrink significantly when there are many merges; this '
588 b'shrink significantly when there are many merges; this '
586 b'optimization will slow down execution in proportion to '
589 b'optimization will slow down execution in proportion to '
587 b'the number of merges in the repository and the amount '
590 b'the number of merges in the repository and the amount '
588 b'of files in the repository; this slow down should not '
591 b'of files in the repository; this slow down should not '
589 b'be significant unless there are tens of thousands of '
592 b'be significant unless there are tens of thousands of '
590 b'files and thousands of merges'
593 b'files and thousands of merges'
591 )
594 )
592
595
593 upgrademessage = _(
596 upgrademessage = _(
594 b'deltas within internal storage will choose an '
597 b'deltas within internal storage will choose an '
595 b'optimal delta by computing deltas against multiple '
598 b'optimal delta by computing deltas against multiple '
596 b'parents; may slow down execution time '
599 b'parents; may slow down execution time '
597 b'significantly'
600 b'significantly'
598 )
601 )
599
602
600
603
601 @register_optimization
604 @register_optimization
602 class redeltaall(optimization):
605 class redeltaall(optimization):
603 name = b're-delta-all'
606 name = b're-delta-all'
604
607
605 type = OPTIMISATION
608 type = OPTIMISATION
606
609
607 description = _(
610 description = _(
608 b'deltas within internal storage will always be '
611 b'deltas within internal storage will always be '
609 b'recalculated without reusing prior deltas; this will '
612 b'recalculated without reusing prior deltas; this will '
610 b'likely make execution run several times slower; this '
613 b'likely make execution run several times slower; this '
611 b'optimization is typically not needed'
614 b'optimization is typically not needed'
612 )
615 )
613
616
614 upgrademessage = _(
617 upgrademessage = _(
615 b'deltas within internal storage will be fully '
618 b'deltas within internal storage will be fully '
616 b'recomputed; this will likely drastically slow down '
619 b'recomputed; this will likely drastically slow down '
617 b'execution time'
620 b'execution time'
618 )
621 )
619
622
620
623
621 @register_optimization
624 @register_optimization
622 class redeltafulladd(optimization):
625 class redeltafulladd(optimization):
623 name = b're-delta-fulladd'
626 name = b're-delta-fulladd'
624
627
625 type = OPTIMISATION
628 type = OPTIMISATION
626
629
627 description = _(
630 description = _(
628 b'every revision will be re-added as if it was new '
631 b'every revision will be re-added as if it was new '
629 b'content. It will go through the full storage '
632 b'content. It will go through the full storage '
630 b'mechanism giving extensions a chance to process it '
633 b'mechanism giving extensions a chance to process it '
631 b'(eg. lfs). This is similar to "re-delta-all" but even '
634 b'(eg. lfs). This is similar to "re-delta-all" but even '
632 b'slower since more logic is involved.'
635 b'slower since more logic is involved.'
633 )
636 )
634
637
635 upgrademessage = _(
638 upgrademessage = _(
636 b'each revision will be added as new content to the '
639 b'each revision will be added as new content to the '
637 b'internal storage; this will likely drastically slow '
640 b'internal storage; this will likely drastically slow '
638 b'down execution time, but some extensions might need '
641 b'down execution time, but some extensions might need '
639 b'it'
642 b'it'
640 )
643 )
641
644
642
645
643 def findoptimizations(repo):
646 def findoptimizations(repo):
644 """Determine optimisation that could be used during upgrade"""
647 """Determine optimisation that could be used during upgrade"""
645 # These are unconditionally added. There is logic later that figures out
648 # These are unconditionally added. There is logic later that figures out
646 # which ones to apply.
649 # which ones to apply.
647 return list(ALL_OPTIMISATIONS)
650 return list(ALL_OPTIMISATIONS)
648
651
649
652
650 def determine_upgrade_actions(
653 def determine_upgrade_actions(
651 repo, format_upgrades, optimizations, sourcereqs, destreqs
654 repo, format_upgrades, optimizations, sourcereqs, destreqs
652 ):
655 ):
653 """Determine upgrade actions that will be performed.
656 """Determine upgrade actions that will be performed.
654
657
655 Given a list of improvements as returned by ``find_format_upgrades`` and
658 Given a list of improvements as returned by ``find_format_upgrades`` and
656 ``findoptimizations``, determine the list of upgrade actions that
659 ``findoptimizations``, determine the list of upgrade actions that
657 will be performed.
660 will be performed.
658
661
659 The role of this function is to filter improvements if needed, apply
662 The role of this function is to filter improvements if needed, apply
660 recommended optimizations from the improvements list that make sense,
663 recommended optimizations from the improvements list that make sense,
661 etc.
664 etc.
662
665
663 Returns a list of action names.
666 Returns a list of action names.
664 """
667 """
665 newactions = []
668 newactions = []
666
669
667 for d in format_upgrades:
670 for d in format_upgrades:
668 if util.safehasattr(d, '_requirement'):
671 if util.safehasattr(d, '_requirement'):
669 name = d._requirement
672 name = d._requirement
670 else:
673 else:
671 name = None
674 name = None
672
675
673 # If the action is a requirement that doesn't show up in the
676 # If the action is a requirement that doesn't show up in the
674 # destination requirements, prune the action.
677 # destination requirements, prune the action.
675 if name is not None and name not in destreqs:
678 if name is not None and name not in destreqs:
676 continue
679 continue
677
680
678 newactions.append(d)
681 newactions.append(d)
679
682
680 newactions.extend(o for o in sorted(optimizations) if o not in newactions)
683 newactions.extend(o for o in sorted(optimizations) if o not in newactions)
681
684
682 # FUTURE consider adding some optimizations here for certain transitions.
685 # FUTURE consider adding some optimizations here for certain transitions.
683 # e.g. adding generaldelta could schedule parent redeltas.
686 # e.g. adding generaldelta could schedule parent redeltas.
684
687
685 return newactions
688 return newactions
686
689
687
690
688 class UpgradeOperation:
691 class UpgradeOperation:
689 """represent the work to be done during an upgrade"""
692 """represent the work to be done during an upgrade"""
690
693
691 def __init__(
694 def __init__(
692 self,
695 self,
693 ui,
696 ui,
694 new_requirements,
697 new_requirements,
695 current_requirements,
698 current_requirements,
696 upgrade_actions,
699 upgrade_actions,
697 removed_actions,
700 removed_actions,
698 revlogs_to_process,
701 revlogs_to_process,
699 backup_store,
702 backup_store,
700 ):
703 ):
701 self.ui = ui
704 self.ui = ui
702 self.new_requirements = new_requirements
705 self.new_requirements = new_requirements
703 self.current_requirements = current_requirements
706 self.current_requirements = current_requirements
704 # list of upgrade actions the operation will perform
707 # list of upgrade actions the operation will perform
705 self.upgrade_actions = upgrade_actions
708 self.upgrade_actions = upgrade_actions
706 self.removed_actions = removed_actions
709 self.removed_actions = removed_actions
707 self.revlogs_to_process = revlogs_to_process
710 self.revlogs_to_process = revlogs_to_process
708 # requirements which will be added by the operation
711 # requirements which will be added by the operation
709 self._added_requirements = (
712 self._added_requirements = (
710 self.new_requirements - self.current_requirements
713 self.new_requirements - self.current_requirements
711 )
714 )
712 # requirements which will be removed by the operation
715 # requirements which will be removed by the operation
713 self._removed_requirements = (
716 self._removed_requirements = (
714 self.current_requirements - self.new_requirements
717 self.current_requirements - self.new_requirements
715 )
718 )
716 # requirements which will be preserved by the operation
719 # requirements which will be preserved by the operation
717 self._preserved_requirements = (
720 self._preserved_requirements = (
718 self.current_requirements & self.new_requirements
721 self.current_requirements & self.new_requirements
719 )
722 )
720 # optimizations which are not used and it's recommended that they
723 # optimizations which are not used and it's recommended that they
721 # should use them
724 # should use them
722 all_optimizations = findoptimizations(None)
725 all_optimizations = findoptimizations(None)
723 self.unused_optimizations = [
726 self.unused_optimizations = [
724 i for i in all_optimizations if i not in self.upgrade_actions
727 i for i in all_optimizations if i not in self.upgrade_actions
725 ]
728 ]
726
729
727 # delta reuse mode of this upgrade operation
730 # delta reuse mode of this upgrade operation
728 upgrade_actions_names = self.upgrade_actions_names
731 upgrade_actions_names = self.upgrade_actions_names
729 self.delta_reuse_mode = revlog.revlog.DELTAREUSEALWAYS
732 self.delta_reuse_mode = revlog.revlog.DELTAREUSEALWAYS
730 if b're-delta-all' in upgrade_actions_names:
733 if b're-delta-all' in upgrade_actions_names:
731 self.delta_reuse_mode = revlog.revlog.DELTAREUSENEVER
734 self.delta_reuse_mode = revlog.revlog.DELTAREUSENEVER
732 elif b're-delta-parent' in upgrade_actions_names:
735 elif b're-delta-parent' in upgrade_actions_names:
733 self.delta_reuse_mode = revlog.revlog.DELTAREUSESAMEREVS
736 self.delta_reuse_mode = revlog.revlog.DELTAREUSESAMEREVS
734 elif b're-delta-multibase' in upgrade_actions_names:
737 elif b're-delta-multibase' in upgrade_actions_names:
735 self.delta_reuse_mode = revlog.revlog.DELTAREUSESAMEREVS
738 self.delta_reuse_mode = revlog.revlog.DELTAREUSESAMEREVS
736 elif b're-delta-fulladd' in upgrade_actions_names:
739 elif b're-delta-fulladd' in upgrade_actions_names:
737 self.delta_reuse_mode = revlog.revlog.DELTAREUSEFULLADD
740 self.delta_reuse_mode = revlog.revlog.DELTAREUSEFULLADD
738
741
739 # should this operation force re-delta of both parents
742 # should this operation force re-delta of both parents
740 self.force_re_delta_both_parents = (
743 self.force_re_delta_both_parents = (
741 b're-delta-multibase' in upgrade_actions_names
744 b're-delta-multibase' in upgrade_actions_names
742 )
745 )
743
746
744 # should this operation create a backup of the store
747 # should this operation create a backup of the store
745 self.backup_store = backup_store
748 self.backup_store = backup_store
746
749
747 @property
750 @property
748 def upgrade_actions_names(self):
751 def upgrade_actions_names(self):
749 return set([a.name for a in self.upgrade_actions])
752 return set([a.name for a in self.upgrade_actions])
750
753
751 @property
754 @property
752 def requirements_only(self):
755 def requirements_only(self):
753 # does the operation only touches repository requirement
756 # does the operation only touches repository requirement
754 return (
757 return (
755 self.touches_requirements
758 self.touches_requirements
756 and not self.touches_filelogs
759 and not self.touches_filelogs
757 and not self.touches_manifests
760 and not self.touches_manifests
758 and not self.touches_changelog
761 and not self.touches_changelog
759 and not self.touches_dirstate
762 and not self.touches_dirstate
760 )
763 )
761
764
762 @property
765 @property
763 def touches_filelogs(self):
766 def touches_filelogs(self):
764 for a in self.upgrade_actions:
767 for a in self.upgrade_actions:
765 # in optimisations, we re-process the revlogs again
768 # in optimisations, we re-process the revlogs again
766 if a.type == OPTIMISATION:
769 if a.type == OPTIMISATION:
767 return True
770 return True
768 elif a.touches_filelogs:
771 elif a.touches_filelogs:
769 return True
772 return True
770 for a in self.removed_actions:
773 for a in self.removed_actions:
771 if a.touches_filelogs:
774 if a.touches_filelogs:
772 return True
775 return True
773 return False
776 return False
774
777
775 @property
778 @property
776 def touches_manifests(self):
779 def touches_manifests(self):
777 for a in self.upgrade_actions:
780 for a in self.upgrade_actions:
778 # in optimisations, we re-process the revlogs again
781 # in optimisations, we re-process the revlogs again
779 if a.type == OPTIMISATION:
782 if a.type == OPTIMISATION:
780 return True
783 return True
781 elif a.touches_manifests:
784 elif a.touches_manifests:
782 return True
785 return True
783 for a in self.removed_actions:
786 for a in self.removed_actions:
784 if a.touches_manifests:
787 if a.touches_manifests:
785 return True
788 return True
786 return False
789 return False
787
790
788 @property
791 @property
789 def touches_changelog(self):
792 def touches_changelog(self):
790 for a in self.upgrade_actions:
793 for a in self.upgrade_actions:
791 # in optimisations, we re-process the revlogs again
794 # in optimisations, we re-process the revlogs again
792 if a.type == OPTIMISATION:
795 if a.type == OPTIMISATION:
793 return True
796 return True
794 elif a.touches_changelog:
797 elif a.touches_changelog:
795 return True
798 return True
796 for a in self.removed_actions:
799 for a in self.removed_actions:
797 if a.touches_changelog:
800 if a.touches_changelog:
798 return True
801 return True
799 return False
802 return False
800
803
801 @property
804 @property
802 def touches_requirements(self):
805 def touches_requirements(self):
803 for a in self.upgrade_actions:
806 for a in self.upgrade_actions:
804 # optimisations are used to re-process revlogs and does not result
807 # optimisations are used to re-process revlogs and does not result
805 # in a requirement being added or removed
808 # in a requirement being added or removed
806 if a.type == OPTIMISATION:
809 if a.type == OPTIMISATION:
807 pass
810 pass
808 elif a.touches_requirements:
811 elif a.touches_requirements:
809 return True
812 return True
810 for a in self.removed_actions:
813 for a in self.removed_actions:
811 if a.touches_requirements:
814 if a.touches_requirements:
812 return True
815 return True
813
816
814 @property
817 @property
815 def touches_dirstate(self):
818 def touches_dirstate(self):
816 for a in self.upgrade_actions:
819 for a in self.upgrade_actions:
817 # revlog optimisations do not affect the dirstate
820 # revlog optimisations do not affect the dirstate
818 if a.type == OPTIMISATION:
821 if a.type == OPTIMISATION:
819 pass
822 pass
820 elif a.touches_dirstate:
823 elif a.touches_dirstate:
821 return True
824 return True
822 for a in self.removed_actions:
825 for a in self.removed_actions:
823 if a.touches_dirstate:
826 if a.touches_dirstate:
824 return True
827 return True
825
828
826 return False
829 return False
827
830
828 def _write_labeled(self, l, label):
831 def _write_labeled(self, l, label):
829 """
832 """
830 Utility function to aid writing of a list under one label
833 Utility function to aid writing of a list under one label
831 """
834 """
832 first = True
835 first = True
833 for r in sorted(l):
836 for r in sorted(l):
834 if not first:
837 if not first:
835 self.ui.write(b', ')
838 self.ui.write(b', ')
836 self.ui.write(r, label=label)
839 self.ui.write(r, label=label)
837 first = False
840 first = False
838
841
839 def print_requirements(self):
842 def print_requirements(self):
840 self.ui.write(_(b'requirements\n'))
843 self.ui.write(_(b'requirements\n'))
841 self.ui.write(_(b' preserved: '))
844 self.ui.write(_(b' preserved: '))
842 self._write_labeled(
845 self._write_labeled(
843 self._preserved_requirements, "upgrade-repo.requirement.preserved"
846 self._preserved_requirements, "upgrade-repo.requirement.preserved"
844 )
847 )
845 self.ui.write((b'\n'))
848 self.ui.write((b'\n'))
846 if self._removed_requirements:
849 if self._removed_requirements:
847 self.ui.write(_(b' removed: '))
850 self.ui.write(_(b' removed: '))
848 self._write_labeled(
851 self._write_labeled(
849 self._removed_requirements, "upgrade-repo.requirement.removed"
852 self._removed_requirements, "upgrade-repo.requirement.removed"
850 )
853 )
851 self.ui.write((b'\n'))
854 self.ui.write((b'\n'))
852 if self._added_requirements:
855 if self._added_requirements:
853 self.ui.write(_(b' added: '))
856 self.ui.write(_(b' added: '))
854 self._write_labeled(
857 self._write_labeled(
855 self._added_requirements, "upgrade-repo.requirement.added"
858 self._added_requirements, "upgrade-repo.requirement.added"
856 )
859 )
857 self.ui.write((b'\n'))
860 self.ui.write((b'\n'))
858 self.ui.write(b'\n')
861 self.ui.write(b'\n')
859
862
860 def print_optimisations(self):
863 def print_optimisations(self):
861 optimisations = [
864 optimisations = [
862 a for a in self.upgrade_actions if a.type == OPTIMISATION
865 a for a in self.upgrade_actions if a.type == OPTIMISATION
863 ]
866 ]
864 optimisations.sort(key=lambda a: a.name)
867 optimisations.sort(key=lambda a: a.name)
865 if optimisations:
868 if optimisations:
866 self.ui.write(_(b'optimisations: '))
869 self.ui.write(_(b'optimisations: '))
867 self._write_labeled(
870 self._write_labeled(
868 [a.name for a in optimisations],
871 [a.name for a in optimisations],
869 "upgrade-repo.optimisation.performed",
872 "upgrade-repo.optimisation.performed",
870 )
873 )
871 self.ui.write(b'\n\n')
874 self.ui.write(b'\n\n')
872
875
873 def print_upgrade_actions(self):
876 def print_upgrade_actions(self):
874 for a in self.upgrade_actions:
877 for a in self.upgrade_actions:
875 self.ui.status(b'%s\n %s\n\n' % (a.name, a.upgrademessage))
878 self.ui.status(b'%s\n %s\n\n' % (a.name, a.upgrademessage))
876
879
877 def print_affected_revlogs(self):
880 def print_affected_revlogs(self):
878 if not self.revlogs_to_process:
881 if not self.revlogs_to_process:
879 self.ui.write((b'no revlogs to process\n'))
882 self.ui.write((b'no revlogs to process\n'))
880 else:
883 else:
881 self.ui.write((b'processed revlogs:\n'))
884 self.ui.write((b'processed revlogs:\n'))
882 for r in sorted(self.revlogs_to_process):
885 for r in sorted(self.revlogs_to_process):
883 self.ui.write((b' - %s\n' % r))
886 self.ui.write((b' - %s\n' % r))
884 self.ui.write((b'\n'))
887 self.ui.write((b'\n'))
885
888
886 def print_unused_optimizations(self):
889 def print_unused_optimizations(self):
887 for i in self.unused_optimizations:
890 for i in self.unused_optimizations:
888 self.ui.status(_(b'%s\n %s\n\n') % (i.name, i.description))
891 self.ui.status(_(b'%s\n %s\n\n') % (i.name, i.description))
889
892
890 def has_upgrade_action(self, name):
893 def has_upgrade_action(self, name):
891 """Check whether the upgrade operation will perform this action"""
894 """Check whether the upgrade operation will perform this action"""
892 return name in self._upgrade_actions_names
895 return name in self._upgrade_actions_names
893
896
894 def print_post_op_messages(self):
897 def print_post_op_messages(self):
895 """print post upgrade operation warning messages"""
898 """print post upgrade operation warning messages"""
896 for a in self.upgrade_actions:
899 for a in self.upgrade_actions:
897 if a.postupgrademessage is not None:
900 if a.postupgrademessage is not None:
898 self.ui.warn(b'%s\n' % a.postupgrademessage)
901 self.ui.warn(b'%s\n' % a.postupgrademessage)
899 for a in self.removed_actions:
902 for a in self.removed_actions:
900 if a.postdowngrademessage is not None:
903 if a.postdowngrademessage is not None:
901 self.ui.warn(b'%s\n' % a.postdowngrademessage)
904 self.ui.warn(b'%s\n' % a.postdowngrademessage)
902
905
903
906
904 ### Code checking if a repository can got through the upgrade process at all. #
907 ### Code checking if a repository can got through the upgrade process at all. #
905
908
906
909
907 def requiredsourcerequirements(repo):
910 def requiredsourcerequirements(repo):
908 """Obtain requirements required to be present to upgrade a repo.
911 """Obtain requirements required to be present to upgrade a repo.
909
912
910 An upgrade will not be allowed if the repository doesn't have the
913 An upgrade will not be allowed if the repository doesn't have the
911 requirements returned by this function.
914 requirements returned by this function.
912 """
915 """
913 return {
916 return {
914 # Introduced in Mercurial 0.9.2.
917 # Introduced in Mercurial 0.9.2.
915 requirements.STORE_REQUIREMENT,
918 requirements.STORE_REQUIREMENT,
916 }
919 }
917
920
918
921
919 def blocksourcerequirements(repo):
922 def blocksourcerequirements(repo):
920 """Obtain requirements that will prevent an upgrade from occurring.
923 """Obtain requirements that will prevent an upgrade from occurring.
921
924
922 An upgrade cannot be performed if the source repository contains a
925 An upgrade cannot be performed if the source repository contains a
923 requirements in the returned set.
926 requirements in the returned set.
924 """
927 """
925 return {
928 return {
926 # The upgrade code does not yet support these experimental features.
929 # The upgrade code does not yet support these experimental features.
927 # This is an artificial limitation.
930 # This is an artificial limitation.
928 requirements.TREEMANIFEST_REQUIREMENT,
931 requirements.TREEMANIFEST_REQUIREMENT,
929 # This was a precursor to generaldelta and was never enabled by default.
932 # This was a precursor to generaldelta and was never enabled by default.
930 # It should (hopefully) not exist in the wild.
933 # It should (hopefully) not exist in the wild.
931 b'parentdelta',
934 b'parentdelta',
932 }
935 }
933
936
934
937
935 def check_revlog_version(reqs):
938 def check_revlog_version(reqs):
936 """Check that the requirements contain at least one Revlog version"""
939 """Check that the requirements contain at least one Revlog version"""
937 all_revlogs = {
940 all_revlogs = {
938 requirements.REVLOGV1_REQUIREMENT,
941 requirements.REVLOGV1_REQUIREMENT,
939 requirements.REVLOGV2_REQUIREMENT,
942 requirements.REVLOGV2_REQUIREMENT,
940 }
943 }
941 if not all_revlogs.intersection(reqs):
944 if not all_revlogs.intersection(reqs):
942 msg = _(b'cannot upgrade repository; missing a revlog version')
945 msg = _(b'cannot upgrade repository; missing a revlog version')
943 raise error.Abort(msg)
946 raise error.Abort(msg)
944
947
945
948
946 def check_source_requirements(repo):
949 def check_source_requirements(repo):
947 """Ensure that no existing requirements prevent the repository upgrade"""
950 """Ensure that no existing requirements prevent the repository upgrade"""
948
951
949 check_revlog_version(repo.requirements)
952 check_revlog_version(repo.requirements)
950 required = requiredsourcerequirements(repo)
953 required = requiredsourcerequirements(repo)
951 missingreqs = required - repo.requirements
954 missingreqs = required - repo.requirements
952 if missingreqs:
955 if missingreqs:
953 msg = _(b'cannot upgrade repository; requirement missing: %s')
956 msg = _(b'cannot upgrade repository; requirement missing: %s')
954 missingreqs = b', '.join(sorted(missingreqs))
957 missingreqs = b', '.join(sorted(missingreqs))
955 raise error.Abort(msg % missingreqs)
958 raise error.Abort(msg % missingreqs)
956
959
957 blocking = blocksourcerequirements(repo)
960 blocking = blocksourcerequirements(repo)
958 blockingreqs = blocking & repo.requirements
961 blockingreqs = blocking & repo.requirements
959 if blockingreqs:
962 if blockingreqs:
960 m = _(b'cannot upgrade repository; unsupported source requirement: %s')
963 m = _(b'cannot upgrade repository; unsupported source requirement: %s')
961 blockingreqs = b', '.join(sorted(blockingreqs))
964 blockingreqs = b', '.join(sorted(blockingreqs))
962 raise error.Abort(m % blockingreqs)
965 raise error.Abort(m % blockingreqs)
963 # Upgrade should operate on the actual store, not the shared link.
966 # Upgrade should operate on the actual store, not the shared link.
964
967
965 bad_share = (
968 bad_share = (
966 requirements.SHARED_REQUIREMENT in repo.requirements
969 requirements.SHARED_REQUIREMENT in repo.requirements
967 and requirements.SHARESAFE_REQUIREMENT not in repo.requirements
970 and requirements.SHARESAFE_REQUIREMENT not in repo.requirements
968 )
971 )
969 if bad_share:
972 if bad_share:
970 m = _(b'cannot upgrade repository; share repository without share-safe')
973 m = _(b'cannot upgrade repository; share repository without share-safe')
971 h = _(b'check :hg:`help config.format.use-share-safe`')
974 h = _(b'check :hg:`help config.format.use-share-safe`')
972 raise error.Abort(m, hint=h)
975 raise error.Abort(m, hint=h)
973
976
974
977
975 ### Verify the validity of the planned requirement changes ####################
978 ### Verify the validity of the planned requirement changes ####################
976
979
977
980
978 def supportremovedrequirements(repo):
981 def supportremovedrequirements(repo):
979 """Obtain requirements that can be removed during an upgrade.
982 """Obtain requirements that can be removed during an upgrade.
980
983
981 If an upgrade were to create a repository that dropped a requirement,
984 If an upgrade were to create a repository that dropped a requirement,
982 the dropped requirement must appear in the returned set for the upgrade
985 the dropped requirement must appear in the returned set for the upgrade
983 to be allowed.
986 to be allowed.
984 """
987 """
985 supported = {
988 supported = {
986 requirements.SPARSEREVLOG_REQUIREMENT,
989 requirements.SPARSEREVLOG_REQUIREMENT,
987 requirements.COPIESSDC_REQUIREMENT,
990 requirements.COPIESSDC_REQUIREMENT,
988 requirements.NODEMAP_REQUIREMENT,
991 requirements.NODEMAP_REQUIREMENT,
989 requirements.SHARESAFE_REQUIREMENT,
992 requirements.SHARESAFE_REQUIREMENT,
990 requirements.REVLOGV2_REQUIREMENT,
993 requirements.REVLOGV2_REQUIREMENT,
991 requirements.CHANGELOGV2_REQUIREMENT,
994 requirements.CHANGELOGV2_REQUIREMENT,
992 requirements.REVLOGV1_REQUIREMENT,
995 requirements.REVLOGV1_REQUIREMENT,
993 requirements.DIRSTATE_TRACKED_HINT_V1,
996 requirements.DIRSTATE_TRACKED_HINT_V1,
994 requirements.DIRSTATE_V2_REQUIREMENT,
997 requirements.DIRSTATE_V2_REQUIREMENT,
995 }
998 }
996 for name in compression.compengines:
999 for name in compression.compengines:
997 engine = compression.compengines[name]
1000 engine = compression.compengines[name]
998 if engine.available() and engine.revlogheader():
1001 if engine.available() and engine.revlogheader():
999 supported.add(b'exp-compression-%s' % name)
1002 supported.add(b'exp-compression-%s' % name)
1000 if engine.name() == b'zstd':
1003 if engine.name() == b'zstd':
1001 supported.add(b'revlog-compression-zstd')
1004 supported.add(b'revlog-compression-zstd')
1002 return supported
1005 return supported
1003
1006
1004
1007
1005 def supporteddestrequirements(repo):
1008 def supporteddestrequirements(repo):
1006 """Obtain requirements that upgrade supports in the destination.
1009 """Obtain requirements that upgrade supports in the destination.
1007
1010
1008 If the result of the upgrade would have requirements not in this set,
1011 If the result of the upgrade would have requirements not in this set,
1009 the upgrade is disallowed.
1012 the upgrade is disallowed.
1010
1013
1011 Extensions should monkeypatch this to add their custom requirements.
1014 Extensions should monkeypatch this to add their custom requirements.
1012 """
1015 """
1013 supported = {
1016 supported = {
1014 requirements.CHANGELOGV2_REQUIREMENT,
1017 requirements.CHANGELOGV2_REQUIREMENT,
1015 requirements.COPIESSDC_REQUIREMENT,
1018 requirements.COPIESSDC_REQUIREMENT,
1016 requirements.DIRSTATE_TRACKED_HINT_V1,
1019 requirements.DIRSTATE_TRACKED_HINT_V1,
1017 requirements.DIRSTATE_V2_REQUIREMENT,
1020 requirements.DIRSTATE_V2_REQUIREMENT,
1018 requirements.DOTENCODE_REQUIREMENT,
1021 requirements.DOTENCODE_REQUIREMENT,
1019 requirements.FNCACHE_REQUIREMENT,
1022 requirements.FNCACHE_REQUIREMENT,
1020 requirements.GENERALDELTA_REQUIREMENT,
1023 requirements.GENERALDELTA_REQUIREMENT,
1021 requirements.NODEMAP_REQUIREMENT,
1024 requirements.NODEMAP_REQUIREMENT,
1022 requirements.REVLOGV1_REQUIREMENT, # allowed in case of downgrade
1025 requirements.REVLOGV1_REQUIREMENT, # allowed in case of downgrade
1023 requirements.REVLOGV2_REQUIREMENT,
1026 requirements.REVLOGV2_REQUIREMENT,
1024 requirements.SHARED_REQUIREMENT,
1027 requirements.SHARED_REQUIREMENT,
1025 requirements.SHARESAFE_REQUIREMENT,
1028 requirements.SHARESAFE_REQUIREMENT,
1026 requirements.SPARSEREVLOG_REQUIREMENT,
1029 requirements.SPARSEREVLOG_REQUIREMENT,
1027 requirements.STORE_REQUIREMENT,
1030 requirements.STORE_REQUIREMENT,
1028 requirements.NARROW_REQUIREMENT,
1031 requirements.NARROW_REQUIREMENT,
1029 }
1032 }
1030 for name in compression.compengines:
1033 for name in compression.compengines:
1031 engine = compression.compengines[name]
1034 engine = compression.compengines[name]
1032 if engine.available() and engine.revlogheader():
1035 if engine.available() and engine.revlogheader():
1033 supported.add(b'exp-compression-%s' % name)
1036 supported.add(b'exp-compression-%s' % name)
1034 if engine.name() == b'zstd':
1037 if engine.name() == b'zstd':
1035 supported.add(b'revlog-compression-zstd')
1038 supported.add(b'revlog-compression-zstd')
1036 return supported
1039 return supported
1037
1040
1038
1041
1039 def allowednewrequirements(repo):
1042 def allowednewrequirements(repo):
1040 """Obtain requirements that can be added to a repository during upgrade.
1043 """Obtain requirements that can be added to a repository during upgrade.
1041
1044
1042 This is used to disallow proposed requirements from being added when
1045 This is used to disallow proposed requirements from being added when
1043 they weren't present before.
1046 they weren't present before.
1044
1047
1045 We use a list of allowed requirement additions instead of a list of known
1048 We use a list of allowed requirement additions instead of a list of known
1046 bad additions because the whitelist approach is safer and will prevent
1049 bad additions because the whitelist approach is safer and will prevent
1047 future, unknown requirements from accidentally being added.
1050 future, unknown requirements from accidentally being added.
1048 """
1051 """
1049 supported = {
1052 supported = {
1050 requirements.DOTENCODE_REQUIREMENT,
1053 requirements.DOTENCODE_REQUIREMENT,
1051 requirements.FNCACHE_REQUIREMENT,
1054 requirements.FNCACHE_REQUIREMENT,
1052 requirements.GENERALDELTA_REQUIREMENT,
1055 requirements.GENERALDELTA_REQUIREMENT,
1053 requirements.SPARSEREVLOG_REQUIREMENT,
1056 requirements.SPARSEREVLOG_REQUIREMENT,
1054 requirements.COPIESSDC_REQUIREMENT,
1057 requirements.COPIESSDC_REQUIREMENT,
1055 requirements.NODEMAP_REQUIREMENT,
1058 requirements.NODEMAP_REQUIREMENT,
1056 requirements.SHARESAFE_REQUIREMENT,
1059 requirements.SHARESAFE_REQUIREMENT,
1057 requirements.REVLOGV1_REQUIREMENT,
1060 requirements.REVLOGV1_REQUIREMENT,
1058 requirements.REVLOGV2_REQUIREMENT,
1061 requirements.REVLOGV2_REQUIREMENT,
1059 requirements.CHANGELOGV2_REQUIREMENT,
1062 requirements.CHANGELOGV2_REQUIREMENT,
1060 requirements.DIRSTATE_TRACKED_HINT_V1,
1063 requirements.DIRSTATE_TRACKED_HINT_V1,
1061 requirements.DIRSTATE_V2_REQUIREMENT,
1064 requirements.DIRSTATE_V2_REQUIREMENT,
1062 }
1065 }
1063 for name in compression.compengines:
1066 for name in compression.compengines:
1064 engine = compression.compengines[name]
1067 engine = compression.compengines[name]
1065 if engine.available() and engine.revlogheader():
1068 if engine.available() and engine.revlogheader():
1066 supported.add(b'exp-compression-%s' % name)
1069 supported.add(b'exp-compression-%s' % name)
1067 if engine.name() == b'zstd':
1070 if engine.name() == b'zstd':
1068 supported.add(b'revlog-compression-zstd')
1071 supported.add(b'revlog-compression-zstd')
1069 return supported
1072 return supported
1070
1073
1071
1074
1072 def check_requirements_changes(repo, new_reqs):
1075 def check_requirements_changes(repo, new_reqs):
1073 old_reqs = repo.requirements
1076 old_reqs = repo.requirements
1074 check_revlog_version(repo.requirements)
1077 check_revlog_version(repo.requirements)
1075 support_removal = supportremovedrequirements(repo)
1078 support_removal = supportremovedrequirements(repo)
1076 no_remove_reqs = old_reqs - new_reqs - support_removal
1079 no_remove_reqs = old_reqs - new_reqs - support_removal
1077 if no_remove_reqs:
1080 if no_remove_reqs:
1078 msg = _(b'cannot upgrade repository; requirement would be removed: %s')
1081 msg = _(b'cannot upgrade repository; requirement would be removed: %s')
1079 no_remove_reqs = b', '.join(sorted(no_remove_reqs))
1082 no_remove_reqs = b', '.join(sorted(no_remove_reqs))
1080 raise error.Abort(msg % no_remove_reqs)
1083 raise error.Abort(msg % no_remove_reqs)
1081
1084
1082 support_addition = allowednewrequirements(repo)
1085 support_addition = allowednewrequirements(repo)
1083 no_add_reqs = new_reqs - old_reqs - support_addition
1086 no_add_reqs = new_reqs - old_reqs - support_addition
1084 if no_add_reqs:
1087 if no_add_reqs:
1085 m = _(b'cannot upgrade repository; do not support adding requirement: ')
1088 m = _(b'cannot upgrade repository; do not support adding requirement: ')
1086 no_add_reqs = b', '.join(sorted(no_add_reqs))
1089 no_add_reqs = b', '.join(sorted(no_add_reqs))
1087 raise error.Abort(m + no_add_reqs)
1090 raise error.Abort(m + no_add_reqs)
1088
1091
1089 supported = supporteddestrequirements(repo)
1092 supported = supporteddestrequirements(repo)
1090 unsupported_reqs = new_reqs - supported
1093 unsupported_reqs = new_reqs - supported
1091 if unsupported_reqs:
1094 if unsupported_reqs:
1092 msg = _(
1095 msg = _(
1093 b'cannot upgrade repository; do not support destination '
1096 b'cannot upgrade repository; do not support destination '
1094 b'requirement: %s'
1097 b'requirement: %s'
1095 )
1098 )
1096 unsupported_reqs = b', '.join(sorted(unsupported_reqs))
1099 unsupported_reqs = b', '.join(sorted(unsupported_reqs))
1097 raise error.Abort(msg % unsupported_reqs)
1100 raise error.Abort(msg % unsupported_reqs)
General Comments 0
You need to be logged in to leave comments. Login now