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