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