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