##// END OF EJS Templates
pytype: stop excluding upgrade.py...
Matt Harbison -
r49303:a4d8de93 default
parent child Browse files
Show More
@@ -1,401 +1,401
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 optimize = {}
45 optimize = set()
46 46 repo = repo.unfiltered()
47 47
48 48 specified_revlogs = {}
49 49 if changelog is not None:
50 50 specified_revlogs[upgrade_engine.UPGRADE_CHANGELOG] = changelog
51 51 if manifest is not None:
52 52 specified_revlogs[upgrade_engine.UPGRADE_MANIFEST] = manifest
53 53 if filelogs is not None:
54 54 specified_revlogs[upgrade_engine.UPGRADE_FILELOGS] = filelogs
55 55
56 56 # Ensure the repository can be upgraded.
57 57 upgrade_actions.check_source_requirements(repo)
58 58
59 59 default_options = localrepo.defaultcreateopts(repo.ui)
60 60 newreqs = localrepo.newreporequirements(repo.ui, default_options)
61 61 newreqs.update(upgrade_actions.preservedrequirements(repo))
62 62
63 63 upgrade_actions.check_requirements_changes(repo, newreqs)
64 64
65 65 # Find and validate all improvements that can be made.
66 66 alloptimizations = upgrade_actions.findoptimizations(repo)
67 67
68 68 # Apply and Validate arguments.
69 69 optimizations = []
70 70 for o in alloptimizations:
71 71 if o.name in optimize:
72 72 optimizations.append(o)
73 73 optimize.discard(o.name)
74 74
75 75 if optimize: # anything left is unknown
76 76 raise error.Abort(
77 77 _(b'unknown optimization action requested: %s')
78 78 % b', '.join(sorted(optimize)),
79 79 hint=_(b'run without arguments to see valid optimizations'),
80 80 )
81 81
82 82 format_upgrades = upgrade_actions.find_format_upgrades(repo)
83 83 up_actions = upgrade_actions.determine_upgrade_actions(
84 84 repo, format_upgrades, optimizations, repo.requirements, newreqs
85 85 )
86 86 removed_actions = upgrade_actions.find_format_downgrades(repo)
87 87
88 88 # check if we need to touch revlog and if so, which ones
89 89
90 90 touched_revlogs = set()
91 91 overwrite_msg = _(b'warning: ignoring %14s, as upgrade is changing: %s\n')
92 92 select_msg = _(b'note: selecting %s for processing to change: %s\n')
93 93 msg_issued = 0
94 94
95 95 FL = upgrade_engine.UPGRADE_FILELOGS
96 96 MN = upgrade_engine.UPGRADE_MANIFEST
97 97 CL = upgrade_engine.UPGRADE_CHANGELOG
98 98
99 99 if optimizations:
100 100 if any(specified_revlogs.values()):
101 101 # we have some limitation on revlogs to be recloned
102 102 for rl, enabled in specified_revlogs.items():
103 103 if enabled:
104 104 touched_revlogs.add(rl)
105 105 else:
106 106 touched_revlogs = set(upgrade_engine.UPGRADE_ALL_REVLOGS)
107 107 for rl, enabled in specified_revlogs.items():
108 108 if not enabled:
109 109 touched_revlogs.discard(rl)
110 110
111 111 for action in sorted(up_actions + removed_actions, key=lambda a: a.name):
112 112 # optimisation does not "requires anything, they just needs it.
113 113 if action.type != upgrade_actions.FORMAT_VARIANT:
114 114 continue
115 115
116 116 if action.touches_filelogs and FL not in touched_revlogs:
117 117 if FL in specified_revlogs:
118 118 if not specified_revlogs[FL]:
119 119 msg = overwrite_msg % (b'--no-filelogs', action.name)
120 120 ui.warn(msg)
121 121 msg_issued = 2
122 122 else:
123 123 msg = select_msg % (b'all-filelogs', action.name)
124 124 ui.status(msg)
125 125 if not ui.quiet:
126 126 msg_issued = 1
127 127 touched_revlogs.add(FL)
128 128
129 129 if action.touches_manifests and MN not in touched_revlogs:
130 130 if MN in specified_revlogs:
131 131 if not specified_revlogs[MN]:
132 132 msg = overwrite_msg % (b'--no-manifest', action.name)
133 133 ui.warn(msg)
134 134 msg_issued = 2
135 135 else:
136 136 msg = select_msg % (b'all-manifestlogs', action.name)
137 137 ui.status(msg)
138 138 if not ui.quiet:
139 139 msg_issued = 1
140 140 touched_revlogs.add(MN)
141 141
142 142 if action.touches_changelog and CL not in touched_revlogs:
143 143 if CL in specified_revlogs:
144 144 if not specified_revlogs[CL]:
145 145 msg = overwrite_msg % (b'--no-changelog', action.name)
146 146 ui.warn(msg)
147 147 msg_issued = True
148 148 else:
149 149 msg = select_msg % (b'changelog', action.name)
150 150 ui.status(msg)
151 151 if not ui.quiet:
152 152 msg_issued = 1
153 153 touched_revlogs.add(CL)
154 154 if msg_issued >= 2:
155 155 ui.warn((b"\n"))
156 156 elif msg_issued >= 1:
157 157 ui.status((b"\n"))
158 158
159 159 upgrade_op = upgrade_actions.UpgradeOperation(
160 160 ui,
161 161 newreqs,
162 162 repo.requirements,
163 163 up_actions,
164 164 removed_actions,
165 165 touched_revlogs,
166 166 backup,
167 167 )
168 168
169 169 if not run:
170 170 fromconfig = []
171 171 onlydefault = []
172 172
173 173 for d in format_upgrades:
174 174 if d.fromconfig(repo):
175 175 fromconfig.append(d)
176 176 elif d.default:
177 177 onlydefault.append(d)
178 178
179 179 if fromconfig or onlydefault:
180 180
181 181 if fromconfig:
182 182 ui.status(
183 183 _(
184 184 b'repository lacks features recommended by '
185 185 b'current config options:\n\n'
186 186 )
187 187 )
188 188 for i in fromconfig:
189 189 ui.status(b'%s\n %s\n\n' % (i.name, i.description))
190 190
191 191 if onlydefault:
192 192 ui.status(
193 193 _(
194 194 b'repository lacks features used by the default '
195 195 b'config options:\n\n'
196 196 )
197 197 )
198 198 for i in onlydefault:
199 199 ui.status(b'%s\n %s\n\n' % (i.name, i.description))
200 200
201 201 ui.status(b'\n')
202 202 else:
203 203 ui.status(_(b'(no format upgrades found in existing repository)\n'))
204 204
205 205 ui.status(
206 206 _(
207 207 b'performing an upgrade with "--run" will make the following '
208 208 b'changes:\n\n'
209 209 )
210 210 )
211 211
212 212 upgrade_op.print_requirements()
213 213 upgrade_op.print_optimisations()
214 214 upgrade_op.print_upgrade_actions()
215 215 upgrade_op.print_affected_revlogs()
216 216
217 217 if upgrade_op.unused_optimizations:
218 218 ui.status(
219 219 _(
220 220 b'additional optimizations are available by specifying '
221 221 b'"--optimize <name>":\n\n'
222 222 )
223 223 )
224 224 upgrade_op.print_unused_optimizations()
225 225 return
226 226
227 227 if not (upgrade_op.upgrade_actions or upgrade_op.removed_actions):
228 228 ui.status(_(b'nothing to do\n'))
229 229 return
230 230 # Else we're in the run=true case.
231 231 ui.write(_(b'upgrade will perform the following actions:\n\n'))
232 232 upgrade_op.print_requirements()
233 233 upgrade_op.print_optimisations()
234 234 upgrade_op.print_upgrade_actions()
235 235 upgrade_op.print_affected_revlogs()
236 236
237 237 ui.status(_(b'beginning upgrade...\n'))
238 238 with repo.wlock(), repo.lock():
239 239 ui.status(_(b'repository locked and read-only\n'))
240 240 # Our strategy for upgrading the repository is to create a new,
241 241 # temporary repository, write data to it, then do a swap of the
242 242 # data. There are less heavyweight ways to do this, but it is easier
243 243 # to create a new repo object than to instantiate all the components
244 244 # (like the store) separately.
245 245 tmppath = pycompat.mkdtemp(prefix=b'upgrade.', dir=repo.path)
246 246 backuppath = None
247 247 try:
248 248 ui.status(
249 249 _(
250 250 b'creating temporary repository to stage upgraded '
251 251 b'data: %s\n'
252 252 )
253 253 % tmppath
254 254 )
255 255
256 256 # clone ui without using ui.copy because repo.ui is protected
257 257 repoui = repo.ui.__class__(repo.ui)
258 258 dstrepo = hg.repository(repoui, path=tmppath, create=True)
259 259
260 260 with dstrepo.wlock(), dstrepo.lock():
261 261 backuppath = upgrade_engine.upgrade(
262 262 ui, repo, dstrepo, upgrade_op
263 263 )
264 264
265 265 finally:
266 266 ui.status(_(b'removing temporary repository %s\n') % tmppath)
267 267 repo.vfs.rmtree(tmppath, forcibly=True)
268 268
269 269 if backuppath and not ui.quiet:
270 270 ui.warn(
271 271 _(b'copy of old repository backed up at %s\n') % backuppath
272 272 )
273 273 ui.warn(
274 274 _(
275 275 b'the old repository will not be deleted; remove '
276 276 b'it to free up disk space once the upgraded '
277 277 b'repository is verified\n'
278 278 )
279 279 )
280 280
281 281 upgrade_op.print_post_op_messages()
282 282
283 283
284 284 def upgrade_share_to_safe(
285 285 ui,
286 286 hgvfs,
287 287 storevfs,
288 288 current_requirements,
289 289 mismatch_config,
290 290 mismatch_warn,
291 291 ):
292 292 """Upgrades a share to use share-safe mechanism"""
293 293 wlock = None
294 294 store_requirements = localrepo._readrequires(storevfs, False)
295 295 original_crequirements = current_requirements.copy()
296 296 # after upgrade, store requires will be shared, so lets find
297 297 # the requirements which are not present in store and
298 298 # write them to share's .hg/requires
299 299 diffrequires = current_requirements - store_requirements
300 300 # add share-safe requirement as it will mark the share as share-safe
301 301 diffrequires.add(requirementsmod.SHARESAFE_REQUIREMENT)
302 302 current_requirements.add(requirementsmod.SHARESAFE_REQUIREMENT)
303 303 # in `allow` case, we don't try to upgrade, we just respect the source
304 304 # state, update requirements and continue
305 305 if mismatch_config == b'allow':
306 306 return
307 307 try:
308 308 wlock = lockmod.trylock(ui, hgvfs, b'wlock', 0, 0)
309 309 # some process might change the requirement in between, re-read
310 310 # and update current_requirements
311 311 locked_requirements = localrepo._readrequires(hgvfs, True)
312 312 if locked_requirements != original_crequirements:
313 313 removed = current_requirements - locked_requirements
314 314 # update current_requirements in place because it's passed
315 315 # as reference
316 316 current_requirements -= removed
317 317 current_requirements |= locked_requirements
318 318 diffrequires = current_requirements - store_requirements
319 319 # add share-safe requirement as it will mark the share as share-safe
320 320 diffrequires.add(requirementsmod.SHARESAFE_REQUIREMENT)
321 321 current_requirements.add(requirementsmod.SHARESAFE_REQUIREMENT)
322 322 scmutil.writerequires(hgvfs, diffrequires)
323 323 ui.warn(_(b'repository upgraded to use share-safe mode\n'))
324 324 except error.LockError as e:
325 325 hint = _(
326 326 b"see `hg help config.format.use-share-safe` for more information"
327 327 )
328 328 if mismatch_config == b'upgrade-abort':
329 329 raise error.Abort(
330 330 _(b'failed to upgrade share, got error: %s')
331 331 % stringutil.forcebytestr(e.strerror),
332 332 hint=hint,
333 333 )
334 334 elif mismatch_warn:
335 335 ui.warn(
336 336 _(b'failed to upgrade share, got error: %s\n')
337 337 % stringutil.forcebytestr(e.strerror),
338 338 hint=hint,
339 339 )
340 340 finally:
341 341 if wlock:
342 342 wlock.release()
343 343
344 344
345 345 def downgrade_share_to_non_safe(
346 346 ui,
347 347 hgvfs,
348 348 sharedvfs,
349 349 current_requirements,
350 350 mismatch_config,
351 351 mismatch_warn,
352 352 ):
353 353 """Downgrades a share which use share-safe to not use it"""
354 354 wlock = None
355 355 source_requirements = localrepo._readrequires(sharedvfs, True)
356 356 original_crequirements = current_requirements.copy()
357 357 # we cannot be 100% sure on which requirements were present in store when
358 358 # the source supported share-safe. However, we do know that working
359 359 # directory requirements were not there. Hence we remove them
360 360 source_requirements -= requirementsmod.WORKING_DIR_REQUIREMENTS
361 361 current_requirements |= source_requirements
362 362 current_requirements.remove(requirementsmod.SHARESAFE_REQUIREMENT)
363 363 if mismatch_config == b'allow':
364 364 return
365 365
366 366 try:
367 367 wlock = lockmod.trylock(ui, hgvfs, b'wlock', 0, 0)
368 368 # some process might change the requirement in between, re-read
369 369 # and update current_requirements
370 370 locked_requirements = localrepo._readrequires(hgvfs, True)
371 371 if locked_requirements != original_crequirements:
372 372 removed = current_requirements - locked_requirements
373 373 # update current_requirements in place because it's passed
374 374 # as reference
375 375 current_requirements -= removed
376 376 current_requirements |= locked_requirements
377 377 current_requirements |= source_requirements
378 378 current_requirements -= set(requirementsmod.SHARESAFE_REQUIREMENT)
379 379 scmutil.writerequires(hgvfs, current_requirements)
380 380 ui.warn(_(b'repository downgraded to not use share-safe mode\n'))
381 381 except error.LockError as e:
382 382 hint = _(
383 383 b"see `hg help config.format.use-share-safe` for more information"
384 384 )
385 385 # If upgrade-abort is set, abort when upgrade fails, else let the
386 386 # process continue as `upgrade-allow` is set
387 387 if mismatch_config == b'downgrade-abort':
388 388 raise error.Abort(
389 389 _(b'failed to downgrade share, got error: %s')
390 390 % stringutil.forcebytestr(e.strerror),
391 391 hint=hint,
392 392 )
393 393 elif mismatch_warn:
394 394 ui.warn(
395 395 _(b'failed to downgrade share, got error: %s\n')
396 396 % stringutil.forcebytestr(e.strerror),
397 397 hint=hint,
398 398 )
399 399 finally:
400 400 if wlock:
401 401 wlock.release()
@@ -1,104 +1,102
1 1 #require pytype py3 slow
2 2
3 3 $ cd $RUNTESTDIR/..
4 4
5 5 Many of the individual files that are excluded here confuse pytype
6 6 because they do a mix of Python 2 and Python 3 things
7 7 conditionally. There's no good way to help it out with that as far as
8 8 I can tell, so let's just hide those files from it for now. We should
9 9 endeavor to empty this list out over time, as some of these are
10 10 probably hiding real problems.
11 11
12 12 mercurial/bundlerepo.py # no vfs and ui attrs on bundlerepo
13 13 mercurial/changegroup.py # mysterious incorrect type detection
14 14 mercurial/chgserver.py # [attribute-error]
15 15 mercurial/cmdutil.py # No attribute 'markcopied' on mercurial.context.filectx [attribute-error]
16 16 mercurial/context.py # many [attribute-error]
17 17 mercurial/copies.py # No attribute 'items' on None [attribute-error]
18 18 mercurial/crecord.py # tons of [attribute-error], [module-attr]
19 19 mercurial/debugcommands.py # [wrong-arg-types]
20 20 mercurial/dispatch.py # initstdio: No attribute ... on TextIO [attribute-error]
21 21 mercurial/exchange.py # [attribute-error]
22 22 mercurial/hgweb/hgweb_mod.py # [attribute-error], [name-error], [wrong-arg-types]
23 23 mercurial/hgweb/server.py # [attribute-error], [name-error], [module-attr]
24 24 mercurial/hgweb/webcommands.py # [missing-parameter]
25 25 mercurial/hgweb/wsgicgi.py # confused values in os.environ
26 26 mercurial/httppeer.py # [attribute-error], [wrong-arg-types]
27 27 mercurial/interfaces # No attribute 'capabilities' on peer [attribute-error]
28 28 mercurial/keepalive.py # [attribute-error]
29 29 mercurial/localrepo.py # [attribute-error]
30 30 mercurial/lsprof.py # unguarded import
31 31 mercurial/manifest.py # [unsupported-operands], [wrong-arg-types]
32 32 mercurial/minirst.py # [unsupported-operands], [attribute-error]
33 33 mercurial/patch.py # [wrong-arg-types]
34 34 mercurial/pure/osutil.py # [invalid-typevar], [not-callable]
35 35 mercurial/pure/parsers.py # [attribute-error]
36 36 mercurial/pycompat.py # bytes vs str issues
37 37 mercurial/repoview.py # [attribute-error]
38 38 mercurial/sslutil.py # [attribute-error]
39 39 mercurial/statprof.py # bytes vs str on TextIO.write() [wrong-arg-types]
40 40 mercurial/testing/storage.py # tons of [attribute-error]
41 41 mercurial/ui.py # [attribute-error], [wrong-arg-types]
42 42 mercurial/unionrepo.py # ui, svfs, unfiltered [attribute-error]
43 mercurial/upgrade.py # line 84, in upgraderepo: No attribute 'discard' on Dict[nothing, nothing] [attribute-error]
44 43 mercurial/util.py # [attribute-error], [wrong-arg-count]
45 44 mercurial/utils/procutil.py # [attribute-error], [module-attr], [bad-return-type]
46 45 mercurial/utils/stringutil.py # [module-attr], [wrong-arg-count]
47 46 mercurial/utils/memorytop.py # not 3.6 compatible
48 47 mercurial/win32.py # [not-callable]
49 48 mercurial/wireprotoframing.py # [unsupported-operands], [attribute-error], [import-error]
50 49 mercurial/wireprotoserver.py # line 253, in _availableapis: No attribute '__iter__' on Callable[[Any, Any], Any] [attribute-error]
51 50 mercurial/wireprotov1peer.py # [attribute-error]
52 51 mercurial/wireprotov1server.py # BUG?: BundleValueError handler accesses subclass's attrs
53 52 mercurial/wireprotov2server.py # [unsupported-operands], [attribute-error]
54 53
55 54 TODO: use --no-cache on test server? Caching the files locally helps during
56 55 development, but may be a hinderance for CI testing.
57 56
58 57 $ pytype -V 3.6 --keep-going --jobs auto mercurial \
59 58 > -x mercurial/bundlerepo.py \
60 59 > -x mercurial/changegroup.py \
61 60 > -x mercurial/chgserver.py \
62 61 > -x mercurial/cmdutil.py \
63 62 > -x mercurial/context.py \
64 63 > -x mercurial/copies.py \
65 64 > -x mercurial/crecord.py \
66 65 > -x mercurial/debugcommands.py \
67 66 > -x mercurial/dispatch.py \
68 67 > -x mercurial/exchange.py \
69 68 > -x mercurial/hgweb/hgweb_mod.py \
70 69 > -x mercurial/hgweb/server.py \
71 70 > -x mercurial/hgweb/webcommands.py \
72 71 > -x mercurial/hgweb/wsgicgi.py \
73 72 > -x mercurial/httppeer.py \
74 73 > -x mercurial/interfaces \
75 74 > -x mercurial/keepalive.py \
76 75 > -x mercurial/localrepo.py \
77 76 > -x mercurial/lsprof.py \
78 77 > -x mercurial/manifest.py \
79 78 > -x mercurial/minirst.py \
80 79 > -x mercurial/patch.py \
81 80 > -x mercurial/pure/osutil.py \
82 81 > -x mercurial/pure/parsers.py \
83 82 > -x mercurial/pycompat.py \
84 83 > -x mercurial/repoview.py \
85 84 > -x mercurial/sslutil.py \
86 85 > -x mercurial/statprof.py \
87 86 > -x mercurial/testing/storage.py \
88 87 > -x mercurial/thirdparty \
89 88 > -x mercurial/ui.py \
90 89 > -x mercurial/unionrepo.py \
91 > -x mercurial/upgrade.py \
92 90 > -x mercurial/utils/procutil.py \
93 91 > -x mercurial/utils/stringutil.py \
94 92 > -x mercurial/utils/memorytop.py \
95 93 > -x mercurial/win32.py \
96 94 > -x mercurial/wireprotoframing.py \
97 95 > -x mercurial/wireprotoserver.py \
98 96 > -x mercurial/wireprotov1peer.py \
99 97 > -x mercurial/wireprotov1server.py \
100 98 > -x mercurial/wireprotov2server.py \
101 99 > > $TESTTMP/pytype-output.txt || cat $TESTTMP/pytype-output.txt
102 100
103 101 Only show the results on a failure, because the output on success is also
104 102 voluminous and variable.
General Comments 0
You need to be logged in to leave comments. Login now