##// END OF EJS Templates
narrow: update narrow spec within a dirstate.parentchange context...
marmoute -
r48404:090fc6a9 default
parent child Browse files
Show More
@@ -1,691 +1,694 b''
1 1 # narrowcommands.py - command modifications for narrowhg extension
2 2 #
3 3 # Copyright 2017 Google, Inc.
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 from __future__ import absolute_import
8 8
9 9 import itertools
10 10 import os
11 11
12 12 from mercurial.i18n import _
13 13 from mercurial.node import (
14 14 hex,
15 15 short,
16 16 )
17 17 from mercurial import (
18 18 bundle2,
19 19 cmdutil,
20 20 commands,
21 21 discovery,
22 22 encoding,
23 23 error,
24 24 exchange,
25 25 extensions,
26 26 hg,
27 27 narrowspec,
28 28 pathutil,
29 29 pycompat,
30 30 registrar,
31 31 repair,
32 32 repoview,
33 33 requirements,
34 34 sparse,
35 35 util,
36 36 wireprototypes,
37 37 )
38 38 from mercurial.utils import (
39 39 urlutil,
40 40 )
41 41
42 42 table = {}
43 43 command = registrar.command(table)
44 44
45 45
46 46 def setup():
47 47 """Wraps user-facing mercurial commands with narrow-aware versions."""
48 48
49 49 entry = extensions.wrapcommand(commands.table, b'clone', clonenarrowcmd)
50 50 entry[1].append(
51 51 (b'', b'narrow', None, _(b"create a narrow clone of select files"))
52 52 )
53 53 entry[1].append(
54 54 (
55 55 b'',
56 56 b'depth',
57 57 b'',
58 58 _(b"limit the history fetched by distance from heads"),
59 59 )
60 60 )
61 61 entry[1].append((b'', b'narrowspec', b'', _(b"read narrowspecs from file")))
62 62 # TODO(durin42): unify sparse/narrow --include/--exclude logic a bit
63 63 if b'sparse' not in extensions.enabled():
64 64 entry[1].append(
65 65 (b'', b'include', [], _(b"specifically fetch this file/directory"))
66 66 )
67 67 entry[1].append(
68 68 (
69 69 b'',
70 70 b'exclude',
71 71 [],
72 72 _(b"do not fetch this file/directory, even if included"),
73 73 )
74 74 )
75 75
76 76 entry = extensions.wrapcommand(commands.table, b'pull', pullnarrowcmd)
77 77 entry[1].append(
78 78 (
79 79 b'',
80 80 b'depth',
81 81 b'',
82 82 _(b"limit the history fetched by distance from heads"),
83 83 )
84 84 )
85 85
86 86 extensions.wrapcommand(commands.table, b'archive', archivenarrowcmd)
87 87
88 88
89 89 def clonenarrowcmd(orig, ui, repo, *args, **opts):
90 90 """Wraps clone command, so 'hg clone' first wraps localrepo.clone()."""
91 91 opts = pycompat.byteskwargs(opts)
92 92 wrappedextraprepare = util.nullcontextmanager()
93 93 narrowspecfile = opts[b'narrowspec']
94 94
95 95 if narrowspecfile:
96 96 filepath = os.path.join(encoding.getcwd(), narrowspecfile)
97 97 ui.status(_(b"reading narrowspec from '%s'\n") % filepath)
98 98 try:
99 99 fdata = util.readfile(filepath)
100 100 except IOError as inst:
101 101 raise error.Abort(
102 102 _(b"cannot read narrowspecs from '%s': %s")
103 103 % (filepath, encoding.strtolocal(inst.strerror))
104 104 )
105 105
106 106 includes, excludes, profiles = sparse.parseconfig(ui, fdata, b'narrow')
107 107 if profiles:
108 108 raise error.ConfigError(
109 109 _(
110 110 b"cannot specify other files using '%include' in"
111 111 b" narrowspec"
112 112 )
113 113 )
114 114
115 115 narrowspec.validatepatterns(includes)
116 116 narrowspec.validatepatterns(excludes)
117 117
118 118 # narrowspec is passed so we should assume that user wants narrow clone
119 119 opts[b'narrow'] = True
120 120 opts[b'include'].extend(includes)
121 121 opts[b'exclude'].extend(excludes)
122 122
123 123 if opts[b'narrow']:
124 124
125 125 def pullbundle2extraprepare_widen(orig, pullop, kwargs):
126 126 orig(pullop, kwargs)
127 127
128 128 if opts.get(b'depth'):
129 129 kwargs[b'depth'] = opts[b'depth']
130 130
131 131 wrappedextraprepare = extensions.wrappedfunction(
132 132 exchange, b'_pullbundle2extraprepare', pullbundle2extraprepare_widen
133 133 )
134 134
135 135 with wrappedextraprepare:
136 136 return orig(ui, repo, *args, **pycompat.strkwargs(opts))
137 137
138 138
139 139 def pullnarrowcmd(orig, ui, repo, *args, **opts):
140 140 """Wraps pull command to allow modifying narrow spec."""
141 141 wrappedextraprepare = util.nullcontextmanager()
142 142 if requirements.NARROW_REQUIREMENT in repo.requirements:
143 143
144 144 def pullbundle2extraprepare_widen(orig, pullop, kwargs):
145 145 orig(pullop, kwargs)
146 146 if opts.get('depth'):
147 147 kwargs[b'depth'] = opts['depth']
148 148
149 149 wrappedextraprepare = extensions.wrappedfunction(
150 150 exchange, b'_pullbundle2extraprepare', pullbundle2extraprepare_widen
151 151 )
152 152
153 153 with wrappedextraprepare:
154 154 return orig(ui, repo, *args, **opts)
155 155
156 156
157 157 def archivenarrowcmd(orig, ui, repo, *args, **opts):
158 158 """Wraps archive command to narrow the default includes."""
159 159 if requirements.NARROW_REQUIREMENT in repo.requirements:
160 160 repo_includes, repo_excludes = repo.narrowpats
161 161 includes = set(opts.get('include', []))
162 162 excludes = set(opts.get('exclude', []))
163 163 includes, excludes, unused_invalid = narrowspec.restrictpatterns(
164 164 includes, excludes, repo_includes, repo_excludes
165 165 )
166 166 if includes:
167 167 opts['include'] = includes
168 168 if excludes:
169 169 opts['exclude'] = excludes
170 170 return orig(ui, repo, *args, **opts)
171 171
172 172
173 173 def pullbundle2extraprepare(orig, pullop, kwargs):
174 174 repo = pullop.repo
175 175 if requirements.NARROW_REQUIREMENT not in repo.requirements:
176 176 return orig(pullop, kwargs)
177 177
178 178 if wireprototypes.NARROWCAP not in pullop.remote.capabilities():
179 179 raise error.Abort(_(b"server does not support narrow clones"))
180 180 orig(pullop, kwargs)
181 181 kwargs[b'narrow'] = True
182 182 include, exclude = repo.narrowpats
183 183 kwargs[b'oldincludepats'] = include
184 184 kwargs[b'oldexcludepats'] = exclude
185 185 if include:
186 186 kwargs[b'includepats'] = include
187 187 if exclude:
188 188 kwargs[b'excludepats'] = exclude
189 189 # calculate known nodes only in ellipses cases because in non-ellipses cases
190 190 # we have all the nodes
191 191 if wireprototypes.ELLIPSESCAP1 in pullop.remote.capabilities():
192 192 kwargs[b'known'] = [
193 193 hex(ctx.node())
194 194 for ctx in repo.set(b'::%ln', pullop.common)
195 195 if ctx.node() != repo.nullid
196 196 ]
197 197 if not kwargs[b'known']:
198 198 # Mercurial serializes an empty list as '' and deserializes it as
199 199 # [''], so delete it instead to avoid handling the empty string on
200 200 # the server.
201 201 del kwargs[b'known']
202 202
203 203
204 204 extensions.wrapfunction(
205 205 exchange, b'_pullbundle2extraprepare', pullbundle2extraprepare
206 206 )
207 207
208 208
209 209 def _narrow(
210 210 ui,
211 211 repo,
212 212 remote,
213 213 commoninc,
214 214 oldincludes,
215 215 oldexcludes,
216 216 newincludes,
217 217 newexcludes,
218 218 force,
219 219 backup,
220 220 ):
221 221 oldmatch = narrowspec.match(repo.root, oldincludes, oldexcludes)
222 222 newmatch = narrowspec.match(repo.root, newincludes, newexcludes)
223 223
224 224 # This is essentially doing "hg outgoing" to find all local-only
225 225 # commits. We will then check that the local-only commits don't
226 226 # have any changes to files that will be untracked.
227 227 unfi = repo.unfiltered()
228 228 outgoing = discovery.findcommonoutgoing(unfi, remote, commoninc=commoninc)
229 229 ui.status(_(b'looking for local changes to affected paths\n'))
230 230 progress = ui.makeprogress(
231 231 topic=_(b'changesets'),
232 232 unit=_(b'changesets'),
233 233 total=len(outgoing.missing) + len(outgoing.excluded),
234 234 )
235 235 localnodes = []
236 236 with progress:
237 237 for n in itertools.chain(outgoing.missing, outgoing.excluded):
238 238 progress.increment()
239 239 if any(oldmatch(f) and not newmatch(f) for f in unfi[n].files()):
240 240 localnodes.append(n)
241 241 revstostrip = unfi.revs(b'descendants(%ln)', localnodes)
242 242 hiddenrevs = repoview.filterrevs(repo, b'visible')
243 243 visibletostrip = list(
244 244 repo.changelog.node(r) for r in (revstostrip - hiddenrevs)
245 245 )
246 246 if visibletostrip:
247 247 ui.status(
248 248 _(
249 249 b'The following changeset(s) or their ancestors have '
250 250 b'local changes not on the remote:\n'
251 251 )
252 252 )
253 253 maxnodes = 10
254 254 if ui.verbose or len(visibletostrip) <= maxnodes:
255 255 for n in visibletostrip:
256 256 ui.status(b'%s\n' % short(n))
257 257 else:
258 258 for n in visibletostrip[:maxnodes]:
259 259 ui.status(b'%s\n' % short(n))
260 260 ui.status(
261 261 _(b'...and %d more, use --verbose to list all\n')
262 262 % (len(visibletostrip) - maxnodes)
263 263 )
264 264 if not force:
265 265 raise error.StateError(
266 266 _(b'local changes found'),
267 267 hint=_(b'use --force-delete-local-changes to ignore'),
268 268 )
269 269
270 270 with ui.uninterruptible():
271 271 if revstostrip:
272 272 tostrip = [unfi.changelog.node(r) for r in revstostrip]
273 273 if repo[b'.'].node() in tostrip:
274 274 # stripping working copy, so move to a different commit first
275 275 urev = max(
276 276 repo.revs(
277 277 b'(::%n) - %ln + null',
278 278 repo[b'.'].node(),
279 279 visibletostrip,
280 280 )
281 281 )
282 282 hg.clean(repo, urev)
283 283 overrides = {(b'devel', b'strip-obsmarkers'): False}
284 284 if backup:
285 285 ui.status(_(b'moving unwanted changesets to backup\n'))
286 286 else:
287 287 ui.status(_(b'deleting unwanted changesets\n'))
288 288 with ui.configoverride(overrides, b'narrow'):
289 289 repair.strip(ui, unfi, tostrip, topic=b'narrow', backup=backup)
290 290
291 291 todelete = []
292 292 for t, f, f2, size in repo.store.datafiles():
293 293 if f.startswith(b'data/'):
294 294 file = f[5:-2]
295 295 if not newmatch(file):
296 296 todelete.append(f)
297 297 elif f.startswith(b'meta/'):
298 298 dir = f[5:-13]
299 299 dirs = sorted(pathutil.dirs({dir})) + [dir]
300 300 include = True
301 301 for d in dirs:
302 302 visit = newmatch.visitdir(d)
303 303 if not visit:
304 304 include = False
305 305 break
306 306 if visit == b'all':
307 307 break
308 308 if not include:
309 309 todelete.append(f)
310 310
311 311 repo.destroying()
312 312
313 313 with repo.transaction(b'narrowing'):
314 314 # Update narrowspec before removing revlogs, so repo won't be
315 315 # corrupt in case of crash
316 316 repo.setnarrowpats(newincludes, newexcludes)
317 317
318 318 for f in todelete:
319 319 ui.status(_(b'deleting %s\n') % f)
320 320 util.unlinkpath(repo.svfs.join(f))
321 321 repo.store.markremoved(f)
322 322
323 323 ui.status(_(b'deleting unwanted files from working copy\n'))
324 narrowspec.updateworkingcopy(repo, assumeclean=True)
325 narrowspec.copytoworkingcopy(repo)
324 with repo.dirstate.parentchange():
325 narrowspec.updateworkingcopy(repo, assumeclean=True)
326 narrowspec.copytoworkingcopy(repo)
326 327
327 328 repo.destroyed()
328 329
329 330
330 331 def _widen(
331 332 ui,
332 333 repo,
333 334 remote,
334 335 commoninc,
335 336 oldincludes,
336 337 oldexcludes,
337 338 newincludes,
338 339 newexcludes,
339 340 ):
340 341 # for now we assume that if a server has ellipses enabled, we will be
341 342 # exchanging ellipses nodes. In future we should add ellipses as a client
342 343 # side requirement (maybe) to distinguish a client is shallow or not and
343 344 # then send that information to server whether we want ellipses or not.
344 345 # Theoretically a non-ellipses repo should be able to use narrow
345 346 # functionality from an ellipses enabled server
346 347 remotecap = remote.capabilities()
347 348 ellipsesremote = any(
348 349 cap in remotecap for cap in wireprototypes.SUPPORTED_ELLIPSESCAP
349 350 )
350 351
351 352 # check whether we are talking to a server which supports old version of
352 353 # ellipses capabilities
353 354 isoldellipses = (
354 355 ellipsesremote
355 356 and wireprototypes.ELLIPSESCAP1 in remotecap
356 357 and wireprototypes.ELLIPSESCAP not in remotecap
357 358 )
358 359
359 360 def pullbundle2extraprepare_widen(orig, pullop, kwargs):
360 361 orig(pullop, kwargs)
361 362 # The old{in,ex}cludepats have already been set by orig()
362 363 kwargs[b'includepats'] = newincludes
363 364 kwargs[b'excludepats'] = newexcludes
364 365
365 366 wrappedextraprepare = extensions.wrappedfunction(
366 367 exchange, b'_pullbundle2extraprepare', pullbundle2extraprepare_widen
367 368 )
368 369
369 370 # define a function that narrowbundle2 can call after creating the
370 371 # backup bundle, but before applying the bundle from the server
371 372 def setnewnarrowpats():
372 373 repo.setnarrowpats(newincludes, newexcludes)
373 374
374 375 repo.setnewnarrowpats = setnewnarrowpats
375 376 # silence the devel-warning of applying an empty changegroup
376 377 overrides = {(b'devel', b'all-warnings'): False}
377 378
378 379 common = commoninc[0]
379 380 with ui.uninterruptible():
380 381 if ellipsesremote:
381 382 ds = repo.dirstate
382 383 p1, p2 = ds.p1(), ds.p2()
383 384 with ds.parentchange():
384 385 ds.setparents(repo.nullid, repo.nullid)
385 386 if isoldellipses:
386 387 with wrappedextraprepare:
387 388 exchange.pull(repo, remote, heads=common)
388 389 else:
389 390 known = []
390 391 if ellipsesremote:
391 392 known = [
392 393 ctx.node()
393 394 for ctx in repo.set(b'::%ln', common)
394 395 if ctx.node() != repo.nullid
395 396 ]
396 397 with remote.commandexecutor() as e:
397 398 bundle = e.callcommand(
398 399 b'narrow_widen',
399 400 {
400 401 b'oldincludes': oldincludes,
401 402 b'oldexcludes': oldexcludes,
402 403 b'newincludes': newincludes,
403 404 b'newexcludes': newexcludes,
404 405 b'cgversion': b'03',
405 406 b'commonheads': common,
406 407 b'known': known,
407 408 b'ellipses': ellipsesremote,
408 409 },
409 410 ).result()
410 411
411 412 trmanager = exchange.transactionmanager(
412 413 repo, b'widen', remote.url()
413 414 )
414 415 with trmanager, repo.ui.configoverride(overrides, b'widen'):
415 416 op = bundle2.bundleoperation(
416 417 repo, trmanager.transaction, source=b'widen'
417 418 )
418 419 # TODO: we should catch error.Abort here
419 420 bundle2.processbundle(repo, bundle, op=op)
420 421
421 422 if ellipsesremote:
422 423 with ds.parentchange():
423 424 ds.setparents(p1, p2)
424 425
425 with repo.transaction(b'widening'):
426 with repo.transaction(b'widening'), repo.dirstate.parentchange():
426 427 repo.setnewnarrowpats()
427 428 narrowspec.updateworkingcopy(repo)
428 429 narrowspec.copytoworkingcopy(repo)
429 430
430 431
431 432 # TODO(rdamazio): Make new matcher format and update description
432 433 @command(
433 434 b'tracked',
434 435 [
435 436 (b'', b'addinclude', [], _(b'new paths to include')),
436 437 (b'', b'removeinclude', [], _(b'old paths to no longer include')),
437 438 (
438 439 b'',
439 440 b'auto-remove-includes',
440 441 False,
441 442 _(b'automatically choose unused includes to remove'),
442 443 ),
443 444 (b'', b'addexclude', [], _(b'new paths to exclude')),
444 445 (b'', b'import-rules', b'', _(b'import narrowspecs from a file')),
445 446 (b'', b'removeexclude', [], _(b'old paths to no longer exclude')),
446 447 (
447 448 b'',
448 449 b'clear',
449 450 False,
450 451 _(b'whether to replace the existing narrowspec'),
451 452 ),
452 453 (
453 454 b'',
454 455 b'force-delete-local-changes',
455 456 False,
456 457 _(b'forces deletion of local changes when narrowing'),
457 458 ),
458 459 (
459 460 b'',
460 461 b'backup',
461 462 True,
462 463 _(b'back up local changes when narrowing'),
463 464 ),
464 465 (
465 466 b'',
466 467 b'update-working-copy',
467 468 False,
468 469 _(b'update working copy when the store has changed'),
469 470 ),
470 471 ]
471 472 + commands.remoteopts,
472 473 _(b'[OPTIONS]... [REMOTE]'),
473 474 inferrepo=True,
474 475 helpcategory=command.CATEGORY_MAINTENANCE,
475 476 )
476 477 def trackedcmd(ui, repo, remotepath=None, *pats, **opts):
477 478 """show or change the current narrowspec
478 479
479 480 With no argument, shows the current narrowspec entries, one per line. Each
480 481 line will be prefixed with 'I' or 'X' for included or excluded patterns,
481 482 respectively.
482 483
483 484 The narrowspec is comprised of expressions to match remote files and/or
484 485 directories that should be pulled into your client.
485 486 The narrowspec has *include* and *exclude* expressions, with excludes always
486 487 trumping includes: that is, if a file matches an exclude expression, it will
487 488 be excluded even if it also matches an include expression.
488 489 Excluding files that were never included has no effect.
489 490
490 491 Each included or excluded entry is in the format described by
491 492 'hg help patterns'.
492 493
493 494 The options allow you to add or remove included and excluded expressions.
494 495
495 496 If --clear is specified, then all previous includes and excludes are DROPPED
496 497 and replaced by the new ones specified to --addinclude and --addexclude.
497 498 If --clear is specified without any further options, the narrowspec will be
498 499 empty and will not match any files.
499 500
500 501 If --auto-remove-includes is specified, then those includes that don't match
501 502 any files modified by currently visible local commits (those not shared by
502 503 the remote) will be added to the set of explicitly specified includes to
503 504 remove.
504 505
505 506 --import-rules accepts a path to a file containing rules, allowing you to
506 507 add --addinclude, --addexclude rules in bulk. Like the other include and
507 508 exclude switches, the changes are applied immediately.
508 509 """
509 510 opts = pycompat.byteskwargs(opts)
510 511 if requirements.NARROW_REQUIREMENT not in repo.requirements:
511 512 raise error.InputError(
512 513 _(
513 514 b'the tracked command is only supported on '
514 515 b'repositories cloned with --narrow'
515 516 )
516 517 )
517 518
518 519 # Before supporting, decide whether it "hg tracked --clear" should mean
519 520 # tracking no paths or all paths.
520 521 if opts[b'clear']:
521 522 raise error.InputError(_(b'the --clear option is not yet supported'))
522 523
523 524 # import rules from a file
524 525 newrules = opts.get(b'import_rules')
525 526 if newrules:
526 527 try:
527 528 filepath = os.path.join(encoding.getcwd(), newrules)
528 529 fdata = util.readfile(filepath)
529 530 except IOError as inst:
530 531 raise error.StorageError(
531 532 _(b"cannot read narrowspecs from '%s': %s")
532 533 % (filepath, encoding.strtolocal(inst.strerror))
533 534 )
534 535 includepats, excludepats, profiles = sparse.parseconfig(
535 536 ui, fdata, b'narrow'
536 537 )
537 538 if profiles:
538 539 raise error.InputError(
539 540 _(
540 541 b"including other spec files using '%include' "
541 542 b"is not supported in narrowspec"
542 543 )
543 544 )
544 545 opts[b'addinclude'].extend(includepats)
545 546 opts[b'addexclude'].extend(excludepats)
546 547
547 548 addedincludes = narrowspec.parsepatterns(opts[b'addinclude'])
548 549 removedincludes = narrowspec.parsepatterns(opts[b'removeinclude'])
549 550 addedexcludes = narrowspec.parsepatterns(opts[b'addexclude'])
550 551 removedexcludes = narrowspec.parsepatterns(opts[b'removeexclude'])
551 552 autoremoveincludes = opts[b'auto_remove_includes']
552 553
553 554 update_working_copy = opts[b'update_working_copy']
554 555 only_show = not (
555 556 addedincludes
556 557 or removedincludes
557 558 or addedexcludes
558 559 or removedexcludes
559 560 or newrules
560 561 or autoremoveincludes
561 562 or update_working_copy
562 563 )
563 564
564 565 oldincludes, oldexcludes = repo.narrowpats
565 566
566 567 # filter the user passed additions and deletions into actual additions and
567 568 # deletions of excludes and includes
568 569 addedincludes -= oldincludes
569 570 removedincludes &= oldincludes
570 571 addedexcludes -= oldexcludes
571 572 removedexcludes &= oldexcludes
572 573
573 574 widening = addedincludes or removedexcludes
574 575 narrowing = removedincludes or addedexcludes
575 576
576 577 # Only print the current narrowspec.
577 578 if only_show:
578 579 ui.pager(b'tracked')
579 580 fm = ui.formatter(b'narrow', opts)
580 581 for i in sorted(oldincludes):
581 582 fm.startitem()
582 583 fm.write(b'status', b'%s ', b'I', label=b'narrow.included')
583 584 fm.write(b'pat', b'%s\n', i, label=b'narrow.included')
584 585 for i in sorted(oldexcludes):
585 586 fm.startitem()
586 587 fm.write(b'status', b'%s ', b'X', label=b'narrow.excluded')
587 588 fm.write(b'pat', b'%s\n', i, label=b'narrow.excluded')
588 589 fm.end()
589 590 return 0
590 591
591 592 if update_working_copy:
592 with repo.wlock(), repo.lock(), repo.transaction(b'narrow-wc'):
593 with repo.wlock(), repo.lock(), repo.transaction(
594 b'narrow-wc'
595 ), repo.dirstate.parentchange():
593 596 narrowspec.updateworkingcopy(repo)
594 597 narrowspec.copytoworkingcopy(repo)
595 598 return 0
596 599
597 600 if not (widening or narrowing or autoremoveincludes):
598 601 ui.status(_(b"nothing to widen or narrow\n"))
599 602 return 0
600 603
601 604 with repo.wlock(), repo.lock():
602 605 cmdutil.bailifchanged(repo)
603 606
604 607 # Find the revisions we have in common with the remote. These will
605 608 # be used for finding local-only changes for narrowing. They will
606 609 # also define the set of revisions to update for widening.
607 610 r = urlutil.get_unique_pull_path(b'tracked', repo, ui, remotepath)
608 611 url, branches = r
609 612 ui.status(_(b'comparing with %s\n') % urlutil.hidepassword(url))
610 613 remote = hg.peer(repo, opts, url)
611 614
612 615 try:
613 616 # check narrow support before doing anything if widening needs to be
614 617 # performed. In future we should also abort if client is ellipses and
615 618 # server does not support ellipses
616 619 if (
617 620 widening
618 621 and wireprototypes.NARROWCAP not in remote.capabilities()
619 622 ):
620 623 raise error.Abort(_(b"server does not support narrow clones"))
621 624
622 625 commoninc = discovery.findcommonincoming(repo, remote)
623 626
624 627 if autoremoveincludes:
625 628 outgoing = discovery.findcommonoutgoing(
626 629 repo, remote, commoninc=commoninc
627 630 )
628 631 ui.status(_(b'looking for unused includes to remove\n'))
629 632 localfiles = set()
630 633 for n in itertools.chain(outgoing.missing, outgoing.excluded):
631 634 localfiles.update(repo[n].files())
632 635 suggestedremovals = []
633 636 for include in sorted(oldincludes):
634 637 match = narrowspec.match(repo.root, [include], oldexcludes)
635 638 if not any(match(f) for f in localfiles):
636 639 suggestedremovals.append(include)
637 640 if suggestedremovals:
638 641 for s in suggestedremovals:
639 642 ui.status(b'%s\n' % s)
640 643 if (
641 644 ui.promptchoice(
642 645 _(
643 646 b'remove these unused includes (yn)?'
644 647 b'$$ &Yes $$ &No'
645 648 )
646 649 )
647 650 == 0
648 651 ):
649 652 removedincludes.update(suggestedremovals)
650 653 narrowing = True
651 654 else:
652 655 ui.status(_(b'found no unused includes\n'))
653 656
654 657 if narrowing:
655 658 newincludes = oldincludes - removedincludes
656 659 newexcludes = oldexcludes | addedexcludes
657 660 _narrow(
658 661 ui,
659 662 repo,
660 663 remote,
661 664 commoninc,
662 665 oldincludes,
663 666 oldexcludes,
664 667 newincludes,
665 668 newexcludes,
666 669 opts[b'force_delete_local_changes'],
667 670 opts[b'backup'],
668 671 )
669 672 # _narrow() updated the narrowspec and _widen() below needs to
670 673 # use the updated values as its base (otherwise removed includes
671 674 # and addedexcludes will be lost in the resulting narrowspec)
672 675 oldincludes = newincludes
673 676 oldexcludes = newexcludes
674 677
675 678 if widening:
676 679 newincludes = oldincludes | addedincludes
677 680 newexcludes = oldexcludes - removedexcludes
678 681 _widen(
679 682 ui,
680 683 repo,
681 684 remote,
682 685 commoninc,
683 686 oldincludes,
684 687 oldexcludes,
685 688 newincludes,
686 689 newexcludes,
687 690 )
688 691 finally:
689 692 remote.close()
690 693
691 694 return 0
General Comments 0
You need to be logged in to leave comments. Login now