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