comparison mercurial/sparse.py @ 33555:6755b719048c

sparse: consolidate common code for writing sparse config In 3 functions we were writing the sparse config and updating the working directory. In two of them we had a transaction-like process for restoring the sparse config in case of wdir update fail. Because the pattern is common, we've already made mistakes, and the complexity will increase in the near future, let's consolidate the code into a reusable function. As part of this refactor, we end up reading the "sparse" file twice when updating it. This is a bit sub-optimal. But I don't think it is worth the code complexity to pass around the variables to avoid the redundancy. Differential Revision: https://phab.mercurial-scm.org/D109
author Gregory Szorc <gregory.szorc@gmail.com>
date Mon, 17 Jul 2017 11:21:23 -0700
parents 1d1779734c99
children 22371eabb3b1
comparison
equal deleted inserted replaced
33554:2943141f5e07 33555:6755b719048c
519 origsparsematch = matcher(repo) 519 origsparsematch = matcher(repo)
520 refreshwdir(repo, origstatus, origsparsematch, force=True) 520 refreshwdir(repo, origstatus, origsparsematch, force=True)
521 521
522 prunetemporaryincludes(repo) 522 prunetemporaryincludes(repo)
523 523
524 def _updateconfigandrefreshwdir(repo, includes, excludes, profiles,
525 force=False):
526 """Update the sparse config and working directory state."""
527 raw = repo.vfs.tryread('sparse')
528 oldincludes, oldexcludes, oldprofiles = parseconfig(repo.ui, raw)
529
530 oldstatus = repo.status()
531 oldmatch = matcher(repo)
532
533 # TODO remove this try..except once the matcher integrates better
534 # with dirstate. We currently have to write the updated config
535 # because that will invalidate the matcher cache and force a
536 # re-read. We ideally want to update the cached matcher on the
537 # repo instance then flush the new config to disk once wdir is
538 # updated. But this requires massive rework to matcher() and its
539 # consumers.
540
541 writeconfig(repo, includes, excludes, profiles)
542
543 try:
544 return refreshwdir(repo, oldstatus, oldmatch, force=force)
545 except Exception:
546 writeconfig(repo, oldincludes, oldexcludes, oldprofiles)
547 raise
548
524 def clearrules(repo, force=False): 549 def clearrules(repo, force=False):
525 """Clears include/exclude rules from the sparse config. 550 """Clears include/exclude rules from the sparse config.
526 551
527 The remaining sparse config only has profiles, if defined. The working 552 The remaining sparse config only has profiles, if defined. The working
528 directory is refreshed, as needed. 553 directory is refreshed, as needed.
532 includes, excludes, profiles = parseconfig(repo.ui, raw) 557 includes, excludes, profiles = parseconfig(repo.ui, raw)
533 558
534 if not includes and not excludes: 559 if not includes and not excludes:
535 return 560 return
536 561
537 oldstatus = repo.status() 562 _updateconfigandrefreshwdir(repo, set(), set(), profiles, force=force)
538 oldmatch = matcher(repo)
539 writeconfig(repo, set(), set(), profiles)
540 refreshwdir(repo, oldstatus, oldmatch, force=force)
541 563
542 def importfromfiles(repo, opts, paths, force=False): 564 def importfromfiles(repo, opts, paths, force=False):
543 """Import sparse config rules from files. 565 """Import sparse config rules from files.
544 566
545 The updated sparse config is written out and the working directory 567 The updated sparse config is written out and the working directory
546 is refreshed, as needed. 568 is refreshed, as needed.
547 """ 569 """
548 with repo.wlock(): 570 with repo.wlock():
549 # read current configuration 571 # read current configuration
550 raw = repo.vfs.tryread('sparse') 572 raw = repo.vfs.tryread('sparse')
551 oincludes, oexcludes, oprofiles = parseconfig(repo.ui, raw) 573 includes, excludes, profiles = parseconfig(repo.ui, raw)
552 includes, excludes, profiles = map(
553 set, (oincludes, oexcludes, oprofiles))
554
555 aincludes, aexcludes, aprofiles = activeconfig(repo) 574 aincludes, aexcludes, aprofiles = activeconfig(repo)
556 575
557 # Import rules on top; only take in rules that are not yet 576 # Import rules on top; only take in rules that are not yet
558 # part of the active rules. 577 # part of the active rules.
559 changed = False 578 changed = False
575 if changed: 594 if changed:
576 profilecount = len(profiles - aprofiles) 595 profilecount = len(profiles - aprofiles)
577 includecount = len(includes - aincludes) 596 includecount = len(includes - aincludes)
578 excludecount = len(excludes - aexcludes) 597 excludecount = len(excludes - aexcludes)
579 598
580 oldstatus = repo.status() 599 fcounts = map(len, _updateconfigandrefreshwdir(
581 oldsparsematch = matcher(repo) 600 repo, includes, excludes, profiles, force=force))
582
583 # TODO remove this try..except once the matcher integrates better
584 # with dirstate. We currently have to write the updated config
585 # because that will invalidate the matcher cache and force a
586 # re-read. We ideally want to update the cached matcher on the
587 # repo instance then flush the new config to disk once wdir is
588 # updated. But this requires massive rework to matcher() and its
589 # consumers.
590 writeconfig(repo, includes, excludes, profiles)
591
592 try:
593 fcounts = map(
594 len,
595 refreshwdir(repo, oldstatus, oldsparsematch, force=force))
596 except Exception:
597 writeconfig(repo, oincludes, oexcludes, oprofiles)
598 raise
599 601
600 printchanges(repo.ui, opts, profilecount, includecount, excludecount, 602 printchanges(repo.ui, opts, profilecount, includecount, excludecount,
601 *fcounts) 603 *fcounts)
602 604
603 def updateconfig(repo, pats, opts, include=False, exclude=False, reset=False, 605 def updateconfig(repo, pats, opts, include=False, exclude=False, reset=False,
608 Only one of the actions may be performed. 610 Only one of the actions may be performed.
609 611
610 The new config is written out and a working directory refresh is performed. 612 The new config is written out and a working directory refresh is performed.
611 """ 613 """
612 with repo.wlock(): 614 with repo.wlock():
613 oldmatcher = matcher(repo)
614
615 raw = repo.vfs.tryread('sparse') 615 raw = repo.vfs.tryread('sparse')
616 oldinclude, oldexclude, oldprofiles = parseconfig(repo.ui, raw) 616 oldinclude, oldexclude, oldprofiles = parseconfig(repo.ui, raw)
617 617
618 if reset: 618 if reset:
619 newinclude = set() 619 newinclude = set()
621 newprofiles = set() 621 newprofiles = set()
622 else: 622 else:
623 newinclude = set(oldinclude) 623 newinclude = set(oldinclude)
624 newexclude = set(oldexclude) 624 newexclude = set(oldexclude)
625 newprofiles = set(oldprofiles) 625 newprofiles = set(oldprofiles)
626
627 oldstatus = repo.status()
628 626
629 if any(pat.startswith('/') for pat in pats): 627 if any(pat.startswith('/') for pat in pats):
630 repo.ui.warn(_('warning: paths cannot start with /, ignoring: %s\n') 628 repo.ui.warn(_('warning: paths cannot start with /, ignoring: %s\n')
631 % ([pat for pat in pats if pat.startswith('/')])) 629 % ([pat for pat in pats if pat.startswith('/')]))
632 elif include: 630 elif include:
646 includecount = (len(newinclude - oldinclude) - 644 includecount = (len(newinclude - oldinclude) -
647 len(oldinclude - newinclude)) 645 len(oldinclude - newinclude))
648 excludecount = (len(newexclude - oldexclude) - 646 excludecount = (len(newexclude - oldexclude) -
649 len(oldexclude - newexclude)) 647 len(oldexclude - newexclude))
650 648
651 # TODO clean up this writeconfig() + try..except pattern once we can. 649 fcounts = map(len, _updateconfigandrefreshwdir(
652 # See comment in importfromfiles() explaining it. 650 repo, newinclude, newexclude, newprofiles, force=force))
653 writeconfig(repo, newinclude, newexclude, newprofiles) 651
654 652 printchanges(repo.ui, opts, profilecount, includecount,
655 try: 653 excludecount, *fcounts)
656 fcounts = map(
657 len,
658 refreshwdir(repo, oldstatus, oldmatcher, force=force))
659
660 printchanges(repo.ui, opts, profilecount, includecount,
661 excludecount, *fcounts)
662 except Exception:
663 writeconfig(repo, oldinclude, oldexclude, oldprofiles)
664 raise
665 654
666 def printchanges(ui, opts, profilecount=0, includecount=0, excludecount=0, 655 def printchanges(ui, opts, profilecount=0, includecount=0, excludecount=0,
667 added=0, dropped=0, conflicting=0): 656 added=0, dropped=0, conflicting=0):
668 """Print output summarizing sparse config changes.""" 657 """Print output summarizing sparse config changes."""
669 with ui.formatter('sparse', opts) as fm: 658 with ui.formatter('sparse', opts) as fm: