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