##// END OF EJS Templates
upgrade: move the revlog selection code lower down the chain...
marmoute -
r49273:6e045497 default
parent child Browse files
Show More
@@ -1,356 +1,361 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 lock as lockmod,
15 lock as lockmod,
16 pycompat,
16 pycompat,
17 requirements as requirementsmod,
17 requirements as requirementsmod,
18 scmutil,
18 scmutil,
19 )
19 )
20
20
21 from .upgrade_utils import (
21 from .upgrade_utils import (
22 actions as upgrade_actions,
22 actions as upgrade_actions,
23 engine as upgrade_engine,
23 engine as upgrade_engine,
24 )
24 )
25
25
26 from .utils import (
26 from .utils import (
27 stringutil,
27 stringutil,
28 )
28 )
29
29
30 allformatvariant = upgrade_actions.allformatvariant
30 allformatvariant = upgrade_actions.allformatvariant
31
31
32
32
33 def upgraderepo(
33 def upgraderepo(
34 ui,
34 ui,
35 repo,
35 repo,
36 run=False,
36 run=False,
37 optimize=None,
37 optimize=None,
38 backup=True,
38 backup=True,
39 manifest=None,
39 manifest=None,
40 changelog=None,
40 changelog=None,
41 filelogs=None,
41 filelogs=None,
42 ):
42 ):
43 """Upgrade a repository in place."""
43 """Upgrade a repository in place."""
44 if optimize is None:
44 if optimize is None:
45 optimize = {}
45 optimize = {}
46 repo = repo.unfiltered()
46 repo = repo.unfiltered()
47
47
48 revlogs = set(upgrade_engine.UPGRADE_ALL_REVLOGS)
49 specentries = (
48 specentries = (
50 (upgrade_engine.UPGRADE_CHANGELOG, changelog),
49 (upgrade_engine.UPGRADE_CHANGELOG, changelog),
51 (upgrade_engine.UPGRADE_MANIFEST, manifest),
50 (upgrade_engine.UPGRADE_MANIFEST, manifest),
52 (upgrade_engine.UPGRADE_FILELOGS, filelogs),
51 (upgrade_engine.UPGRADE_FILELOGS, filelogs),
53 )
52 )
54 specified = [(y, x) for (y, x) in specentries if x is not None]
55 if specified:
56 # we have some limitation on revlogs to be recloned
57 if any(x for y, x in specified):
58 revlogs = set()
59 for upgrade, enabled in specified:
60 if enabled:
61 revlogs.add(upgrade)
62 else:
63 # none are enabled
64 for upgrade, __ in specified:
65 revlogs.discard(upgrade)
66
53
67 # Ensure the repository can be upgraded.
54 # Ensure the repository can be upgraded.
68 upgrade_actions.check_source_requirements(repo)
55 upgrade_actions.check_source_requirements(repo)
69
56
70 default_options = localrepo.defaultcreateopts(repo.ui)
57 default_options = localrepo.defaultcreateopts(repo.ui)
71 newreqs = localrepo.newreporequirements(repo.ui, default_options)
58 newreqs = localrepo.newreporequirements(repo.ui, default_options)
72 newreqs.update(upgrade_actions.preservedrequirements(repo))
59 newreqs.update(upgrade_actions.preservedrequirements(repo))
73
60
74 upgrade_actions.check_requirements_changes(repo, newreqs)
61 upgrade_actions.check_requirements_changes(repo, newreqs)
75
62
76 # Find and validate all improvements that can be made.
63 # Find and validate all improvements that can be made.
77 alloptimizations = upgrade_actions.findoptimizations(repo)
64 alloptimizations = upgrade_actions.findoptimizations(repo)
78
65
79 # Apply and Validate arguments.
66 # Apply and Validate arguments.
80 optimizations = []
67 optimizations = []
81 for o in alloptimizations:
68 for o in alloptimizations:
82 if o.name in optimize:
69 if o.name in optimize:
83 optimizations.append(o)
70 optimizations.append(o)
84 optimize.discard(o.name)
71 optimize.discard(o.name)
85
72
86 if optimize: # anything left is unknown
73 if optimize: # anything left is unknown
87 raise error.Abort(
74 raise error.Abort(
88 _(b'unknown optimization action requested: %s')
75 _(b'unknown optimization action requested: %s')
89 % b', '.join(sorted(optimize)),
76 % b', '.join(sorted(optimize)),
90 hint=_(b'run without arguments to see valid optimizations'),
77 hint=_(b'run without arguments to see valid optimizations'),
91 )
78 )
92
79
93 format_upgrades = upgrade_actions.find_format_upgrades(repo)
80 format_upgrades = upgrade_actions.find_format_upgrades(repo)
94 up_actions = upgrade_actions.determine_upgrade_actions(
81 up_actions = upgrade_actions.determine_upgrade_actions(
95 repo, format_upgrades, optimizations, repo.requirements, newreqs
82 repo, format_upgrades, optimizations, repo.requirements, newreqs
96 )
83 )
97 removed_actions = upgrade_actions.find_format_downgrades(repo)
84 removed_actions = upgrade_actions.find_format_downgrades(repo)
98
85
99 removedreqs = repo.requirements - newreqs
86 removedreqs = repo.requirements - newreqs
100 addedreqs = newreqs - repo.requirements
87 addedreqs = newreqs - repo.requirements
101
88
89 # check if we need to touch revlog and if so, which ones
90
91 revlogs = set(upgrade_engine.UPGRADE_ALL_REVLOGS)
92 specified = [(y, x) for (y, x) in specentries if x is not None]
93 if specified:
94 # we have some limitation on revlogs to be recloned
95 if any(x for y, x in specified):
96 revlogs = set()
97 for upgrade, enabled in specified:
98 if enabled:
99 revlogs.add(upgrade)
100 else:
101 # none are enabled
102 for upgrade, __ in specified:
103 revlogs.discard(upgrade)
104
105 # check the consistency of the revlog selection with the planned action
106
102 if revlogs != upgrade_engine.UPGRADE_ALL_REVLOGS:
107 if revlogs != upgrade_engine.UPGRADE_ALL_REVLOGS:
103 incompatible = upgrade_actions.RECLONES_REQUIREMENTS & (
108 incompatible = upgrade_actions.RECLONES_REQUIREMENTS & (
104 removedreqs | addedreqs
109 removedreqs | addedreqs
105 )
110 )
106 if incompatible:
111 if incompatible:
107 msg = _(
112 msg = _(
108 b'ignoring revlogs selection flags, format requirements '
113 b'ignoring revlogs selection flags, format requirements '
109 b'change: %s\n'
114 b'change: %s\n'
110 )
115 )
111 ui.warn(msg % b', '.join(sorted(incompatible)))
116 ui.warn(msg % b', '.join(sorted(incompatible)))
112 revlogs = upgrade_engine.UPGRADE_ALL_REVLOGS
117 revlogs = upgrade_engine.UPGRADE_ALL_REVLOGS
113
118
114 upgrade_op = upgrade_actions.UpgradeOperation(
119 upgrade_op = upgrade_actions.UpgradeOperation(
115 ui,
120 ui,
116 newreqs,
121 newreqs,
117 repo.requirements,
122 repo.requirements,
118 up_actions,
123 up_actions,
119 removed_actions,
124 removed_actions,
120 revlogs,
125 revlogs,
121 backup,
126 backup,
122 )
127 )
123
128
124 if not run:
129 if not run:
125 fromconfig = []
130 fromconfig = []
126 onlydefault = []
131 onlydefault = []
127
132
128 for d in format_upgrades:
133 for d in format_upgrades:
129 if d.fromconfig(repo):
134 if d.fromconfig(repo):
130 fromconfig.append(d)
135 fromconfig.append(d)
131 elif d.default:
136 elif d.default:
132 onlydefault.append(d)
137 onlydefault.append(d)
133
138
134 if fromconfig or onlydefault:
139 if fromconfig or onlydefault:
135
140
136 if fromconfig:
141 if fromconfig:
137 ui.status(
142 ui.status(
138 _(
143 _(
139 b'repository lacks features recommended by '
144 b'repository lacks features recommended by '
140 b'current config options:\n\n'
145 b'current config options:\n\n'
141 )
146 )
142 )
147 )
143 for i in fromconfig:
148 for i in fromconfig:
144 ui.status(b'%s\n %s\n\n' % (i.name, i.description))
149 ui.status(b'%s\n %s\n\n' % (i.name, i.description))
145
150
146 if onlydefault:
151 if onlydefault:
147 ui.status(
152 ui.status(
148 _(
153 _(
149 b'repository lacks features used by the default '
154 b'repository lacks features used by the default '
150 b'config options:\n\n'
155 b'config options:\n\n'
151 )
156 )
152 )
157 )
153 for i in onlydefault:
158 for i in onlydefault:
154 ui.status(b'%s\n %s\n\n' % (i.name, i.description))
159 ui.status(b'%s\n %s\n\n' % (i.name, i.description))
155
160
156 ui.status(b'\n')
161 ui.status(b'\n')
157 else:
162 else:
158 ui.status(_(b'(no format upgrades found in existing repository)\n'))
163 ui.status(_(b'(no format upgrades found in existing repository)\n'))
159
164
160 ui.status(
165 ui.status(
161 _(
166 _(
162 b'performing an upgrade with "--run" will make the following '
167 b'performing an upgrade with "--run" will make the following '
163 b'changes:\n\n'
168 b'changes:\n\n'
164 )
169 )
165 )
170 )
166
171
167 upgrade_op.print_requirements()
172 upgrade_op.print_requirements()
168 upgrade_op.print_optimisations()
173 upgrade_op.print_optimisations()
169 upgrade_op.print_upgrade_actions()
174 upgrade_op.print_upgrade_actions()
170 upgrade_op.print_affected_revlogs()
175 upgrade_op.print_affected_revlogs()
171
176
172 if upgrade_op.unused_optimizations:
177 if upgrade_op.unused_optimizations:
173 ui.status(
178 ui.status(
174 _(
179 _(
175 b'additional optimizations are available by specifying '
180 b'additional optimizations are available by specifying '
176 b'"--optimize <name>":\n\n'
181 b'"--optimize <name>":\n\n'
177 )
182 )
178 )
183 )
179 upgrade_op.print_unused_optimizations()
184 upgrade_op.print_unused_optimizations()
180 return
185 return
181
186
182 if not (upgrade_op.upgrade_actions or upgrade_op.removed_actions):
187 if not (upgrade_op.upgrade_actions or upgrade_op.removed_actions):
183 ui.status(_(b'nothing to do\n'))
188 ui.status(_(b'nothing to do\n'))
184 return
189 return
185 # Else we're in the run=true case.
190 # Else we're in the run=true case.
186 ui.write(_(b'upgrade will perform the following actions:\n\n'))
191 ui.write(_(b'upgrade will perform the following actions:\n\n'))
187 upgrade_op.print_requirements()
192 upgrade_op.print_requirements()
188 upgrade_op.print_optimisations()
193 upgrade_op.print_optimisations()
189 upgrade_op.print_upgrade_actions()
194 upgrade_op.print_upgrade_actions()
190 upgrade_op.print_affected_revlogs()
195 upgrade_op.print_affected_revlogs()
191
196
192 ui.status(_(b'beginning upgrade...\n'))
197 ui.status(_(b'beginning upgrade...\n'))
193 with repo.wlock(), repo.lock():
198 with repo.wlock(), repo.lock():
194 ui.status(_(b'repository locked and read-only\n'))
199 ui.status(_(b'repository locked and read-only\n'))
195 # Our strategy for upgrading the repository is to create a new,
200 # Our strategy for upgrading the repository is to create a new,
196 # temporary repository, write data to it, then do a swap of the
201 # temporary repository, write data to it, then do a swap of the
197 # data. There are less heavyweight ways to do this, but it is easier
202 # data. There are less heavyweight ways to do this, but it is easier
198 # to create a new repo object than to instantiate all the components
203 # to create a new repo object than to instantiate all the components
199 # (like the store) separately.
204 # (like the store) separately.
200 tmppath = pycompat.mkdtemp(prefix=b'upgrade.', dir=repo.path)
205 tmppath = pycompat.mkdtemp(prefix=b'upgrade.', dir=repo.path)
201 backuppath = None
206 backuppath = None
202 try:
207 try:
203 ui.status(
208 ui.status(
204 _(
209 _(
205 b'creating temporary repository to stage upgraded '
210 b'creating temporary repository to stage upgraded '
206 b'data: %s\n'
211 b'data: %s\n'
207 )
212 )
208 % tmppath
213 % tmppath
209 )
214 )
210
215
211 # clone ui without using ui.copy because repo.ui is protected
216 # clone ui without using ui.copy because repo.ui is protected
212 repoui = repo.ui.__class__(repo.ui)
217 repoui = repo.ui.__class__(repo.ui)
213 dstrepo = hg.repository(repoui, path=tmppath, create=True)
218 dstrepo = hg.repository(repoui, path=tmppath, create=True)
214
219
215 with dstrepo.wlock(), dstrepo.lock():
220 with dstrepo.wlock(), dstrepo.lock():
216 backuppath = upgrade_engine.upgrade(
221 backuppath = upgrade_engine.upgrade(
217 ui, repo, dstrepo, upgrade_op
222 ui, repo, dstrepo, upgrade_op
218 )
223 )
219
224
220 finally:
225 finally:
221 ui.status(_(b'removing temporary repository %s\n') % tmppath)
226 ui.status(_(b'removing temporary repository %s\n') % tmppath)
222 repo.vfs.rmtree(tmppath, forcibly=True)
227 repo.vfs.rmtree(tmppath, forcibly=True)
223
228
224 if backuppath and not ui.quiet:
229 if backuppath and not ui.quiet:
225 ui.warn(
230 ui.warn(
226 _(b'copy of old repository backed up at %s\n') % backuppath
231 _(b'copy of old repository backed up at %s\n') % backuppath
227 )
232 )
228 ui.warn(
233 ui.warn(
229 _(
234 _(
230 b'the old repository will not be deleted; remove '
235 b'the old repository will not be deleted; remove '
231 b'it to free up disk space once the upgraded '
236 b'it to free up disk space once the upgraded '
232 b'repository is verified\n'
237 b'repository is verified\n'
233 )
238 )
234 )
239 )
235
240
236 upgrade_op.print_post_op_messages()
241 upgrade_op.print_post_op_messages()
237
242
238
243
239 def upgrade_share_to_safe(
244 def upgrade_share_to_safe(
240 ui,
245 ui,
241 hgvfs,
246 hgvfs,
242 storevfs,
247 storevfs,
243 current_requirements,
248 current_requirements,
244 mismatch_config,
249 mismatch_config,
245 mismatch_warn,
250 mismatch_warn,
246 ):
251 ):
247 """Upgrades a share to use share-safe mechanism"""
252 """Upgrades a share to use share-safe mechanism"""
248 wlock = None
253 wlock = None
249 store_requirements = localrepo._readrequires(storevfs, False)
254 store_requirements = localrepo._readrequires(storevfs, False)
250 original_crequirements = current_requirements.copy()
255 original_crequirements = current_requirements.copy()
251 # after upgrade, store requires will be shared, so lets find
256 # after upgrade, store requires will be shared, so lets find
252 # the requirements which are not present in store and
257 # the requirements which are not present in store and
253 # write them to share's .hg/requires
258 # write them to share's .hg/requires
254 diffrequires = current_requirements - store_requirements
259 diffrequires = current_requirements - store_requirements
255 # add share-safe requirement as it will mark the share as share-safe
260 # add share-safe requirement as it will mark the share as share-safe
256 diffrequires.add(requirementsmod.SHARESAFE_REQUIREMENT)
261 diffrequires.add(requirementsmod.SHARESAFE_REQUIREMENT)
257 current_requirements.add(requirementsmod.SHARESAFE_REQUIREMENT)
262 current_requirements.add(requirementsmod.SHARESAFE_REQUIREMENT)
258 # in `allow` case, we don't try to upgrade, we just respect the source
263 # in `allow` case, we don't try to upgrade, we just respect the source
259 # state, update requirements and continue
264 # state, update requirements and continue
260 if mismatch_config == b'allow':
265 if mismatch_config == b'allow':
261 return
266 return
262 try:
267 try:
263 wlock = lockmod.trylock(ui, hgvfs, b'wlock', 0, 0)
268 wlock = lockmod.trylock(ui, hgvfs, b'wlock', 0, 0)
264 # some process might change the requirement in between, re-read
269 # some process might change the requirement in between, re-read
265 # and update current_requirements
270 # and update current_requirements
266 locked_requirements = localrepo._readrequires(hgvfs, True)
271 locked_requirements = localrepo._readrequires(hgvfs, True)
267 if locked_requirements != original_crequirements:
272 if locked_requirements != original_crequirements:
268 removed = current_requirements - locked_requirements
273 removed = current_requirements - locked_requirements
269 # update current_requirements in place because it's passed
274 # update current_requirements in place because it's passed
270 # as reference
275 # as reference
271 current_requirements -= removed
276 current_requirements -= removed
272 current_requirements |= locked_requirements
277 current_requirements |= locked_requirements
273 diffrequires = current_requirements - store_requirements
278 diffrequires = current_requirements - store_requirements
274 # add share-safe requirement as it will mark the share as share-safe
279 # add share-safe requirement as it will mark the share as share-safe
275 diffrequires.add(requirementsmod.SHARESAFE_REQUIREMENT)
280 diffrequires.add(requirementsmod.SHARESAFE_REQUIREMENT)
276 current_requirements.add(requirementsmod.SHARESAFE_REQUIREMENT)
281 current_requirements.add(requirementsmod.SHARESAFE_REQUIREMENT)
277 scmutil.writerequires(hgvfs, diffrequires)
282 scmutil.writerequires(hgvfs, diffrequires)
278 ui.warn(_(b'repository upgraded to use share-safe mode\n'))
283 ui.warn(_(b'repository upgraded to use share-safe mode\n'))
279 except error.LockError as e:
284 except error.LockError as e:
280 hint = _(
285 hint = _(
281 "see `hg help config.format.use-share-safe` for more information"
286 "see `hg help config.format.use-share-safe` for more information"
282 )
287 )
283 if mismatch_config == b'upgrade-abort':
288 if mismatch_config == b'upgrade-abort':
284 raise error.Abort(
289 raise error.Abort(
285 _(b'failed to upgrade share, got error: %s')
290 _(b'failed to upgrade share, got error: %s')
286 % stringutil.forcebytestr(e.strerror),
291 % stringutil.forcebytestr(e.strerror),
287 hint=hint,
292 hint=hint,
288 )
293 )
289 elif mismatch_warn:
294 elif mismatch_warn:
290 ui.warn(
295 ui.warn(
291 _(b'failed to upgrade share, got error: %s\n')
296 _(b'failed to upgrade share, got error: %s\n')
292 % stringutil.forcebytestr(e.strerror),
297 % stringutil.forcebytestr(e.strerror),
293 hint=hint,
298 hint=hint,
294 )
299 )
295 finally:
300 finally:
296 if wlock:
301 if wlock:
297 wlock.release()
302 wlock.release()
298
303
299
304
300 def downgrade_share_to_non_safe(
305 def downgrade_share_to_non_safe(
301 ui,
306 ui,
302 hgvfs,
307 hgvfs,
303 sharedvfs,
308 sharedvfs,
304 current_requirements,
309 current_requirements,
305 mismatch_config,
310 mismatch_config,
306 mismatch_warn,
311 mismatch_warn,
307 ):
312 ):
308 """Downgrades a share which use share-safe to not use it"""
313 """Downgrades a share which use share-safe to not use it"""
309 wlock = None
314 wlock = None
310 source_requirements = localrepo._readrequires(sharedvfs, True)
315 source_requirements = localrepo._readrequires(sharedvfs, True)
311 original_crequirements = current_requirements.copy()
316 original_crequirements = current_requirements.copy()
312 # we cannot be 100% sure on which requirements were present in store when
317 # we cannot be 100% sure on which requirements were present in store when
313 # the source supported share-safe. However, we do know that working
318 # the source supported share-safe. However, we do know that working
314 # directory requirements were not there. Hence we remove them
319 # directory requirements were not there. Hence we remove them
315 source_requirements -= requirementsmod.WORKING_DIR_REQUIREMENTS
320 source_requirements -= requirementsmod.WORKING_DIR_REQUIREMENTS
316 current_requirements |= source_requirements
321 current_requirements |= source_requirements
317 current_requirements.remove(requirementsmod.SHARESAFE_REQUIREMENT)
322 current_requirements.remove(requirementsmod.SHARESAFE_REQUIREMENT)
318 if mismatch_config == b'allow':
323 if mismatch_config == b'allow':
319 return
324 return
320
325
321 try:
326 try:
322 wlock = lockmod.trylock(ui, hgvfs, b'wlock', 0, 0)
327 wlock = lockmod.trylock(ui, hgvfs, b'wlock', 0, 0)
323 # some process might change the requirement in between, re-read
328 # some process might change the requirement in between, re-read
324 # and update current_requirements
329 # and update current_requirements
325 locked_requirements = localrepo._readrequires(hgvfs, True)
330 locked_requirements = localrepo._readrequires(hgvfs, True)
326 if locked_requirements != original_crequirements:
331 if locked_requirements != original_crequirements:
327 removed = current_requirements - locked_requirements
332 removed = current_requirements - locked_requirements
328 # update current_requirements in place because it's passed
333 # update current_requirements in place because it's passed
329 # as reference
334 # as reference
330 current_requirements -= removed
335 current_requirements -= removed
331 current_requirements |= locked_requirements
336 current_requirements |= locked_requirements
332 current_requirements |= source_requirements
337 current_requirements |= source_requirements
333 current_requirements -= set(requirementsmod.SHARESAFE_REQUIREMENT)
338 current_requirements -= set(requirementsmod.SHARESAFE_REQUIREMENT)
334 scmutil.writerequires(hgvfs, current_requirements)
339 scmutil.writerequires(hgvfs, current_requirements)
335 ui.warn(_(b'repository downgraded to not use share-safe mode\n'))
340 ui.warn(_(b'repository downgraded to not use share-safe mode\n'))
336 except error.LockError as e:
341 except error.LockError as e:
337 hint = _(
342 hint = _(
338 "see `hg help config.format.use-share-safe` for more information"
343 "see `hg help config.format.use-share-safe` for more information"
339 )
344 )
340 # If upgrade-abort is set, abort when upgrade fails, else let the
345 # If upgrade-abort is set, abort when upgrade fails, else let the
341 # process continue as `upgrade-allow` is set
346 # process continue as `upgrade-allow` is set
342 if mismatch_config == b'downgrade-abort':
347 if mismatch_config == b'downgrade-abort':
343 raise error.Abort(
348 raise error.Abort(
344 _(b'failed to downgrade share, got error: %s')
349 _(b'failed to downgrade share, got error: %s')
345 % stringutil.forcebytestr(e.strerror),
350 % stringutil.forcebytestr(e.strerror),
346 hint=hint,
351 hint=hint,
347 )
352 )
348 elif mismatch_warn:
353 elif mismatch_warn:
349 ui.warn(
354 ui.warn(
350 _(b'failed to downgrade share, got error: %s\n')
355 _(b'failed to downgrade share, got error: %s\n')
351 % stringutil.forcebytestr(e.strerror),
356 % stringutil.forcebytestr(e.strerror),
352 hint=hint,
357 hint=hint,
353 )
358 )
354 finally:
359 finally:
355 if wlock:
360 if wlock:
356 wlock.release()
361 wlock.release()
General Comments 0
You need to be logged in to leave comments. Login now