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