##// 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 1 # upgrade.py - functions for in place upgrade of Mercurial repository
2 2 #
3 3 # Copyright (c) 2016-present, Gregory Szorc
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import absolute_import
9 9
10 10 from .i18n import _
11 11 from . import (
12 12 error,
13 13 hg,
14 14 localrepo,
15 15 lock as lockmod,
16 16 pycompat,
17 17 requirements as requirementsmod,
18 18 scmutil,
19 19 )
20 20
21 21 from .upgrade_utils import (
22 22 actions as upgrade_actions,
23 23 engine as upgrade_engine,
24 24 )
25 25
26 26 from .utils import (
27 27 stringutil,
28 28 )
29 29
30 30 allformatvariant = upgrade_actions.allformatvariant
31 31
32 32
33 33 def upgraderepo(
34 34 ui,
35 35 repo,
36 36 run=False,
37 37 optimize=None,
38 38 backup=True,
39 39 manifest=None,
40 40 changelog=None,
41 41 filelogs=None,
42 42 ):
43 43 """Upgrade a repository in place."""
44 44 if optimize is None:
45 45 optimize = {}
46 46 repo = repo.unfiltered()
47 47
48 48 revlogs = set(upgrade_engine.UPGRADE_ALL_REVLOGS)
49 49 specentries = (
50 50 (upgrade_engine.UPGRADE_CHANGELOG, changelog),
51 51 (upgrade_engine.UPGRADE_MANIFEST, manifest),
52 52 (upgrade_engine.UPGRADE_FILELOGS, filelogs),
53 53 )
54 54 specified = [(y, x) for (y, x) in specentries if x is not None]
55 55 if specified:
56 56 # we have some limitation on revlogs to be recloned
57 57 if any(x for y, x in specified):
58 58 revlogs = set()
59 59 for upgrade, enabled in specified:
60 60 if enabled:
61 61 revlogs.add(upgrade)
62 62 else:
63 63 # none are enabled
64 64 for upgrade, __ in specified:
65 65 revlogs.discard(upgrade)
66 66
67 67 # Ensure the repository can be upgraded.
68 68 upgrade_actions.check_source_requirements(repo)
69 69
70 70 default_options = localrepo.defaultcreateopts(repo.ui)
71 71 newreqs = localrepo.newreporequirements(repo.ui, default_options)
72 72 newreqs.update(upgrade_actions.preservedrequirements(repo))
73 73
74 74 upgrade_actions.check_requirements_changes(repo, newreqs)
75 75
76 76 # Find and validate all improvements that can be made.
77 77 alloptimizations = upgrade_actions.findoptimizations(repo)
78 78
79 79 # Apply and Validate arguments.
80 80 optimizations = []
81 81 for o in alloptimizations:
82 82 if o.name in optimize:
83 83 optimizations.append(o)
84 84 optimize.discard(o.name)
85 85
86 86 if optimize: # anything left is unknown
87 87 raise error.Abort(
88 88 _(b'unknown optimization action requested: %s')
89 89 % b', '.join(sorted(optimize)),
90 90 hint=_(b'run without arguments to see valid optimizations'),
91 91 )
92 92
93 93 format_upgrades = upgrade_actions.find_format_upgrades(repo)
94 94 up_actions = upgrade_actions.determine_upgrade_actions(
95 95 repo, format_upgrades, optimizations, repo.requirements, newreqs
96 96 )
97 97 removed_actions = upgrade_actions.find_format_downgrades(repo)
98 98
99 99 removedreqs = repo.requirements - newreqs
100 100 addedreqs = newreqs - repo.requirements
101 101
102 102 if revlogs != upgrade_engine.UPGRADE_ALL_REVLOGS:
103 103 incompatible = upgrade_actions.RECLONES_REQUIREMENTS & (
104 104 removedreqs | addedreqs
105 105 )
106 106 if incompatible:
107 107 msg = _(
108 108 b'ignoring revlogs selection flags, format requirements '
109 109 b'change: %s\n'
110 110 )
111 111 ui.warn(msg % b', '.join(sorted(incompatible)))
112 112 revlogs = upgrade_engine.UPGRADE_ALL_REVLOGS
113 113
114 114 upgrade_op = upgrade_actions.UpgradeOperation(
115 115 ui,
116 116 newreqs,
117 117 repo.requirements,
118 118 up_actions,
119 119 removed_actions,
120 120 revlogs,
121 121 )
122 122
123 123 if not run:
124 124 fromconfig = []
125 125 onlydefault = []
126 126
127 127 for d in format_upgrades:
128 128 if d.fromconfig(repo):
129 129 fromconfig.append(d)
130 130 elif d.default:
131 131 onlydefault.append(d)
132 132
133 133 if fromconfig or onlydefault:
134 134
135 135 if fromconfig:
136 136 ui.status(
137 137 _(
138 138 b'repository lacks features recommended by '
139 139 b'current config options:\n\n'
140 140 )
141 141 )
142 142 for i in fromconfig:
143 143 ui.status(b'%s\n %s\n\n' % (i.name, i.description))
144 144
145 145 if onlydefault:
146 146 ui.status(
147 147 _(
148 148 b'repository lacks features used by the default '
149 149 b'config options:\n\n'
150 150 )
151 151 )
152 152 for i in onlydefault:
153 153 ui.status(b'%s\n %s\n\n' % (i.name, i.description))
154 154
155 155 ui.status(b'\n')
156 156 else:
157 157 ui.status(_(b'(no format upgrades found in existing repository)\n'))
158 158
159 159 ui.status(
160 160 _(
161 161 b'performing an upgrade with "--run" will make the following '
162 162 b'changes:\n\n'
163 163 )
164 164 )
165 165
166 166 upgrade_op.print_requirements()
167 167 upgrade_op.print_optimisations()
168 168 upgrade_op.print_upgrade_actions()
169 169 upgrade_op.print_affected_revlogs()
170 170
171 171 if upgrade_op.unused_optimizations:
172 172 ui.status(
173 173 _(
174 174 b'additional optimizations are available by specifying '
175 175 b'"--optimize <name>":\n\n'
176 176 )
177 177 )
178 178 upgrade_op.print_unused_optimizations()
179 179 return
180 180
181 181 if not (upgrade_op.upgrade_actions or upgrade_op.removed_actions):
182 182 ui.status(_(b'nothing to do\n'))
183 183 return
184 184 # Else we're in the run=true case.
185 185 ui.write(_(b'upgrade will perform the following actions:\n\n'))
186 186 upgrade_op.print_requirements()
187 187 upgrade_op.print_optimisations()
188 188 upgrade_op.print_upgrade_actions()
189 189 upgrade_op.print_affected_revlogs()
190 190
191 191 ui.status(_(b'beginning upgrade...\n'))
192 192 with repo.wlock(), repo.lock():
193 193 ui.status(_(b'repository locked and read-only\n'))
194 194 # Our strategy for upgrading the repository is to create a new,
195 195 # temporary repository, write data to it, then do a swap of the
196 196 # data. There are less heavyweight ways to do this, but it is easier
197 197 # to create a new repo object than to instantiate all the components
198 198 # (like the store) separately.
199 199 tmppath = pycompat.mkdtemp(prefix=b'upgrade.', dir=repo.path)
200 200 backuppath = None
201 201 try:
202 202 ui.status(
203 203 _(
204 204 b'creating temporary repository to stage upgraded '
205 205 b'data: %s\n'
206 206 )
207 207 % tmppath
208 208 )
209 209
210 210 # clone ui without using ui.copy because repo.ui is protected
211 211 repoui = repo.ui.__class__(repo.ui)
212 212 dstrepo = hg.repository(repoui, path=tmppath, create=True)
213 213
214 214 with dstrepo.wlock(), dstrepo.lock():
215 215 backuppath = upgrade_engine.upgrade(
216 216 ui, repo, dstrepo, upgrade_op
217 217 )
218 218 if not backup:
219 219 ui.status(
220 220 _(b'removing old repository content %s\n') % backuppath
221 221 )
222 222 repo.vfs.rmtree(backuppath, forcibly=True)
223 223 backuppath = None
224 224
225 225 finally:
226 226 ui.status(_(b'removing temporary repository %s\n') % tmppath)
227 227 repo.vfs.rmtree(tmppath, forcibly=True)
228 228
229 229 if backuppath and not ui.quiet:
230 230 ui.warn(
231 231 _(b'copy of old repository backed up at %s\n') % backuppath
232 232 )
233 233 ui.warn(
234 234 _(
235 235 b'the old repository will not be deleted; remove '
236 236 b'it to free up disk space once the upgraded '
237 237 b'repository is verified\n'
238 238 )
239 239 )
240 240
241 241 upgrade_op.print_post_op_messages()
242 242
243 243
244 244 def upgrade_share_to_safe(ui, hgvfs, storevfs, current_requirements):
245 245 """Upgrades a share to use share-safe mechanism"""
246 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 255 try:
248 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 257 scmutil.writerequires(hgvfs, diffrequires)
257 current_requirements.add(requirementsmod.SHARESAFE_REQUIREMENT)
258 258 ui.warn(_(b'repository upgraded to use share-safe mode\n'))
259 259 except error.LockError as e:
260 260 if ui.configbool(b'experimental', b'sharesafe-auto-upgrade-fail-error'):
261 261 raise error.Abort(
262 262 _(b'failed to upgrade share, got error: %s')
263 263 % stringutil.forcebytestr(e.strerror)
264 264 )
265 265 elif ui.configbool(b'experimental', b'sharesafe-warn-outdated-shares'):
266 266 ui.warn(
267 267 _(b'failed to upgrade share, got error: %s\n')
268 268 % stringutil.forcebytestr(e.strerror)
269 269 )
270 270 finally:
271 271 if wlock:
272 272 wlock.release()
273 273
274 274
275 275 def downgrade_share_to_non_safe(
276 276 ui,
277 277 hgvfs,
278 278 sharedvfs,
279 279 current_requirements,
280 280 ):
281 281 """Downgrades a share which use share-safe to not use it"""
282 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 291 try:
284 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 293 scmutil.writerequires(hgvfs, current_requirements)
293 294 ui.warn(_(b'repository downgraded to not use share-safe mode\n'))
294 295 except error.LockError as e:
295 296 # raise error right away because if downgrade failed, we cannot load
296 297 # the repository because it does not have complete set of requirements
297 298 raise error.Abort(
298 299 _(b'failed to downgrade share, got error: %s')
299 300 % stringutil.forcebytestr(e.strerror)
300 301 )
301 302 finally:
302 303 if wlock:
303 304 wlock.release()
General Comments 0
You need to be logged in to leave comments. Login now