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