##// END OF EJS Templates
upgrade: gather code about requirement checking together...
marmoute -
r46667:08802795 default
parent child Browse files
Show More
@@ -1,719 +1,719 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 util,
15 util,
16 )
16 )
17
17
18 from ..utils import compression
18 from ..utils import compression
19
19
20 # list of requirements that request a clone of all revlog if added/removed
20 # list of requirements that request a clone of all revlog if added/removed
21 RECLONES_REQUIREMENTS = {
21 RECLONES_REQUIREMENTS = {
22 b'generaldelta',
22 b'generaldelta',
23 requirements.SPARSEREVLOG_REQUIREMENT,
23 requirements.SPARSEREVLOG_REQUIREMENT,
24 }
24 }
25
25
26
26
27 def supportremovedrequirements(repo):
28 """Obtain requirements that can be removed during an upgrade.
29
30 If an upgrade were to create a repository that dropped a requirement,
31 the dropped requirement must appear in the returned set for the upgrade
32 to be allowed.
33 """
34 supported = {
35 requirements.SPARSEREVLOG_REQUIREMENT,
36 requirements.SIDEDATA_REQUIREMENT,
37 requirements.COPIESSDC_REQUIREMENT,
38 requirements.NODEMAP_REQUIREMENT,
39 requirements.SHARESAFE_REQUIREMENT,
40 }
41 for name in compression.compengines:
42 engine = compression.compengines[name]
43 if engine.available() and engine.revlogheader():
44 supported.add(b'exp-compression-%s' % name)
45 if engine.name() == b'zstd':
46 supported.add(b'revlog-compression-zstd')
47 return supported
48
49
50 def supporteddestrequirements(repo):
51 """Obtain requirements that upgrade supports in the destination.
52
53 If the result of the upgrade would create requirements not in this set,
54 the upgrade is disallowed.
55
56 Extensions should monkeypatch this to add their custom requirements.
57 """
58 supported = {
59 b'dotencode',
60 b'fncache',
61 b'generaldelta',
62 b'revlogv1',
63 b'store',
64 requirements.SPARSEREVLOG_REQUIREMENT,
65 requirements.SIDEDATA_REQUIREMENT,
66 requirements.COPIESSDC_REQUIREMENT,
67 requirements.NODEMAP_REQUIREMENT,
68 requirements.SHARESAFE_REQUIREMENT,
69 }
70 for name in compression.compengines:
71 engine = compression.compengines[name]
72 if engine.available() and engine.revlogheader():
73 supported.add(b'exp-compression-%s' % name)
74 if engine.name() == b'zstd':
75 supported.add(b'revlog-compression-zstd')
76 return supported
77
78
79 def allowednewrequirements(repo):
80 """Obtain requirements that can be added to a repository during upgrade.
81
82 This is used to disallow proposed requirements from being added when
83 they weren't present before.
84
85 We use a list of allowed requirement additions instead of a list of known
86 bad additions because the whitelist approach is safer and will prevent
87 future, unknown requirements from accidentally being added.
88 """
89 supported = {
90 b'dotencode',
91 b'fncache',
92 b'generaldelta',
93 requirements.SPARSEREVLOG_REQUIREMENT,
94 requirements.SIDEDATA_REQUIREMENT,
95 requirements.COPIESSDC_REQUIREMENT,
96 requirements.NODEMAP_REQUIREMENT,
97 requirements.SHARESAFE_REQUIREMENT,
98 }
99 for name in compression.compengines:
100 engine = compression.compengines[name]
101 if engine.available() and engine.revlogheader():
102 supported.add(b'exp-compression-%s' % name)
103 if engine.name() == b'zstd':
104 supported.add(b'revlog-compression-zstd')
105 return supported
106
107
108 def preservedrequirements(repo):
27 def preservedrequirements(repo):
109 return set()
28 return set()
110
29
111
30
112 DEFICIENCY = b'deficiency'
31 DEFICIENCY = b'deficiency'
113 OPTIMISATION = b'optimization'
32 OPTIMISATION = b'optimization'
114
33
115
34
116 class improvement(object):
35 class improvement(object):
117 """Represents an improvement that can be made as part of an upgrade.
36 """Represents an improvement that can be made as part of an upgrade.
118
37
119 The following attributes are defined on each instance:
38 The following attributes are defined on each instance:
120
39
121 name
40 name
122 Machine-readable string uniquely identifying this improvement. It
41 Machine-readable string uniquely identifying this improvement. It
123 will be mapped to an action later in the upgrade process.
42 will be mapped to an action later in the upgrade process.
124
43
125 type
44 type
126 Either ``DEFICIENCY`` or ``OPTIMISATION``. A deficiency is an obvious
45 Either ``DEFICIENCY`` or ``OPTIMISATION``. A deficiency is an obvious
127 problem. An optimization is an action (sometimes optional) that
46 problem. An optimization is an action (sometimes optional) that
128 can be taken to further improve the state of the repository.
47 can be taken to further improve the state of the repository.
129
48
130 description
49 description
131 Message intended for humans explaining the improvement in more detail,
50 Message intended for humans explaining the improvement in more detail,
132 including the implications of it. For ``DEFICIENCY`` types, should be
51 including the implications of it. For ``DEFICIENCY`` types, should be
133 worded in the present tense. For ``OPTIMISATION`` types, should be
52 worded in the present tense. For ``OPTIMISATION`` types, should be
134 worded in the future tense.
53 worded in the future tense.
135
54
136 upgrademessage
55 upgrademessage
137 Message intended for humans explaining what an upgrade addressing this
56 Message intended for humans explaining what an upgrade addressing this
138 issue will do. Should be worded in the future tense.
57 issue will do. Should be worded in the future tense.
139 """
58 """
140
59
141 def __init__(self, name, type, description, upgrademessage):
60 def __init__(self, name, type, description, upgrademessage):
142 self.name = name
61 self.name = name
143 self.type = type
62 self.type = type
144 self.description = description
63 self.description = description
145 self.upgrademessage = upgrademessage
64 self.upgrademessage = upgrademessage
146
65
147 def __eq__(self, other):
66 def __eq__(self, other):
148 if not isinstance(other, improvement):
67 if not isinstance(other, improvement):
149 # This is what python tell use to do
68 # This is what python tell use to do
150 return NotImplemented
69 return NotImplemented
151 return self.name == other.name
70 return self.name == other.name
152
71
153 def __ne__(self, other):
72 def __ne__(self, other):
154 return not (self == other)
73 return not (self == other)
155
74
156 def __hash__(self):
75 def __hash__(self):
157 return hash(self.name)
76 return hash(self.name)
158
77
159
78
160 allformatvariant = []
79 allformatvariant = []
161
80
162
81
163 def registerformatvariant(cls):
82 def registerformatvariant(cls):
164 allformatvariant.append(cls)
83 allformatvariant.append(cls)
165 return cls
84 return cls
166
85
167
86
168 class formatvariant(improvement):
87 class formatvariant(improvement):
169 """an improvement subclass dedicated to repository format"""
88 """an improvement subclass dedicated to repository format"""
170
89
171 type = DEFICIENCY
90 type = DEFICIENCY
172 ### The following attributes should be defined for each class:
91 ### The following attributes should be defined for each class:
173
92
174 # machine-readable string uniquely identifying this improvement. it will be
93 # machine-readable string uniquely identifying this improvement. it will be
175 # mapped to an action later in the upgrade process.
94 # mapped to an action later in the upgrade process.
176 name = None
95 name = None
177
96
178 # message intended for humans explaining the improvement in more detail,
97 # message intended for humans explaining the improvement in more detail,
179 # including the implications of it ``DEFICIENCY`` types, should be worded
98 # including the implications of it ``DEFICIENCY`` types, should be worded
180 # in the present tense.
99 # in the present tense.
181 description = None
100 description = None
182
101
183 # message intended for humans explaining what an upgrade addressing this
102 # message intended for humans explaining what an upgrade addressing this
184 # issue will do. should be worded in the future tense.
103 # issue will do. should be worded in the future tense.
185 upgrademessage = None
104 upgrademessage = None
186
105
187 # value of current Mercurial default for new repository
106 # value of current Mercurial default for new repository
188 default = None
107 default = None
189
108
190 def __init__(self):
109 def __init__(self):
191 raise NotImplementedError()
110 raise NotImplementedError()
192
111
193 @staticmethod
112 @staticmethod
194 def fromrepo(repo):
113 def fromrepo(repo):
195 """current value of the variant in the repository"""
114 """current value of the variant in the repository"""
196 raise NotImplementedError()
115 raise NotImplementedError()
197
116
198 @staticmethod
117 @staticmethod
199 def fromconfig(repo):
118 def fromconfig(repo):
200 """current value of the variant in the configuration"""
119 """current value of the variant in the configuration"""
201 raise NotImplementedError()
120 raise NotImplementedError()
202
121
203
122
204 class requirementformatvariant(formatvariant):
123 class requirementformatvariant(formatvariant):
205 """formatvariant based on a 'requirement' name.
124 """formatvariant based on a 'requirement' name.
206
125
207 Many format variant are controlled by a 'requirement'. We define a small
126 Many format variant are controlled by a 'requirement'. We define a small
208 subclass to factor the code.
127 subclass to factor the code.
209 """
128 """
210
129
211 # the requirement that control this format variant
130 # the requirement that control this format variant
212 _requirement = None
131 _requirement = None
213
132
214 @staticmethod
133 @staticmethod
215 def _newreporequirements(ui):
134 def _newreporequirements(ui):
216 return localrepo.newreporequirements(
135 return localrepo.newreporequirements(
217 ui, localrepo.defaultcreateopts(ui)
136 ui, localrepo.defaultcreateopts(ui)
218 )
137 )
219
138
220 @classmethod
139 @classmethod
221 def fromrepo(cls, repo):
140 def fromrepo(cls, repo):
222 assert cls._requirement is not None
141 assert cls._requirement is not None
223 return cls._requirement in repo.requirements
142 return cls._requirement in repo.requirements
224
143
225 @classmethod
144 @classmethod
226 def fromconfig(cls, repo):
145 def fromconfig(cls, repo):
227 assert cls._requirement is not None
146 assert cls._requirement is not None
228 return cls._requirement in cls._newreporequirements(repo.ui)
147 return cls._requirement in cls._newreporequirements(repo.ui)
229
148
230
149
231 @registerformatvariant
150 @registerformatvariant
232 class fncache(requirementformatvariant):
151 class fncache(requirementformatvariant):
233 name = b'fncache'
152 name = b'fncache'
234
153
235 _requirement = b'fncache'
154 _requirement = b'fncache'
236
155
237 default = True
156 default = True
238
157
239 description = _(
158 description = _(
240 b'long and reserved filenames may not work correctly; '
159 b'long and reserved filenames may not work correctly; '
241 b'repository performance is sub-optimal'
160 b'repository performance is sub-optimal'
242 )
161 )
243
162
244 upgrademessage = _(
163 upgrademessage = _(
245 b'repository will be more resilient to storing '
164 b'repository will be more resilient to storing '
246 b'certain paths and performance of certain '
165 b'certain paths and performance of certain '
247 b'operations should be improved'
166 b'operations should be improved'
248 )
167 )
249
168
250
169
251 @registerformatvariant
170 @registerformatvariant
252 class dotencode(requirementformatvariant):
171 class dotencode(requirementformatvariant):
253 name = b'dotencode'
172 name = b'dotencode'
254
173
255 _requirement = b'dotencode'
174 _requirement = b'dotencode'
256
175
257 default = True
176 default = True
258
177
259 description = _(
178 description = _(
260 b'storage of filenames beginning with a period or '
179 b'storage of filenames beginning with a period or '
261 b'space may not work correctly'
180 b'space may not work correctly'
262 )
181 )
263
182
264 upgrademessage = _(
183 upgrademessage = _(
265 b'repository will be better able to store files '
184 b'repository will be better able to store files '
266 b'beginning with a space or period'
185 b'beginning with a space or period'
267 )
186 )
268
187
269
188
270 @registerformatvariant
189 @registerformatvariant
271 class generaldelta(requirementformatvariant):
190 class generaldelta(requirementformatvariant):
272 name = b'generaldelta'
191 name = b'generaldelta'
273
192
274 _requirement = b'generaldelta'
193 _requirement = b'generaldelta'
275
194
276 default = True
195 default = True
277
196
278 description = _(
197 description = _(
279 b'deltas within internal storage are unable to '
198 b'deltas within internal storage are unable to '
280 b'choose optimal revisions; repository is larger and '
199 b'choose optimal revisions; repository is larger and '
281 b'slower than it could be; interaction with other '
200 b'slower than it could be; interaction with other '
282 b'repositories may require extra network and CPU '
201 b'repositories may require extra network and CPU '
283 b'resources, making "hg push" and "hg pull" slower'
202 b'resources, making "hg push" and "hg pull" slower'
284 )
203 )
285
204
286 upgrademessage = _(
205 upgrademessage = _(
287 b'repository storage will be able to create '
206 b'repository storage will be able to create '
288 b'optimal deltas; new repository data will be '
207 b'optimal deltas; new repository data will be '
289 b'smaller and read times should decrease; '
208 b'smaller and read times should decrease; '
290 b'interacting with other repositories using this '
209 b'interacting with other repositories using this '
291 b'storage model should require less network and '
210 b'storage model should require less network and '
292 b'CPU resources, making "hg push" and "hg pull" '
211 b'CPU resources, making "hg push" and "hg pull" '
293 b'faster'
212 b'faster'
294 )
213 )
295
214
296
215
297 @registerformatvariant
216 @registerformatvariant
298 class sharesafe(requirementformatvariant):
217 class sharesafe(requirementformatvariant):
299 name = b'exp-sharesafe'
218 name = b'exp-sharesafe'
300 _requirement = requirements.SHARESAFE_REQUIREMENT
219 _requirement = requirements.SHARESAFE_REQUIREMENT
301
220
302 default = False
221 default = False
303
222
304 description = _(
223 description = _(
305 b'old shared repositories do not share source repository '
224 b'old shared repositories do not share source repository '
306 b'requirements and config. This leads to various problems '
225 b'requirements and config. This leads to various problems '
307 b'when the source repository format is upgraded or some new '
226 b'when the source repository format is upgraded or some new '
308 b'extensions are enabled.'
227 b'extensions are enabled.'
309 )
228 )
310
229
311 upgrademessage = _(
230 upgrademessage = _(
312 b'Upgrades a repository to share-safe format so that future '
231 b'Upgrades a repository to share-safe format so that future '
313 b'shares of this repository share its requirements and configs.'
232 b'shares of this repository share its requirements and configs.'
314 )
233 )
315
234
316
235
317 @registerformatvariant
236 @registerformatvariant
318 class sparserevlog(requirementformatvariant):
237 class sparserevlog(requirementformatvariant):
319 name = b'sparserevlog'
238 name = b'sparserevlog'
320
239
321 _requirement = requirements.SPARSEREVLOG_REQUIREMENT
240 _requirement = requirements.SPARSEREVLOG_REQUIREMENT
322
241
323 default = True
242 default = True
324
243
325 description = _(
244 description = _(
326 b'in order to limit disk reading and memory usage on older '
245 b'in order to limit disk reading and memory usage on older '
327 b'version, the span of a delta chain from its root to its '
246 b'version, the span of a delta chain from its root to its '
328 b'end is limited, whatever the relevant data in this span. '
247 b'end is limited, whatever the relevant data in this span. '
329 b'This can severly limit Mercurial ability to build good '
248 b'This can severly limit Mercurial ability to build good '
330 b'chain of delta resulting is much more storage space being '
249 b'chain of delta resulting is much more storage space being '
331 b'taken and limit reusability of on disk delta during '
250 b'taken and limit reusability of on disk delta during '
332 b'exchange.'
251 b'exchange.'
333 )
252 )
334
253
335 upgrademessage = _(
254 upgrademessage = _(
336 b'Revlog supports delta chain with more unused data '
255 b'Revlog supports delta chain with more unused data '
337 b'between payload. These gaps will be skipped at read '
256 b'between payload. These gaps will be skipped at read '
338 b'time. This allows for better delta chains, making a '
257 b'time. This allows for better delta chains, making a '
339 b'better compression and faster exchange with server.'
258 b'better compression and faster exchange with server.'
340 )
259 )
341
260
342
261
343 @registerformatvariant
262 @registerformatvariant
344 class sidedata(requirementformatvariant):
263 class sidedata(requirementformatvariant):
345 name = b'sidedata'
264 name = b'sidedata'
346
265
347 _requirement = requirements.SIDEDATA_REQUIREMENT
266 _requirement = requirements.SIDEDATA_REQUIREMENT
348
267
349 default = False
268 default = False
350
269
351 description = _(
270 description = _(
352 b'Allows storage of extra data alongside a revision, '
271 b'Allows storage of extra data alongside a revision, '
353 b'unlocking various caching options.'
272 b'unlocking various caching options.'
354 )
273 )
355
274
356 upgrademessage = _(b'Allows storage of extra data alongside a revision.')
275 upgrademessage = _(b'Allows storage of extra data alongside a revision.')
357
276
358
277
359 @registerformatvariant
278 @registerformatvariant
360 class persistentnodemap(requirementformatvariant):
279 class persistentnodemap(requirementformatvariant):
361 name = b'persistent-nodemap'
280 name = b'persistent-nodemap'
362
281
363 _requirement = requirements.NODEMAP_REQUIREMENT
282 _requirement = requirements.NODEMAP_REQUIREMENT
364
283
365 default = False
284 default = False
366
285
367 description = _(
286 description = _(
368 b'persist the node -> rev mapping on disk to speedup lookup'
287 b'persist the node -> rev mapping on disk to speedup lookup'
369 )
288 )
370
289
371 upgrademessage = _(b'Speedup revision lookup by node id.')
290 upgrademessage = _(b'Speedup revision lookup by node id.')
372
291
373
292
374 @registerformatvariant
293 @registerformatvariant
375 class copiessdc(requirementformatvariant):
294 class copiessdc(requirementformatvariant):
376 name = b'copies-sdc'
295 name = b'copies-sdc'
377
296
378 _requirement = requirements.COPIESSDC_REQUIREMENT
297 _requirement = requirements.COPIESSDC_REQUIREMENT
379
298
380 default = False
299 default = False
381
300
382 description = _(b'Stores copies information alongside changesets.')
301 description = _(b'Stores copies information alongside changesets.')
383
302
384 upgrademessage = _(
303 upgrademessage = _(
385 b'Allows to use more efficient algorithm to deal with ' b'copy tracing.'
304 b'Allows to use more efficient algorithm to deal with ' b'copy tracing.'
386 )
305 )
387
306
388
307
389 @registerformatvariant
308 @registerformatvariant
390 class removecldeltachain(formatvariant):
309 class removecldeltachain(formatvariant):
391 name = b'plain-cl-delta'
310 name = b'plain-cl-delta'
392
311
393 default = True
312 default = True
394
313
395 description = _(
314 description = _(
396 b'changelog storage is using deltas instead of '
315 b'changelog storage is using deltas instead of '
397 b'raw entries; changelog reading and any '
316 b'raw entries; changelog reading and any '
398 b'operation relying on changelog data are slower '
317 b'operation relying on changelog data are slower '
399 b'than they could be'
318 b'than they could be'
400 )
319 )
401
320
402 upgrademessage = _(
321 upgrademessage = _(
403 b'changelog storage will be reformated to '
322 b'changelog storage will be reformated to '
404 b'store raw entries; changelog reading will be '
323 b'store raw entries; changelog reading will be '
405 b'faster; changelog size may be reduced'
324 b'faster; changelog size may be reduced'
406 )
325 )
407
326
408 @staticmethod
327 @staticmethod
409 def fromrepo(repo):
328 def fromrepo(repo):
410 # Mercurial 4.0 changed changelogs to not use delta chains. Search for
329 # Mercurial 4.0 changed changelogs to not use delta chains. Search for
411 # changelogs with deltas.
330 # changelogs with deltas.
412 cl = repo.changelog
331 cl = repo.changelog
413 chainbase = cl.chainbase
332 chainbase = cl.chainbase
414 return all(rev == chainbase(rev) for rev in cl)
333 return all(rev == chainbase(rev) for rev in cl)
415
334
416 @staticmethod
335 @staticmethod
417 def fromconfig(repo):
336 def fromconfig(repo):
418 return True
337 return True
419
338
420
339
421 @registerformatvariant
340 @registerformatvariant
422 class compressionengine(formatvariant):
341 class compressionengine(formatvariant):
423 name = b'compression'
342 name = b'compression'
424 default = b'zlib'
343 default = b'zlib'
425
344
426 description = _(
345 description = _(
427 b'Compresion algorithm used to compress data. '
346 b'Compresion algorithm used to compress data. '
428 b'Some engine are faster than other'
347 b'Some engine are faster than other'
429 )
348 )
430
349
431 upgrademessage = _(
350 upgrademessage = _(
432 b'revlog content will be recompressed with the new algorithm.'
351 b'revlog content will be recompressed with the new algorithm.'
433 )
352 )
434
353
435 @classmethod
354 @classmethod
436 def fromrepo(cls, repo):
355 def fromrepo(cls, repo):
437 # we allow multiple compression engine requirement to co-exist because
356 # we allow multiple compression engine requirement to co-exist because
438 # strickly speaking, revlog seems to support mixed compression style.
357 # strickly speaking, revlog seems to support mixed compression style.
439 #
358 #
440 # The compression used for new entries will be "the last one"
359 # The compression used for new entries will be "the last one"
441 compression = b'zlib'
360 compression = b'zlib'
442 for req in repo.requirements:
361 for req in repo.requirements:
443 prefix = req.startswith
362 prefix = req.startswith
444 if prefix(b'revlog-compression-') or prefix(b'exp-compression-'):
363 if prefix(b'revlog-compression-') or prefix(b'exp-compression-'):
445 compression = req.split(b'-', 2)[2]
364 compression = req.split(b'-', 2)[2]
446 return compression
365 return compression
447
366
448 @classmethod
367 @classmethod
449 def fromconfig(cls, repo):
368 def fromconfig(cls, repo):
450 compengines = repo.ui.configlist(b'format', b'revlog-compression')
369 compengines = repo.ui.configlist(b'format', b'revlog-compression')
451 # return the first valid value as the selection code would do
370 # return the first valid value as the selection code would do
452 for comp in compengines:
371 for comp in compengines:
453 if comp in util.compengines:
372 if comp in util.compengines:
454 return comp
373 return comp
455
374
456 # no valide compression found lets display it all for clarity
375 # no valide compression found lets display it all for clarity
457 return b','.join(compengines)
376 return b','.join(compengines)
458
377
459
378
460 @registerformatvariant
379 @registerformatvariant
461 class compressionlevel(formatvariant):
380 class compressionlevel(formatvariant):
462 name = b'compression-level'
381 name = b'compression-level'
463 default = b'default'
382 default = b'default'
464
383
465 description = _(b'compression level')
384 description = _(b'compression level')
466
385
467 upgrademessage = _(b'revlog content will be recompressed')
386 upgrademessage = _(b'revlog content will be recompressed')
468
387
469 @classmethod
388 @classmethod
470 def fromrepo(cls, repo):
389 def fromrepo(cls, repo):
471 comp = compressionengine.fromrepo(repo)
390 comp = compressionengine.fromrepo(repo)
472 level = None
391 level = None
473 if comp == b'zlib':
392 if comp == b'zlib':
474 level = repo.ui.configint(b'storage', b'revlog.zlib.level')
393 level = repo.ui.configint(b'storage', b'revlog.zlib.level')
475 elif comp == b'zstd':
394 elif comp == b'zstd':
476 level = repo.ui.configint(b'storage', b'revlog.zstd.level')
395 level = repo.ui.configint(b'storage', b'revlog.zstd.level')
477 if level is None:
396 if level is None:
478 return b'default'
397 return b'default'
479 return bytes(level)
398 return bytes(level)
480
399
481 @classmethod
400 @classmethod
482 def fromconfig(cls, repo):
401 def fromconfig(cls, repo):
483 comp = compressionengine.fromconfig(repo)
402 comp = compressionengine.fromconfig(repo)
484 level = None
403 level = None
485 if comp == b'zlib':
404 if comp == b'zlib':
486 level = repo.ui.configint(b'storage', b'revlog.zlib.level')
405 level = repo.ui.configint(b'storage', b'revlog.zlib.level')
487 elif comp == b'zstd':
406 elif comp == b'zstd':
488 level = repo.ui.configint(b'storage', b'revlog.zstd.level')
407 level = repo.ui.configint(b'storage', b'revlog.zstd.level')
489 if level is None:
408 if level is None:
490 return b'default'
409 return b'default'
491 return bytes(level)
410 return bytes(level)
492
411
493
412
494 def finddeficiencies(repo):
413 def finddeficiencies(repo):
495 """returns a list of deficiencies that the repo suffer from"""
414 """returns a list of deficiencies that the repo suffer from"""
496 deficiencies = []
415 deficiencies = []
497
416
498 # We could detect lack of revlogv1 and store here, but they were added
417 # We could detect lack of revlogv1 and store here, but they were added
499 # in 0.9.2 and we don't support upgrading repos without these
418 # in 0.9.2 and we don't support upgrading repos without these
500 # requirements, so let's not bother.
419 # requirements, so let's not bother.
501
420
502 for fv in allformatvariant:
421 for fv in allformatvariant:
503 if not fv.fromrepo(repo):
422 if not fv.fromrepo(repo):
504 deficiencies.append(fv)
423 deficiencies.append(fv)
505
424
506 return deficiencies
425 return deficiencies
507
426
508
427
509 ALL_OPTIMISATIONS = []
428 ALL_OPTIMISATIONS = []
510
429
511
430
512 def register_optimization(obj):
431 def register_optimization(obj):
513 ALL_OPTIMISATIONS.append(obj)
432 ALL_OPTIMISATIONS.append(obj)
514 return obj
433 return obj
515
434
516
435
517 register_optimization(
436 register_optimization(
518 improvement(
437 improvement(
519 name=b're-delta-parent',
438 name=b're-delta-parent',
520 type=OPTIMISATION,
439 type=OPTIMISATION,
521 description=_(
440 description=_(
522 b'deltas within internal storage will be recalculated to '
441 b'deltas within internal storage will be recalculated to '
523 b'choose an optimal base revision where this was not '
442 b'choose an optimal base revision where this was not '
524 b'already done; the size of the repository may shrink and '
443 b'already done; the size of the repository may shrink and '
525 b'various operations may become faster; the first time '
444 b'various operations may become faster; the first time '
526 b'this optimization is performed could slow down upgrade '
445 b'this optimization is performed could slow down upgrade '
527 b'execution considerably; subsequent invocations should '
446 b'execution considerably; subsequent invocations should '
528 b'not run noticeably slower'
447 b'not run noticeably slower'
529 ),
448 ),
530 upgrademessage=_(
449 upgrademessage=_(
531 b'deltas within internal storage will choose a new '
450 b'deltas within internal storage will choose a new '
532 b'base revision if needed'
451 b'base revision if needed'
533 ),
452 ),
534 )
453 )
535 )
454 )
536
455
537 register_optimization(
456 register_optimization(
538 improvement(
457 improvement(
539 name=b're-delta-multibase',
458 name=b're-delta-multibase',
540 type=OPTIMISATION,
459 type=OPTIMISATION,
541 description=_(
460 description=_(
542 b'deltas within internal storage will be recalculated '
461 b'deltas within internal storage will be recalculated '
543 b'against multiple base revision and the smallest '
462 b'against multiple base revision and the smallest '
544 b'difference will be used; the size of the repository may '
463 b'difference will be used; the size of the repository may '
545 b'shrink significantly when there are many merges; this '
464 b'shrink significantly when there are many merges; this '
546 b'optimization will slow down execution in proportion to '
465 b'optimization will slow down execution in proportion to '
547 b'the number of merges in the repository and the amount '
466 b'the number of merges in the repository and the amount '
548 b'of files in the repository; this slow down should not '
467 b'of files in the repository; this slow down should not '
549 b'be significant unless there are tens of thousands of '
468 b'be significant unless there are tens of thousands of '
550 b'files and thousands of merges'
469 b'files and thousands of merges'
551 ),
470 ),
552 upgrademessage=_(
471 upgrademessage=_(
553 b'deltas within internal storage will choose an '
472 b'deltas within internal storage will choose an '
554 b'optimal delta by computing deltas against multiple '
473 b'optimal delta by computing deltas against multiple '
555 b'parents; may slow down execution time '
474 b'parents; may slow down execution time '
556 b'significantly'
475 b'significantly'
557 ),
476 ),
558 )
477 )
559 )
478 )
560
479
561 register_optimization(
480 register_optimization(
562 improvement(
481 improvement(
563 name=b're-delta-all',
482 name=b're-delta-all',
564 type=OPTIMISATION,
483 type=OPTIMISATION,
565 description=_(
484 description=_(
566 b'deltas within internal storage will always be '
485 b'deltas within internal storage will always be '
567 b'recalculated without reusing prior deltas; this will '
486 b'recalculated without reusing prior deltas; this will '
568 b'likely make execution run several times slower; this '
487 b'likely make execution run several times slower; this '
569 b'optimization is typically not needed'
488 b'optimization is typically not needed'
570 ),
489 ),
571 upgrademessage=_(
490 upgrademessage=_(
572 b'deltas within internal storage will be fully '
491 b'deltas within internal storage will be fully '
573 b'recomputed; this will likely drastically slow down '
492 b'recomputed; this will likely drastically slow down '
574 b'execution time'
493 b'execution time'
575 ),
494 ),
576 )
495 )
577 )
496 )
578
497
579 register_optimization(
498 register_optimization(
580 improvement(
499 improvement(
581 name=b're-delta-fulladd',
500 name=b're-delta-fulladd',
582 type=OPTIMISATION,
501 type=OPTIMISATION,
583 description=_(
502 description=_(
584 b'every revision will be re-added as if it was new '
503 b'every revision will be re-added as if it was new '
585 b'content. It will go through the full storage '
504 b'content. It will go through the full storage '
586 b'mechanism giving extensions a chance to process it '
505 b'mechanism giving extensions a chance to process it '
587 b'(eg. lfs). This is similar to "re-delta-all" but even '
506 b'(eg. lfs). This is similar to "re-delta-all" but even '
588 b'slower since more logic is involved.'
507 b'slower since more logic is involved.'
589 ),
508 ),
590 upgrademessage=_(
509 upgrademessage=_(
591 b'each revision will be added as new content to the '
510 b'each revision will be added as new content to the '
592 b'internal storage; this will likely drastically slow '
511 b'internal storage; this will likely drastically slow '
593 b'down execution time, but some extensions might need '
512 b'down execution time, but some extensions might need '
594 b'it'
513 b'it'
595 ),
514 ),
596 )
515 )
597 )
516 )
598
517
599
518
600 def findoptimizations(repo):
519 def findoptimizations(repo):
601 """Determine optimisation that could be used during upgrade"""
520 """Determine optimisation that could be used during upgrade"""
602 # These are unconditionally added. There is logic later that figures out
521 # These are unconditionally added. There is logic later that figures out
603 # which ones to apply.
522 # which ones to apply.
604 return list(ALL_OPTIMISATIONS)
523 return list(ALL_OPTIMISATIONS)
605
524
606
525
607 def determineactions(repo, deficiencies, sourcereqs, destreqs):
526 def determineactions(repo, deficiencies, sourcereqs, destreqs):
608 """Determine upgrade actions that will be performed.
527 """Determine upgrade actions that will be performed.
609
528
610 Given a list of improvements as returned by ``finddeficiencies`` and
529 Given a list of improvements as returned by ``finddeficiencies`` and
611 ``findoptimizations``, determine the list of upgrade actions that
530 ``findoptimizations``, determine the list of upgrade actions that
612 will be performed.
531 will be performed.
613
532
614 The role of this function is to filter improvements if needed, apply
533 The role of this function is to filter improvements if needed, apply
615 recommended optimizations from the improvements list that make sense,
534 recommended optimizations from the improvements list that make sense,
616 etc.
535 etc.
617
536
618 Returns a list of action names.
537 Returns a list of action names.
619 """
538 """
620 newactions = []
539 newactions = []
621
540
622 for d in deficiencies:
541 for d in deficiencies:
623 name = d._requirement
542 name = d._requirement
624
543
625 # If the action is a requirement that doesn't show up in the
544 # If the action is a requirement that doesn't show up in the
626 # destination requirements, prune the action.
545 # destination requirements, prune the action.
627 if name is not None and name not in destreqs:
546 if name is not None and name not in destreqs:
628 continue
547 continue
629
548
630 newactions.append(d)
549 newactions.append(d)
631
550
632 # FUTURE consider adding some optimizations here for certain transitions.
551 # FUTURE consider adding some optimizations here for certain transitions.
633 # e.g. adding generaldelta could schedule parent redeltas.
552 # e.g. adding generaldelta could schedule parent redeltas.
634
553
635 return newactions
554 return newactions
636
555
637
556
638 ### Code checking if a repository can got through the upgrade process at all. #
557 ### Code checking if a repository can got through the upgrade process at all. #
639
558
640
559
641 def requiredsourcerequirements(repo):
560 def requiredsourcerequirements(repo):
642 """Obtain requirements required to be present to upgrade a repo.
561 """Obtain requirements required to be present to upgrade a repo.
643
562
644 An upgrade will not be allowed if the repository doesn't have the
563 An upgrade will not be allowed if the repository doesn't have the
645 requirements returned by this function.
564 requirements returned by this function.
646 """
565 """
647 return {
566 return {
648 # Introduced in Mercurial 0.9.2.
567 # Introduced in Mercurial 0.9.2.
649 b'revlogv1',
568 b'revlogv1',
650 # Introduced in Mercurial 0.9.2.
569 # Introduced in Mercurial 0.9.2.
651 b'store',
570 b'store',
652 }
571 }
653
572
654
573
655 def blocksourcerequirements(repo):
574 def blocksourcerequirements(repo):
656 """Obtain requirements that will prevent an upgrade from occurring.
575 """Obtain requirements that will prevent an upgrade from occurring.
657
576
658 An upgrade cannot be performed if the source repository contains a
577 An upgrade cannot be performed if the source repository contains a
659 requirements in the returned set.
578 requirements in the returned set.
660 """
579 """
661 return {
580 return {
662 # The upgrade code does not yet support these experimental features.
581 # The upgrade code does not yet support these experimental features.
663 # This is an artificial limitation.
582 # This is an artificial limitation.
664 requirements.TREEMANIFEST_REQUIREMENT,
583 requirements.TREEMANIFEST_REQUIREMENT,
665 # This was a precursor to generaldelta and was never enabled by default.
584 # This was a precursor to generaldelta and was never enabled by default.
666 # It should (hopefully) not exist in the wild.
585 # It should (hopefully) not exist in the wild.
667 b'parentdelta',
586 b'parentdelta',
668 # Upgrade should operate on the actual store, not the shared link.
587 # Upgrade should operate on the actual store, not the shared link.
669 requirements.SHARED_REQUIREMENT,
588 requirements.SHARED_REQUIREMENT,
670 }
589 }
671
590
672
591
673 def check_source_requirements(repo):
592 def check_source_requirements(repo):
674 """Ensure that no existing requirements prevent the repository upgrade"""
593 """Ensure that no existing requirements prevent the repository upgrade"""
675
594
676 required = requiredsourcerequirements(repo)
595 required = requiredsourcerequirements(repo)
677 missingreqs = required - repo.requirements
596 missingreqs = required - repo.requirements
678 if missingreqs:
597 if missingreqs:
679 msg = _(b'cannot upgrade repository; requirement missing: %s')
598 msg = _(b'cannot upgrade repository; requirement missing: %s')
680 missingreqs = b', '.join(sorted(missingreqs))
599 missingreqs = b', '.join(sorted(missingreqs))
681 raise error.Abort(msg % missingreqs)
600 raise error.Abort(msg % missingreqs)
682
601
683 blocking = blocksourcerequirements(repo)
602 blocking = blocksourcerequirements(repo)
684 blockingreqs = blocking & repo.requirements
603 blockingreqs = blocking & repo.requirements
685 if blockingreqs:
604 if blockingreqs:
686 m = _(b'cannot upgrade repository; unsupported source requirement: %s')
605 m = _(b'cannot upgrade repository; unsupported source requirement: %s')
687 blockingreqs = b', '.join(sorted(blockingreqs))
606 blockingreqs = b', '.join(sorted(blockingreqs))
688 raise error.Abort(m % blockingreqs)
607 raise error.Abort(m % blockingreqs)
689
608
690
609
691 ### Verify the validity of the planned requirement changes ####################
610 ### Verify the validity of the planned requirement changes ####################
692
611
693
612
613 def supportremovedrequirements(repo):
614 """Obtain requirements that can be removed during an upgrade.
615
616 If an upgrade were to create a repository that dropped a requirement,
617 the dropped requirement must appear in the returned set for the upgrade
618 to be allowed.
619 """
620 supported = {
621 requirements.SPARSEREVLOG_REQUIREMENT,
622 requirements.SIDEDATA_REQUIREMENT,
623 requirements.COPIESSDC_REQUIREMENT,
624 requirements.NODEMAP_REQUIREMENT,
625 requirements.SHARESAFE_REQUIREMENT,
626 }
627 for name in compression.compengines:
628 engine = compression.compengines[name]
629 if engine.available() and engine.revlogheader():
630 supported.add(b'exp-compression-%s' % name)
631 if engine.name() == b'zstd':
632 supported.add(b'revlog-compression-zstd')
633 return supported
634
635
636 def supporteddestrequirements(repo):
637 """Obtain requirements that upgrade supports in the destination.
638
639 If the result of the upgrade would create requirements not in this set,
640 the upgrade is disallowed.
641
642 Extensions should monkeypatch this to add their custom requirements.
643 """
644 supported = {
645 b'dotencode',
646 b'fncache',
647 b'generaldelta',
648 b'revlogv1',
649 b'store',
650 requirements.SPARSEREVLOG_REQUIREMENT,
651 requirements.SIDEDATA_REQUIREMENT,
652 requirements.COPIESSDC_REQUIREMENT,
653 requirements.NODEMAP_REQUIREMENT,
654 requirements.SHARESAFE_REQUIREMENT,
655 }
656 for name in compression.compengines:
657 engine = compression.compengines[name]
658 if engine.available() and engine.revlogheader():
659 supported.add(b'exp-compression-%s' % name)
660 if engine.name() == b'zstd':
661 supported.add(b'revlog-compression-zstd')
662 return supported
663
664
665 def allowednewrequirements(repo):
666 """Obtain requirements that can be added to a repository during upgrade.
667
668 This is used to disallow proposed requirements from being added when
669 they weren't present before.
670
671 We use a list of allowed requirement additions instead of a list of known
672 bad additions because the whitelist approach is safer and will prevent
673 future, unknown requirements from accidentally being added.
674 """
675 supported = {
676 b'dotencode',
677 b'fncache',
678 b'generaldelta',
679 requirements.SPARSEREVLOG_REQUIREMENT,
680 requirements.SIDEDATA_REQUIREMENT,
681 requirements.COPIESSDC_REQUIREMENT,
682 requirements.NODEMAP_REQUIREMENT,
683 requirements.SHARESAFE_REQUIREMENT,
684 }
685 for name in compression.compengines:
686 engine = compression.compengines[name]
687 if engine.available() and engine.revlogheader():
688 supported.add(b'exp-compression-%s' % name)
689 if engine.name() == b'zstd':
690 supported.add(b'revlog-compression-zstd')
691 return supported
692
693
694 def check_requirements_changes(repo, new_reqs):
694 def check_requirements_changes(repo, new_reqs):
695 old_reqs = repo.requirements
695 old_reqs = repo.requirements
696
696
697 support_removal = supportremovedrequirements(repo)
697 support_removal = supportremovedrequirements(repo)
698 no_remove_reqs = old_reqs - new_reqs - support_removal
698 no_remove_reqs = old_reqs - new_reqs - support_removal
699 if no_remove_reqs:
699 if no_remove_reqs:
700 msg = _(b'cannot upgrade repository; requirement would be removed: %s')
700 msg = _(b'cannot upgrade repository; requirement would be removed: %s')
701 no_remove_reqs = b', '.join(sorted(no_remove_reqs))
701 no_remove_reqs = b', '.join(sorted(no_remove_reqs))
702 raise error.Abort(msg % no_remove_reqs)
702 raise error.Abort(msg % no_remove_reqs)
703
703
704 support_addition = allowednewrequirements(repo)
704 support_addition = allowednewrequirements(repo)
705 no_add_reqs = new_reqs - old_reqs - support_addition
705 no_add_reqs = new_reqs - old_reqs - support_addition
706 if no_add_reqs:
706 if no_add_reqs:
707 m = _(b'cannot upgrade repository; do not support adding requirement: ')
707 m = _(b'cannot upgrade repository; do not support adding requirement: ')
708 no_add_reqs = b', '.join(sorted(no_add_reqs))
708 no_add_reqs = b', '.join(sorted(no_add_reqs))
709 raise error.Abort(m + no_add_reqs)
709 raise error.Abort(m + no_add_reqs)
710
710
711 supported = supporteddestrequirements(repo)
711 supported = supporteddestrequirements(repo)
712 unsupported_reqs = new_reqs - supported
712 unsupported_reqs = new_reqs - supported
713 if unsupported_reqs:
713 if unsupported_reqs:
714 msg = _(
714 msg = _(
715 b'cannot upgrade repository; do not support destination '
715 b'cannot upgrade repository; do not support destination '
716 b'requirement: %s'
716 b'requirement: %s'
717 )
717 )
718 unsupported_reqs = b', '.join(sorted(unsupported_reqs))
718 unsupported_reqs = b', '.join(sorted(unsupported_reqs))
719 raise error.Abort(msg % unsupported_reqs)
719 raise error.Abort(msg % unsupported_reqs)
General Comments 0
You need to be logged in to leave comments. Login now