comparison mercurial/sparse.py @ 43076:2372284d9457

formatting: blacken the codebase This is using my patch to black (https://github.com/psf/black/pull/826) so we don't un-wrap collection literals. Done with: hg files 'set:**.py - mercurial/thirdparty/** - "contrib/python-zstandard/**"' | xargs black -S # skip-blame mass-reformatting only # no-check-commit reformats foo_bar functions Differential Revision: https://phab.mercurial-scm.org/D6971
author Augie Fackler <augie@google.com>
date Sun, 06 Oct 2019 09:45:02 -0400
parents 87a34c767384
children 687b865b95ad
comparison
equal deleted inserted replaced
43075:57875cf423c9 43076:2372284d9457
28 # Whether sparse features are enabled. This variable is intended to be 28 # Whether sparse features are enabled. This variable is intended to be
29 # temporary to facilitate porting sparse to core. It should eventually be 29 # temporary to facilitate porting sparse to core. It should eventually be
30 # a per-repo option, possibly a repo requirement. 30 # a per-repo option, possibly a repo requirement.
31 enabled = False 31 enabled = False
32 32
33
33 def parseconfig(ui, raw, action): 34 def parseconfig(ui, raw, action):
34 """Parse sparse config file content. 35 """Parse sparse config file content.
35 36
36 action is the command which is trigerring this read, can be narrow, sparse 37 action is the command which is trigerring this read, can be narrow, sparse
37 38
53 if line: 54 if line:
54 profiles.add(line) 55 profiles.add(line)
55 elif line == '[include]': 56 elif line == '[include]':
56 if havesection and current != includes: 57 if havesection and current != includes:
57 # TODO pass filename into this API so we can report it. 58 # TODO pass filename into this API so we can report it.
58 raise error.Abort(_('%(action)s config cannot have includes ' 59 raise error.Abort(
59 'after excludes') % {'action': action}) 60 _(
61 '%(action)s config cannot have includes '
62 'after excludes'
63 )
64 % {'action': action}
65 )
60 havesection = True 66 havesection = True
61 current = includes 67 current = includes
62 continue 68 continue
63 elif line == '[exclude]': 69 elif line == '[exclude]':
64 havesection = True 70 havesection = True
65 current = excludes 71 current = excludes
66 elif line: 72 elif line:
67 if current is None: 73 if current is None:
68 raise error.Abort(_('%(action)s config entry outside of ' 74 raise error.Abort(
69 'section: %(line)s') 75 _('%(action)s config entry outside of ' 'section: %(line)s')
70 % {'action': action, 'line': line}, 76 % {'action': action, 'line': line},
71 hint=_('add an [include] or [exclude] line ' 77 hint=_(
72 'to declare the entry type')) 78 'add an [include] or [exclude] line '
79 'to declare the entry type'
80 ),
81 )
73 82
74 if line.strip().startswith('/'): 83 if line.strip().startswith('/'):
75 ui.warn(_('warning: %(action)s profile cannot use' 84 ui.warn(
76 ' paths starting with /, ignoring %(line)s\n') 85 _(
77 % {'action': action, 'line': line}) 86 'warning: %(action)s profile cannot use'
87 ' paths starting with /, ignoring %(line)s\n'
88 )
89 % {'action': action, 'line': line}
90 )
78 continue 91 continue
79 current.add(line) 92 current.add(line)
80 93
81 return includes, excludes, profiles 94 return includes, excludes, profiles
95
82 96
83 # Exists as separate function to facilitate monkeypatching. 97 # Exists as separate function to facilitate monkeypatching.
84 def readprofile(repo, profile, changeid): 98 def readprofile(repo, profile, changeid):
85 """Resolve the raw content of a sparse profile file.""" 99 """Resolve the raw content of a sparse profile file."""
86 # TODO add some kind of cache here because this incurs a manifest 100 # TODO add some kind of cache here because this incurs a manifest
87 # resolve and can be slow. 101 # resolve and can be slow.
88 return repo.filectx(profile, changeid=changeid).data() 102 return repo.filectx(profile, changeid=changeid).data()
89 103
104
90 def patternsforrev(repo, rev): 105 def patternsforrev(repo, rev):
91 """Obtain sparse checkout patterns for the given rev. 106 """Obtain sparse checkout patterns for the given rev.
92 107
93 Returns a tuple of iterables representing includes, excludes, and 108 Returns a tuple of iterables representing includes, excludes, and
94 patterns. 109 patterns.
100 raw = repo.vfs.tryread('sparse') 115 raw = repo.vfs.tryread('sparse')
101 if not raw: 116 if not raw:
102 return set(), set(), set() 117 return set(), set(), set()
103 118
104 if rev is None: 119 if rev is None:
105 raise error.Abort(_('cannot parse sparse patterns from working ' 120 raise error.Abort(
106 'directory')) 121 _('cannot parse sparse patterns from working ' 'directory')
122 )
107 123
108 includes, excludes, profiles = parseconfig(repo.ui, raw, 'sparse') 124 includes, excludes, profiles = parseconfig(repo.ui, raw, 'sparse')
109 ctx = repo[rev] 125 ctx = repo[rev]
110 126
111 if profiles: 127 if profiles:
120 try: 136 try:
121 raw = readprofile(repo, profile, rev) 137 raw = readprofile(repo, profile, rev)
122 except error.ManifestLookupError: 138 except error.ManifestLookupError:
123 msg = ( 139 msg = (
124 "warning: sparse profile '%s' not found " 140 "warning: sparse profile '%s' not found "
125 "in rev %s - ignoring it\n" % (profile, ctx)) 141 "in rev %s - ignoring it\n" % (profile, ctx)
142 )
126 # experimental config: sparse.missingwarning 143 # experimental config: sparse.missingwarning
127 if repo.ui.configbool( 144 if repo.ui.configbool('sparse', 'missingwarning'):
128 'sparse', 'missingwarning'):
129 repo.ui.warn(msg) 145 repo.ui.warn(msg)
130 else: 146 else:
131 repo.ui.debug(msg) 147 repo.ui.debug(msg)
132 continue 148 continue
133 149
141 if includes: 157 if includes:
142 includes.add('.hg*') 158 includes.add('.hg*')
143 159
144 return includes, excludes, profiles 160 return includes, excludes, profiles
145 161
162
146 def activeconfig(repo): 163 def activeconfig(repo):
147 """Determine the active sparse config rules. 164 """Determine the active sparse config rules.
148 165
149 Rules are constructed by reading the current sparse config and bringing in 166 Rules are constructed by reading the current sparse config and bringing in
150 referenced profiles from parents of the working directory. 167 referenced profiles from parents of the working directory.
151 """ 168 """
152 revs = [repo.changelog.rev(node) for node in 169 revs = [
153 repo.dirstate.parents() if node != nullid] 170 repo.changelog.rev(node)
171 for node in repo.dirstate.parents()
172 if node != nullid
173 ]
154 174
155 allincludes = set() 175 allincludes = set()
156 allexcludes = set() 176 allexcludes = set()
157 allprofiles = set() 177 allprofiles = set()
158 178
162 allexcludes |= excludes 182 allexcludes |= excludes
163 allprofiles |= profiles 183 allprofiles |= profiles
164 184
165 return allincludes, allexcludes, allprofiles 185 return allincludes, allexcludes, allprofiles
166 186
187
167 def configsignature(repo, includetemp=True): 188 def configsignature(repo, includetemp=True):
168 """Obtain the signature string for the current sparse configuration. 189 """Obtain the signature string for the current sparse configuration.
169 190
170 This is used to construct a cache key for matchers. 191 This is used to construct a cache key for matchers.
171 """ 192 """
186 raw = repo.vfs.tryread('tempsparse') 207 raw = repo.vfs.tryread('tempsparse')
187 tempsignature = hex(hashlib.sha1(raw).digest()) 208 tempsignature = hex(hashlib.sha1(raw).digest())
188 cache['tempsignature'] = tempsignature 209 cache['tempsignature'] = tempsignature
189 210
190 return '%s %s' % (signature, tempsignature) 211 return '%s %s' % (signature, tempsignature)
212
191 213
192 def writeconfig(repo, includes, excludes, profiles): 214 def writeconfig(repo, includes, excludes, profiles):
193 """Write the sparse config file given a sparse configuration.""" 215 """Write the sparse config file given a sparse configuration."""
194 with repo.vfs('sparse', 'wb') as fh: 216 with repo.vfs('sparse', 'wb') as fh:
195 for p in sorted(profiles): 217 for p in sorted(profiles):
207 fh.write(e) 229 fh.write(e)
208 fh.write('\n') 230 fh.write('\n')
209 231
210 repo._sparsesignaturecache.clear() 232 repo._sparsesignaturecache.clear()
211 233
234
212 def readtemporaryincludes(repo): 235 def readtemporaryincludes(repo):
213 raw = repo.vfs.tryread('tempsparse') 236 raw = repo.vfs.tryread('tempsparse')
214 if not raw: 237 if not raw:
215 return set() 238 return set()
216 239
217 return set(raw.split('\n')) 240 return set(raw.split('\n'))
218 241
242
219 def writetemporaryincludes(repo, includes): 243 def writetemporaryincludes(repo, includes):
220 repo.vfs.write('tempsparse', '\n'.join(sorted(includes))) 244 repo.vfs.write('tempsparse', '\n'.join(sorted(includes)))
221 repo._sparsesignaturecache.clear() 245 repo._sparsesignaturecache.clear()
246
222 247
223 def addtemporaryincludes(repo, additional): 248 def addtemporaryincludes(repo, additional):
224 includes = readtemporaryincludes(repo) 249 includes = readtemporaryincludes(repo)
225 for i in additional: 250 for i in additional:
226 includes.add(i) 251 includes.add(i)
227 writetemporaryincludes(repo, includes) 252 writetemporaryincludes(repo, includes)
253
228 254
229 def prunetemporaryincludes(repo): 255 def prunetemporaryincludes(repo):
230 if not enabled or not repo.vfs.exists('tempsparse'): 256 if not enabled or not repo.vfs.exists('tempsparse'):
231 return 257 return
232 258
246 actions.append((file, None, message)) 272 actions.append((file, None, message))
247 dropped.append(file) 273 dropped.append(file)
248 274
249 typeactions = mergemod.emptyactions() 275 typeactions = mergemod.emptyactions()
250 typeactions['r'] = actions 276 typeactions['r'] = actions
251 mergemod.applyupdates(repo, typeactions, repo[None], repo['.'], False, 277 mergemod.applyupdates(
252 wantfiledata=False) 278 repo, typeactions, repo[None], repo['.'], False, wantfiledata=False
279 )
253 280
254 # Fix dirstate 281 # Fix dirstate
255 for file in dropped: 282 for file in dropped:
256 dirstate.drop(file) 283 dirstate.drop(file)
257 284
258 repo.vfs.unlink('tempsparse') 285 repo.vfs.unlink('tempsparse')
259 repo._sparsesignaturecache.clear() 286 repo._sparsesignaturecache.clear()
260 msg = _('cleaned up %d temporarily added file(s) from the ' 287 msg = _(
261 'sparse checkout\n') 288 'cleaned up %d temporarily added file(s) from the ' 'sparse checkout\n'
289 )
262 repo.ui.status(msg % len(tempincludes)) 290 repo.ui.status(msg % len(tempincludes))
291
263 292
264 def forceincludematcher(matcher, includes): 293 def forceincludematcher(matcher, includes):
265 """Returns a matcher that returns true for any of the forced includes 294 """Returns a matcher that returns true for any of the forced includes
266 before testing against the actual matcher.""" 295 before testing against the actual matcher."""
267 kindpats = [('path', include, '') for include in includes] 296 kindpats = [('path', include, '') for include in includes]
268 includematcher = matchmod.includematcher('', kindpats) 297 includematcher = matchmod.includematcher('', kindpats)
269 return matchmod.unionmatcher([includematcher, matcher]) 298 return matchmod.unionmatcher([includematcher, matcher])
270 299
300
271 def matcher(repo, revs=None, includetemp=True): 301 def matcher(repo, revs=None, includetemp=True):
272 """Obtain a matcher for sparse working directories for the given revs. 302 """Obtain a matcher for sparse working directories for the given revs.
273 303
274 If multiple revisions are specified, the matcher is the union of all 304 If multiple revisions are specified, the matcher is the union of all
275 revs. 305 revs.
279 # If sparse isn't enabled, sparse matcher matches everything. 309 # If sparse isn't enabled, sparse matcher matches everything.
280 if not enabled: 310 if not enabled:
281 return matchmod.always() 311 return matchmod.always()
282 312
283 if not revs or revs == [None]: 313 if not revs or revs == [None]:
284 revs = [repo.changelog.rev(node) 314 revs = [
285 for node in repo.dirstate.parents() if node != nullid] 315 repo.changelog.rev(node)
316 for node in repo.dirstate.parents()
317 if node != nullid
318 ]
286 319
287 signature = configsignature(repo, includetemp=includetemp) 320 signature = configsignature(repo, includetemp=includetemp)
288 321
289 key = '%s %s' % (signature, ' '.join(map(pycompat.bytestr, revs))) 322 key = '%s %s' % (signature, ' '.join(map(pycompat.bytestr, revs)))
290 323
296 for rev in revs: 329 for rev in revs:
297 try: 330 try:
298 includes, excludes, profiles = patternsforrev(repo, rev) 331 includes, excludes, profiles = patternsforrev(repo, rev)
299 332
300 if includes or excludes: 333 if includes or excludes:
301 matcher = matchmod.match(repo.root, '', [], 334 matcher = matchmod.match(
302 include=includes, exclude=excludes, 335 repo.root,
303 default='relpath') 336 '',
337 [],
338 include=includes,
339 exclude=excludes,
340 default='relpath',
341 )
304 matchers.append(matcher) 342 matchers.append(matcher)
305 except IOError: 343 except IOError:
306 pass 344 pass
307 345
308 if not matchers: 346 if not matchers:
317 result = forceincludematcher(result, tempincludes) 355 result = forceincludematcher(result, tempincludes)
318 356
319 repo._sparsematchercache[key] = result 357 repo._sparsematchercache[key] = result
320 358
321 return result 359 return result
360
322 361
323 def filterupdatesactions(repo, wctx, mctx, branchmerge, actions): 362 def filterupdatesactions(repo, wctx, mctx, branchmerge, actions):
324 """Filter updates to only lay out files that match the sparse rules.""" 363 """Filter updates to only lay out files that match the sparse rules."""
325 if not enabled: 364 if not enabled:
326 return actions 365 return actions
365 f1, f2, fa, move, anc = args 404 f1, f2, fa, move, anc = args
366 if not sparsematch(f1): 405 if not sparsematch(f1):
367 temporaryfiles.append(f1) 406 temporaryfiles.append(f1)
368 407
369 if len(temporaryfiles) > 0: 408 if len(temporaryfiles) > 0:
370 repo.ui.status(_('temporarily included %d file(s) in the sparse ' 409 repo.ui.status(
371 'checkout for merging\n') % len(temporaryfiles)) 410 _(
411 'temporarily included %d file(s) in the sparse '
412 'checkout for merging\n'
413 )
414 % len(temporaryfiles)
415 )
372 addtemporaryincludes(repo, temporaryfiles) 416 addtemporaryincludes(repo, temporaryfiles)
373 417
374 # Add the new files to the working copy so they can be merged, etc 418 # Add the new files to the working copy so they can be merged, etc
375 actions = [] 419 actions = []
376 message = 'temporarily adding to sparse checkout' 420 message = 'temporarily adding to sparse checkout'
380 fctx = repo[None][file] 424 fctx = repo[None][file]
381 actions.append((file, (fctx.flags(), False), message)) 425 actions.append((file, (fctx.flags(), False), message))
382 426
383 typeactions = mergemod.emptyactions() 427 typeactions = mergemod.emptyactions()
384 typeactions['g'] = actions 428 typeactions['g'] = actions
385 mergemod.applyupdates(repo, typeactions, repo[None], repo['.'], 429 mergemod.applyupdates(
386 False, wantfiledata=False) 430 repo, typeactions, repo[None], repo['.'], False, wantfiledata=False
431 )
387 432
388 dirstate = repo.dirstate 433 dirstate = repo.dirstate
389 for file, flags, msg in actions: 434 for file, flags, msg in actions:
390 dirstate.normal(file) 435 dirstate.normal(file)
391 436
405 elif old and not new: 450 elif old and not new:
406 prunedactions[file] = ('r', [], '') 451 prunedactions[file] = ('r', [], '')
407 452
408 return prunedactions 453 return prunedactions
409 454
455
410 def refreshwdir(repo, origstatus, origsparsematch, force=False): 456 def refreshwdir(repo, origstatus, origsparsematch, force=False):
411 """Refreshes working directory by taking sparse config into account. 457 """Refreshes working directory by taking sparse config into account.
412 458
413 The old status and sparse matcher is compared against the current sparse 459 The old status and sparse matcher is compared against the current sparse
414 matcher. 460 matcher.
428 if not sparsematch(f): 474 if not sparsematch(f):
429 repo.ui.warn(_("pending changes to '%s'\n") % f) 475 repo.ui.warn(_("pending changes to '%s'\n") % f)
430 abort = not force 476 abort = not force
431 477
432 if abort: 478 if abort:
433 raise error.Abort(_('could not update sparseness due to pending ' 479 raise error.Abort(
434 'changes')) 480 _('could not update sparseness due to pending ' 'changes')
481 )
435 482
436 # Calculate actions 483 # Calculate actions
437 dirstate = repo.dirstate 484 dirstate = repo.dirstate
438 ctx = repo['.'] 485 ctx = repo['.']
439 added = [] 486 added = []
468 abort = False 515 abort = False
469 for file in lookup: 516 for file in lookup:
470 repo.ui.warn(_("pending changes to '%s'\n") % file) 517 repo.ui.warn(_("pending changes to '%s'\n") % file)
471 abort = not force 518 abort = not force
472 if abort: 519 if abort:
473 raise error.Abort(_('cannot change sparseness due to pending ' 520 raise error.Abort(
474 'changes (delete the files or use ' 521 _(
475 '--force to bring them back dirty)')) 522 'cannot change sparseness due to pending '
523 'changes (delete the files or use '
524 '--force to bring them back dirty)'
525 )
526 )
476 527
477 # Check for files that were only in the dirstate. 528 # Check for files that were only in the dirstate.
478 for file, state in dirstate.iteritems(): 529 for file, state in dirstate.iteritems():
479 if not file in files: 530 if not file in files:
480 old = origsparsematch(file) 531 old = origsparsematch(file)
485 # Apply changes to disk 536 # Apply changes to disk
486 typeactions = mergemod.emptyactions() 537 typeactions = mergemod.emptyactions()
487 for f, (m, args, msg) in actions.iteritems(): 538 for f, (m, args, msg) in actions.iteritems():
488 typeactions[m].append((f, args, msg)) 539 typeactions[m].append((f, args, msg))
489 540
490 mergemod.applyupdates(repo, typeactions, repo[None], repo['.'], False, 541 mergemod.applyupdates(
491 wantfiledata=False) 542 repo, typeactions, repo[None], repo['.'], False, wantfiledata=False
543 )
492 544
493 # Fix dirstate 545 # Fix dirstate
494 for file in added: 546 for file in added:
495 dirstate.normal(file) 547 dirstate.normal(file)
496 548
500 for file in lookup: 552 for file in lookup:
501 # File exists on disk, and we're bringing it back in an unknown state. 553 # File exists on disk, and we're bringing it back in an unknown state.
502 dirstate.normallookup(file) 554 dirstate.normallookup(file)
503 555
504 return added, dropped, lookup 556 return added, dropped, lookup
557
505 558
506 def aftercommit(repo, node): 559 def aftercommit(repo, node):
507 """Perform actions after a working directory commit.""" 560 """Perform actions after a working directory commit."""
508 # This function is called unconditionally, even if sparse isn't 561 # This function is called unconditionally, even if sparse isn't
509 # enabled. 562 # enabled.
517 origsparsematch = matcher(repo) 570 origsparsematch = matcher(repo)
518 refreshwdir(repo, origstatus, origsparsematch, force=True) 571 refreshwdir(repo, origstatus, origsparsematch, force=True)
519 572
520 prunetemporaryincludes(repo) 573 prunetemporaryincludes(repo)
521 574
522 def _updateconfigandrefreshwdir(repo, includes, excludes, profiles, 575
523 force=False, removing=False): 576 def _updateconfigandrefreshwdir(
577 repo, includes, excludes, profiles, force=False, removing=False
578 ):
524 """Update the sparse config and working directory state.""" 579 """Update the sparse config and working directory state."""
525 raw = repo.vfs.tryread('sparse') 580 raw = repo.vfs.tryread('sparse')
526 oldincludes, oldexcludes, oldprofiles = parseconfig(repo.ui, raw, 'sparse') 581 oldincludes, oldexcludes, oldprofiles = parseconfig(repo.ui, raw, 'sparse')
527 582
528 oldstatus = repo.status() 583 oldstatus = repo.status()
553 repo.requirements |= oldrequires 608 repo.requirements |= oldrequires
554 scmutil.writerequires(repo.vfs, repo.requirements) 609 scmutil.writerequires(repo.vfs, repo.requirements)
555 writeconfig(repo, oldincludes, oldexcludes, oldprofiles) 610 writeconfig(repo, oldincludes, oldexcludes, oldprofiles)
556 raise 611 raise
557 612
613
558 def clearrules(repo, force=False): 614 def clearrules(repo, force=False):
559 """Clears include/exclude rules from the sparse config. 615 """Clears include/exclude rules from the sparse config.
560 616
561 The remaining sparse config only has profiles, if defined. The working 617 The remaining sparse config only has profiles, if defined. The working
562 directory is refreshed, as needed. 618 directory is refreshed, as needed.
567 623
568 if not includes and not excludes: 624 if not includes and not excludes:
569 return 625 return
570 626
571 _updateconfigandrefreshwdir(repo, set(), set(), profiles, force=force) 627 _updateconfigandrefreshwdir(repo, set(), set(), profiles, force=force)
628
572 629
573 def importfromfiles(repo, opts, paths, force=False): 630 def importfromfiles(repo, opts, paths, force=False):
574 """Import sparse config rules from files. 631 """Import sparse config rules from files.
575 632
576 The updated sparse config is written out and the working directory 633 The updated sparse config is written out and the working directory
587 changed = False 644 changed = False
588 for p in paths: 645 for p in paths:
589 with util.posixfile(util.expandpath(p), mode='rb') as fh: 646 with util.posixfile(util.expandpath(p), mode='rb') as fh:
590 raw = fh.read() 647 raw = fh.read()
591 648
592 iincludes, iexcludes, iprofiles = parseconfig(repo.ui, raw, 649 iincludes, iexcludes, iprofiles = parseconfig(
593 'sparse') 650 repo.ui, raw, 'sparse'
651 )
594 oldsize = len(includes) + len(excludes) + len(profiles) 652 oldsize = len(includes) + len(excludes) + len(profiles)
595 includes.update(iincludes - aincludes) 653 includes.update(iincludes - aincludes)
596 excludes.update(iexcludes - aexcludes) 654 excludes.update(iexcludes - aexcludes)
597 profiles.update(iprofiles - aprofiles) 655 profiles.update(iprofiles - aprofiles)
598 if len(includes) + len(excludes) + len(profiles) > oldsize: 656 if len(includes) + len(excludes) + len(profiles) > oldsize:
604 if changed: 662 if changed:
605 profilecount = len(profiles - aprofiles) 663 profilecount = len(profiles - aprofiles)
606 includecount = len(includes - aincludes) 664 includecount = len(includes - aincludes)
607 excludecount = len(excludes - aexcludes) 665 excludecount = len(excludes - aexcludes)
608 666
609 fcounts = map(len, _updateconfigandrefreshwdir( 667 fcounts = map(
610 repo, includes, excludes, profiles, force=force)) 668 len,
611 669 _updateconfigandrefreshwdir(
612 printchanges(repo.ui, opts, profilecount, includecount, excludecount, 670 repo, includes, excludes, profiles, force=force
613 *fcounts) 671 ),
614 672 )
615 def updateconfig(repo, pats, opts, include=False, exclude=False, reset=False, 673
616 delete=False, enableprofile=False, disableprofile=False, 674 printchanges(
617 force=False, usereporootpaths=False): 675 repo.ui, opts, profilecount, includecount, excludecount, *fcounts
676 )
677
678
679 def updateconfig(
680 repo,
681 pats,
682 opts,
683 include=False,
684 exclude=False,
685 reset=False,
686 delete=False,
687 enableprofile=False,
688 disableprofile=False,
689 force=False,
690 usereporootpaths=False,
691 ):
618 """Perform a sparse config update. 692 """Perform a sparse config update.
619 693
620 Only one of the actions may be performed. 694 Only one of the actions may be performed.
621 695
622 The new config is written out and a working directory refresh is performed. 696 The new config is written out and a working directory refresh is performed.
623 """ 697 """
624 with repo.wlock(): 698 with repo.wlock():
625 raw = repo.vfs.tryread('sparse') 699 raw = repo.vfs.tryread('sparse')
626 oldinclude, oldexclude, oldprofiles = parseconfig(repo.ui, raw, 700 oldinclude, oldexclude, oldprofiles = parseconfig(
627 'sparse') 701 repo.ui, raw, 'sparse'
702 )
628 703
629 if reset: 704 if reset:
630 newinclude = set() 705 newinclude = set()
631 newexclude = set() 706 newexclude = set()
632 newprofiles = set() 707 newprofiles = set()
643 root, cwd = repo.root, repo.getcwd() 718 root, cwd = repo.root, repo.getcwd()
644 abspats = [] 719 abspats = []
645 for kindpat in pats: 720 for kindpat in pats:
646 kind, pat = matchmod._patsplit(kindpat, None) 721 kind, pat = matchmod._patsplit(kindpat, None)
647 if kind in matchmod.cwdrelativepatternkinds or kind is None: 722 if kind in matchmod.cwdrelativepatternkinds or kind is None:
648 ap = ((kind + ':' if kind else '') + 723 ap = (kind + ':' if kind else '') + pathutil.canonpath(
649 pathutil.canonpath(root, cwd, pat)) 724 root, cwd, pat
725 )
650 abspats.append(ap) 726 abspats.append(ap)
651 else: 727 else:
652 abspats.append(kindpat) 728 abspats.append(kindpat)
653 pats = abspats 729 pats = abspats
654 730
662 newprofiles.difference_update(pats) 738 newprofiles.difference_update(pats)
663 elif delete: 739 elif delete:
664 newinclude.difference_update(pats) 740 newinclude.difference_update(pats)
665 newexclude.difference_update(pats) 741 newexclude.difference_update(pats)
666 742
667 profilecount = (len(newprofiles - oldprofiles) - 743 profilecount = len(newprofiles - oldprofiles) - len(
668 len(oldprofiles - newprofiles)) 744 oldprofiles - newprofiles
669 includecount = (len(newinclude - oldinclude) - 745 )
670 len(oldinclude - newinclude)) 746 includecount = len(newinclude - oldinclude) - len(
671 excludecount = (len(newexclude - oldexclude) - 747 oldinclude - newinclude
672 len(oldexclude - newexclude)) 748 )
673 749 excludecount = len(newexclude - oldexclude) - len(
674 fcounts = map(len, _updateconfigandrefreshwdir( 750 oldexclude - newexclude
675 repo, newinclude, newexclude, newprofiles, force=force, 751 )
676 removing=reset)) 752
677 753 fcounts = map(
678 printchanges(repo.ui, opts, profilecount, includecount, 754 len,
679 excludecount, *fcounts) 755 _updateconfigandrefreshwdir(
680 756 repo,
681 def printchanges(ui, opts, profilecount=0, includecount=0, excludecount=0, 757 newinclude,
682 added=0, dropped=0, conflicting=0): 758 newexclude,
759 newprofiles,
760 force=force,
761 removing=reset,
762 ),
763 )
764
765 printchanges(
766 repo.ui, opts, profilecount, includecount, excludecount, *fcounts
767 )
768
769
770 def printchanges(
771 ui,
772 opts,
773 profilecount=0,
774 includecount=0,
775 excludecount=0,
776 added=0,
777 dropped=0,
778 conflicting=0,
779 ):
683 """Print output summarizing sparse config changes.""" 780 """Print output summarizing sparse config changes."""
684 with ui.formatter('sparse', opts) as fm: 781 with ui.formatter('sparse', opts) as fm:
685 fm.startitem() 782 fm.startitem()
686 fm.condwrite(ui.verbose, 'profiles_added', _('Profiles changed: %d\n'), 783 fm.condwrite(
687 profilecount) 784 ui.verbose,
688 fm.condwrite(ui.verbose, 'include_rules_added', 785 'profiles_added',
689 _('Include rules changed: %d\n'), includecount) 786 _('Profiles changed: %d\n'),
690 fm.condwrite(ui.verbose, 'exclude_rules_added', 787 profilecount,
691 _('Exclude rules changed: %d\n'), excludecount) 788 )
789 fm.condwrite(
790 ui.verbose,
791 'include_rules_added',
792 _('Include rules changed: %d\n'),
793 includecount,
794 )
795 fm.condwrite(
796 ui.verbose,
797 'exclude_rules_added',
798 _('Exclude rules changed: %d\n'),
799 excludecount,
800 )
692 801
693 # In 'plain' verbose mode, mergemod.applyupdates already outputs what 802 # In 'plain' verbose mode, mergemod.applyupdates already outputs what
694 # files are added or removed outside of the templating formatter 803 # files are added or removed outside of the templating formatter
695 # framework. No point in repeating ourselves in that case. 804 # framework. No point in repeating ourselves in that case.
696 if not fm.isplain(): 805 if not fm.isplain():
697 fm.condwrite(ui.verbose, 'files_added', _('Files added: %d\n'), 806 fm.condwrite(
698 added) 807 ui.verbose, 'files_added', _('Files added: %d\n'), added
699 fm.condwrite(ui.verbose, 'files_dropped', _('Files dropped: %d\n'), 808 )
700 dropped) 809 fm.condwrite(
701 fm.condwrite(ui.verbose, 'files_conflicting', 810 ui.verbose, 'files_dropped', _('Files dropped: %d\n'), dropped
702 _('Files conflicting: %d\n'), conflicting) 811 )
812 fm.condwrite(
813 ui.verbose,
814 'files_conflicting',
815 _('Files conflicting: %d\n'),
816 conflicting,
817 )