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