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