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