##// END OF EJS Templates
upgrade: take lock only for part where it's required...
Pulkit Goyal -
r47047:17176f64 default
parent child Browse files
Show More
@@ -1,303 +1,304 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)
48 revlogs = set(upgrade_engine.UPGRADE_ALL_REVLOGS)
49 specentries = (
49 specentries = (
50 (upgrade_engine.UPGRADE_CHANGELOG, changelog),
50 (upgrade_engine.UPGRADE_CHANGELOG, changelog),
51 (upgrade_engine.UPGRADE_MANIFEST, manifest),
51 (upgrade_engine.UPGRADE_MANIFEST, manifest),
52 (upgrade_engine.UPGRADE_FILELOGS, filelogs),
52 (upgrade_engine.UPGRADE_FILELOGS, filelogs),
53 )
53 )
54 specified = [(y, x) for (y, x) in specentries if x is not None]
54 specified = [(y, x) for (y, x) in specentries if x is not None]
55 if specified:
55 if specified:
56 # we have some limitation on revlogs to be recloned
56 # we have some limitation on revlogs to be recloned
57 if any(x for y, x in specified):
57 if any(x for y, x in specified):
58 revlogs = set()
58 revlogs = set()
59 for upgrade, enabled in specified:
59 for upgrade, enabled in specified:
60 if enabled:
60 if enabled:
61 revlogs.add(upgrade)
61 revlogs.add(upgrade)
62 else:
62 else:
63 # none are enabled
63 # none are enabled
64 for upgrade, __ in specified:
64 for upgrade, __ in specified:
65 revlogs.discard(upgrade)
65 revlogs.discard(upgrade)
66
66
67 # Ensure the repository can be upgraded.
67 # Ensure the repository can be upgraded.
68 upgrade_actions.check_source_requirements(repo)
68 upgrade_actions.check_source_requirements(repo)
69
69
70 default_options = localrepo.defaultcreateopts(repo.ui)
70 default_options = localrepo.defaultcreateopts(repo.ui)
71 newreqs = localrepo.newreporequirements(repo.ui, default_options)
71 newreqs = localrepo.newreporequirements(repo.ui, default_options)
72 newreqs.update(upgrade_actions.preservedrequirements(repo))
72 newreqs.update(upgrade_actions.preservedrequirements(repo))
73
73
74 upgrade_actions.check_requirements_changes(repo, newreqs)
74 upgrade_actions.check_requirements_changes(repo, newreqs)
75
75
76 # Find and validate all improvements that can be made.
76 # Find and validate all improvements that can be made.
77 alloptimizations = upgrade_actions.findoptimizations(repo)
77 alloptimizations = upgrade_actions.findoptimizations(repo)
78
78
79 # Apply and Validate arguments.
79 # Apply and Validate arguments.
80 optimizations = []
80 optimizations = []
81 for o in alloptimizations:
81 for o in alloptimizations:
82 if o.name in optimize:
82 if o.name in optimize:
83 optimizations.append(o)
83 optimizations.append(o)
84 optimize.discard(o.name)
84 optimize.discard(o.name)
85
85
86 if optimize: # anything left is unknown
86 if optimize: # anything left is unknown
87 raise error.Abort(
87 raise error.Abort(
88 _(b'unknown optimization action requested: %s')
88 _(b'unknown optimization action requested: %s')
89 % b', '.join(sorted(optimize)),
89 % b', '.join(sorted(optimize)),
90 hint=_(b'run without arguments to see valid optimizations'),
90 hint=_(b'run without arguments to see valid optimizations'),
91 )
91 )
92
92
93 format_upgrades = upgrade_actions.find_format_upgrades(repo)
93 format_upgrades = upgrade_actions.find_format_upgrades(repo)
94 up_actions = upgrade_actions.determine_upgrade_actions(
94 up_actions = upgrade_actions.determine_upgrade_actions(
95 repo, format_upgrades, optimizations, repo.requirements, newreqs
95 repo, format_upgrades, optimizations, repo.requirements, newreqs
96 )
96 )
97 removed_actions = upgrade_actions.find_format_downgrades(repo)
97 removed_actions = upgrade_actions.find_format_downgrades(repo)
98
98
99 removedreqs = repo.requirements - newreqs
99 removedreqs = repo.requirements - newreqs
100 addedreqs = newreqs - repo.requirements
100 addedreqs = newreqs - repo.requirements
101
101
102 if revlogs != upgrade_engine.UPGRADE_ALL_REVLOGS:
102 if revlogs != upgrade_engine.UPGRADE_ALL_REVLOGS:
103 incompatible = upgrade_actions.RECLONES_REQUIREMENTS & (
103 incompatible = upgrade_actions.RECLONES_REQUIREMENTS & (
104 removedreqs | addedreqs
104 removedreqs | addedreqs
105 )
105 )
106 if incompatible:
106 if incompatible:
107 msg = _(
107 msg = _(
108 b'ignoring revlogs selection flags, format requirements '
108 b'ignoring revlogs selection flags, format requirements '
109 b'change: %s\n'
109 b'change: %s\n'
110 )
110 )
111 ui.warn(msg % b', '.join(sorted(incompatible)))
111 ui.warn(msg % b', '.join(sorted(incompatible)))
112 revlogs = upgrade_engine.UPGRADE_ALL_REVLOGS
112 revlogs = upgrade_engine.UPGRADE_ALL_REVLOGS
113
113
114 upgrade_op = upgrade_actions.UpgradeOperation(
114 upgrade_op = upgrade_actions.UpgradeOperation(
115 ui,
115 ui,
116 newreqs,
116 newreqs,
117 repo.requirements,
117 repo.requirements,
118 up_actions,
118 up_actions,
119 removed_actions,
119 removed_actions,
120 revlogs,
120 revlogs,
121 )
121 )
122
122
123 if not run:
123 if not run:
124 fromconfig = []
124 fromconfig = []
125 onlydefault = []
125 onlydefault = []
126
126
127 for d in format_upgrades:
127 for d in format_upgrades:
128 if d.fromconfig(repo):
128 if d.fromconfig(repo):
129 fromconfig.append(d)
129 fromconfig.append(d)
130 elif d.default:
130 elif d.default:
131 onlydefault.append(d)
131 onlydefault.append(d)
132
132
133 if fromconfig or onlydefault:
133 if fromconfig or onlydefault:
134
134
135 if fromconfig:
135 if fromconfig:
136 ui.status(
136 ui.status(
137 _(
137 _(
138 b'repository lacks features recommended by '
138 b'repository lacks features recommended by '
139 b'current config options:\n\n'
139 b'current config options:\n\n'
140 )
140 )
141 )
141 )
142 for i in fromconfig:
142 for i in fromconfig:
143 ui.status(b'%s\n %s\n\n' % (i.name, i.description))
143 ui.status(b'%s\n %s\n\n' % (i.name, i.description))
144
144
145 if onlydefault:
145 if onlydefault:
146 ui.status(
146 ui.status(
147 _(
147 _(
148 b'repository lacks features used by the default '
148 b'repository lacks features used by the default '
149 b'config options:\n\n'
149 b'config options:\n\n'
150 )
150 )
151 )
151 )
152 for i in onlydefault:
152 for i in onlydefault:
153 ui.status(b'%s\n %s\n\n' % (i.name, i.description))
153 ui.status(b'%s\n %s\n\n' % (i.name, i.description))
154
154
155 ui.status(b'\n')
155 ui.status(b'\n')
156 else:
156 else:
157 ui.status(_(b'(no format upgrades found in existing repository)\n'))
157 ui.status(_(b'(no format upgrades found in existing repository)\n'))
158
158
159 ui.status(
159 ui.status(
160 _(
160 _(
161 b'performing an upgrade with "--run" will make the following '
161 b'performing an upgrade with "--run" will make the following '
162 b'changes:\n\n'
162 b'changes:\n\n'
163 )
163 )
164 )
164 )
165
165
166 upgrade_op.print_requirements()
166 upgrade_op.print_requirements()
167 upgrade_op.print_optimisations()
167 upgrade_op.print_optimisations()
168 upgrade_op.print_upgrade_actions()
168 upgrade_op.print_upgrade_actions()
169 upgrade_op.print_affected_revlogs()
169 upgrade_op.print_affected_revlogs()
170
170
171 if upgrade_op.unused_optimizations:
171 if upgrade_op.unused_optimizations:
172 ui.status(
172 ui.status(
173 _(
173 _(
174 b'additional optimizations are available by specifying '
174 b'additional optimizations are available by specifying '
175 b'"--optimize <name>":\n\n'
175 b'"--optimize <name>":\n\n'
176 )
176 )
177 )
177 )
178 upgrade_op.print_unused_optimizations()
178 upgrade_op.print_unused_optimizations()
179 return
179 return
180
180
181 if not (upgrade_op.upgrade_actions or upgrade_op.removed_actions):
181 if not (upgrade_op.upgrade_actions or upgrade_op.removed_actions):
182 ui.status(_(b'nothing to do\n'))
182 ui.status(_(b'nothing to do\n'))
183 return
183 return
184 # Else we're in the run=true case.
184 # Else we're in the run=true case.
185 ui.write(_(b'upgrade will perform the following actions:\n\n'))
185 ui.write(_(b'upgrade will perform the following actions:\n\n'))
186 upgrade_op.print_requirements()
186 upgrade_op.print_requirements()
187 upgrade_op.print_optimisations()
187 upgrade_op.print_optimisations()
188 upgrade_op.print_upgrade_actions()
188 upgrade_op.print_upgrade_actions()
189 upgrade_op.print_affected_revlogs()
189 upgrade_op.print_affected_revlogs()
190
190
191 ui.status(_(b'beginning upgrade...\n'))
191 ui.status(_(b'beginning upgrade...\n'))
192 with repo.wlock(), repo.lock():
192 with repo.wlock(), repo.lock():
193 ui.status(_(b'repository locked and read-only\n'))
193 ui.status(_(b'repository locked and read-only\n'))
194 # Our strategy for upgrading the repository is to create a new,
194 # Our strategy for upgrading the repository is to create a new,
195 # temporary repository, write data to it, then do a swap of the
195 # temporary repository, write data to it, then do a swap of the
196 # data. There are less heavyweight ways to do this, but it is easier
196 # data. There are less heavyweight ways to do this, but it is easier
197 # to create a new repo object than to instantiate all the components
197 # to create a new repo object than to instantiate all the components
198 # (like the store) separately.
198 # (like the store) separately.
199 tmppath = pycompat.mkdtemp(prefix=b'upgrade.', dir=repo.path)
199 tmppath = pycompat.mkdtemp(prefix=b'upgrade.', dir=repo.path)
200 backuppath = None
200 backuppath = None
201 try:
201 try:
202 ui.status(
202 ui.status(
203 _(
203 _(
204 b'creating temporary repository to stage upgraded '
204 b'creating temporary repository to stage upgraded '
205 b'data: %s\n'
205 b'data: %s\n'
206 )
206 )
207 % tmppath
207 % tmppath
208 )
208 )
209
209
210 # clone ui without using ui.copy because repo.ui is protected
210 # clone ui without using ui.copy because repo.ui is protected
211 repoui = repo.ui.__class__(repo.ui)
211 repoui = repo.ui.__class__(repo.ui)
212 dstrepo = hg.repository(repoui, path=tmppath, create=True)
212 dstrepo = hg.repository(repoui, path=tmppath, create=True)
213
213
214 with dstrepo.wlock(), dstrepo.lock():
214 with dstrepo.wlock(), dstrepo.lock():
215 backuppath = upgrade_engine.upgrade(
215 backuppath = upgrade_engine.upgrade(
216 ui, repo, dstrepo, upgrade_op
216 ui, repo, dstrepo, upgrade_op
217 )
217 )
218 if not backup:
218 if not backup:
219 ui.status(
219 ui.status(
220 _(b'removing old repository content %s\n') % backuppath
220 _(b'removing old repository content %s\n') % backuppath
221 )
221 )
222 repo.vfs.rmtree(backuppath, forcibly=True)
222 repo.vfs.rmtree(backuppath, forcibly=True)
223 backuppath = None
223 backuppath = None
224
224
225 finally:
225 finally:
226 ui.status(_(b'removing temporary repository %s\n') % tmppath)
226 ui.status(_(b'removing temporary repository %s\n') % tmppath)
227 repo.vfs.rmtree(tmppath, forcibly=True)
227 repo.vfs.rmtree(tmppath, forcibly=True)
228
228
229 if backuppath and not ui.quiet:
229 if backuppath and not ui.quiet:
230 ui.warn(
230 ui.warn(
231 _(b'copy of old repository backed up at %s\n') % backuppath
231 _(b'copy of old repository backed up at %s\n') % backuppath
232 )
232 )
233 ui.warn(
233 ui.warn(
234 _(
234 _(
235 b'the old repository will not be deleted; remove '
235 b'the old repository will not be deleted; remove '
236 b'it to free up disk space once the upgraded '
236 b'it to free up disk space once the upgraded '
237 b'repository is verified\n'
237 b'repository is verified\n'
238 )
238 )
239 )
239 )
240
240
241 upgrade_op.print_post_op_messages()
241 upgrade_op.print_post_op_messages()
242
242
243
243
244 def upgrade_share_to_safe(ui, hgvfs, storevfs, current_requirements):
244 def upgrade_share_to_safe(ui, hgvfs, storevfs, current_requirements):
245 """Upgrades a share to use share-safe mechanism"""
245 """Upgrades a share to use share-safe mechanism"""
246 wlock = None
246 wlock = None
247 store_requirements = localrepo._readrequires(storevfs, False)
248 # after upgrade, store requires will be shared, so lets find
249 # the requirements which are not present in store and
250 # write them to share's .hg/requires
251 diffrequires = current_requirements - store_requirements
252 # add share-safe requirement as it will mark the share as share-safe
253 diffrequires.add(requirementsmod.SHARESAFE_REQUIREMENT)
254 current_requirements.add(requirementsmod.SHARESAFE_REQUIREMENT)
247 try:
255 try:
248 wlock = lockmod.trylock(ui, hgvfs, b'wlock', 0, 0)
256 wlock = lockmod.trylock(ui, hgvfs, b'wlock', 0, 0)
249 store_requirements = localrepo._readrequires(storevfs, False)
250 # after upgrade, store requires will be shared, so lets find
251 # the requirements which are not present in store and
252 # write them to share's .hg/requires
253 diffrequires = current_requirements - store_requirements
254 # add share-safe requirement as it will mark the share as share-safe
255 diffrequires.add(requirementsmod.SHARESAFE_REQUIREMENT)
256 scmutil.writerequires(hgvfs, diffrequires)
257 scmutil.writerequires(hgvfs, diffrequires)
257 current_requirements.add(requirementsmod.SHARESAFE_REQUIREMENT)
258 ui.warn(_(b'repository upgraded to use share-safe mode\n'))
258 ui.warn(_(b'repository upgraded to use share-safe mode\n'))
259 except error.LockError as e:
259 except error.LockError as e:
260 if ui.configbool(b'experimental', b'sharesafe-auto-upgrade-fail-error'):
260 if ui.configbool(b'experimental', b'sharesafe-auto-upgrade-fail-error'):
261 raise error.Abort(
261 raise error.Abort(
262 _(b'failed to upgrade share, got error: %s')
262 _(b'failed to upgrade share, got error: %s')
263 % stringutil.forcebytestr(e.strerror)
263 % stringutil.forcebytestr(e.strerror)
264 )
264 )
265 elif ui.configbool(b'experimental', b'sharesafe-warn-outdated-shares'):
265 elif ui.configbool(b'experimental', b'sharesafe-warn-outdated-shares'):
266 ui.warn(
266 ui.warn(
267 _(b'failed to upgrade share, got error: %s\n')
267 _(b'failed to upgrade share, got error: %s\n')
268 % stringutil.forcebytestr(e.strerror)
268 % stringutil.forcebytestr(e.strerror)
269 )
269 )
270 finally:
270 finally:
271 if wlock:
271 if wlock:
272 wlock.release()
272 wlock.release()
273
273
274
274
275 def downgrade_share_to_non_safe(
275 def downgrade_share_to_non_safe(
276 ui,
276 ui,
277 hgvfs,
277 hgvfs,
278 sharedvfs,
278 sharedvfs,
279 current_requirements,
279 current_requirements,
280 ):
280 ):
281 """Downgrades a share which use share-safe to not use it"""
281 """Downgrades a share which use share-safe to not use it"""
282 wlock = None
282 wlock = None
283 source_requirements = localrepo._readrequires(sharedvfs, True)
284 # we cannot be 100% sure on which requirements were present in store when
285 # the source supported share-safe. However, we do know that working
286 # directory requirements were not there. Hence we remove them
287 source_requirements -= requirementsmod.WORKING_DIR_REQUIREMENTS
288 current_requirements |= source_requirements
289 current_requirements.remove(requirementsmod.SHARESAFE_REQUIREMENT)
290
283 try:
291 try:
284 wlock = lockmod.trylock(ui, hgvfs, b'wlock', 0, 0)
292 wlock = lockmod.trylock(ui, hgvfs, b'wlock', 0, 0)
285 source_requirements = localrepo._readrequires(sharedvfs, True)
286 # we cannot be 100% sure on which requirements were present in store when
287 # the source supported share-safe. However, we do know that working
288 # directory requirements were not there. Hence we remove them
289 source_requirements -= requirementsmod.WORKING_DIR_REQUIREMENTS
290 current_requirements |= source_requirements
291 current_requirements.remove(requirementsmod.SHARESAFE_REQUIREMENT)
292 scmutil.writerequires(hgvfs, current_requirements)
293 scmutil.writerequires(hgvfs, current_requirements)
293 ui.warn(_(b'repository downgraded to not use share-safe mode\n'))
294 ui.warn(_(b'repository downgraded to not use share-safe mode\n'))
294 except error.LockError as e:
295 except error.LockError as e:
295 # raise error right away because if downgrade failed, we cannot load
296 # raise error right away because if downgrade failed, we cannot load
296 # the repository because it does not have complete set of requirements
297 # the repository because it does not have complete set of requirements
297 raise error.Abort(
298 raise error.Abort(
298 _(b'failed to downgrade share, got error: %s')
299 _(b'failed to downgrade share, got error: %s')
299 % stringutil.forcebytestr(e.strerror)
300 % stringutil.forcebytestr(e.strerror)
300 )
301 )
301 finally:
302 finally:
302 if wlock:
303 if wlock:
303 wlock.release()
304 wlock.release()
General Comments 0
You need to be logged in to leave comments. Login now