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