diff -r 57875cf423c9 -r 2372284d9457 mercurial/sparse.py --- a/mercurial/sparse.py Sat Oct 05 10:29:34 2019 -0400 +++ b/mercurial/sparse.py Sun Oct 06 09:45:02 2019 -0400 @@ -30,6 +30,7 @@ # a per-repo option, possibly a repo requirement. enabled = False + def parseconfig(ui, raw, action): """Parse sparse config file content. @@ -55,8 +56,13 @@ elif line == '[include]': if havesection and current != includes: # TODO pass filename into this API so we can report it. - raise error.Abort(_('%(action)s config cannot have includes ' - 'after excludes') % {'action': action}) + raise error.Abort( + _( + '%(action)s config cannot have includes ' + 'after excludes' + ) + % {'action': action} + ) havesection = True current = includes continue @@ -65,21 +71,29 @@ current = excludes elif line: if current is None: - raise error.Abort(_('%(action)s config entry outside of ' - 'section: %(line)s') - % {'action': action, 'line': line}, - hint=_('add an [include] or [exclude] line ' - 'to declare the entry type')) + raise error.Abort( + _('%(action)s config entry outside of ' 'section: %(line)s') + % {'action': action, 'line': line}, + hint=_( + 'add an [include] or [exclude] line ' + 'to declare the entry type' + ), + ) if line.strip().startswith('/'): - ui.warn(_('warning: %(action)s profile cannot use' - ' paths starting with /, ignoring %(line)s\n') - % {'action': action, 'line': line}) + ui.warn( + _( + 'warning: %(action)s profile cannot use' + ' paths starting with /, ignoring %(line)s\n' + ) + % {'action': action, 'line': line} + ) continue current.add(line) return includes, excludes, profiles + # Exists as separate function to facilitate monkeypatching. def readprofile(repo, profile, changeid): """Resolve the raw content of a sparse profile file.""" @@ -87,6 +101,7 @@ # resolve and can be slow. return repo.filectx(profile, changeid=changeid).data() + def patternsforrev(repo, rev): """Obtain sparse checkout patterns for the given rev. @@ -102,8 +117,9 @@ return set(), set(), set() if rev is None: - raise error.Abort(_('cannot parse sparse patterns from working ' - 'directory')) + raise error.Abort( + _('cannot parse sparse patterns from working ' 'directory') + ) includes, excludes, profiles = parseconfig(repo.ui, raw, 'sparse') ctx = repo[rev] @@ -122,10 +138,10 @@ except error.ManifestLookupError: msg = ( "warning: sparse profile '%s' not found " - "in rev %s - ignoring it\n" % (profile, ctx)) + "in rev %s - ignoring it\n" % (profile, ctx) + ) # experimental config: sparse.missingwarning - if repo.ui.configbool( - 'sparse', 'missingwarning'): + if repo.ui.configbool('sparse', 'missingwarning'): repo.ui.warn(msg) else: repo.ui.debug(msg) @@ -143,14 +159,18 @@ return includes, excludes, profiles + def activeconfig(repo): """Determine the active sparse config rules. Rules are constructed by reading the current sparse config and bringing in referenced profiles from parents of the working directory. """ - revs = [repo.changelog.rev(node) for node in - repo.dirstate.parents() if node != nullid] + revs = [ + repo.changelog.rev(node) + for node in repo.dirstate.parents() + if node != nullid + ] allincludes = set() allexcludes = set() @@ -164,6 +184,7 @@ return allincludes, allexcludes, allprofiles + def configsignature(repo, includetemp=True): """Obtain the signature string for the current sparse configuration. @@ -189,6 +210,7 @@ return '%s %s' % (signature, tempsignature) + def writeconfig(repo, includes, excludes, profiles): """Write the sparse config file given a sparse configuration.""" with repo.vfs('sparse', 'wb') as fh: @@ -209,6 +231,7 @@ repo._sparsesignaturecache.clear() + def readtemporaryincludes(repo): raw = repo.vfs.tryread('tempsparse') if not raw: @@ -216,16 +239,19 @@ return set(raw.split('\n')) + def writetemporaryincludes(repo, includes): repo.vfs.write('tempsparse', '\n'.join(sorted(includes))) repo._sparsesignaturecache.clear() + def addtemporaryincludes(repo, additional): includes = readtemporaryincludes(repo) for i in additional: includes.add(i) writetemporaryincludes(repo, includes) + def prunetemporaryincludes(repo): if not enabled or not repo.vfs.exists('tempsparse'): return @@ -248,8 +274,9 @@ typeactions = mergemod.emptyactions() typeactions['r'] = actions - mergemod.applyupdates(repo, typeactions, repo[None], repo['.'], False, - wantfiledata=False) + mergemod.applyupdates( + repo, typeactions, repo[None], repo['.'], False, wantfiledata=False + ) # Fix dirstate for file in dropped: @@ -257,10 +284,12 @@ repo.vfs.unlink('tempsparse') repo._sparsesignaturecache.clear() - msg = _('cleaned up %d temporarily added file(s) from the ' - 'sparse checkout\n') + msg = _( + 'cleaned up %d temporarily added file(s) from the ' 'sparse checkout\n' + ) repo.ui.status(msg % len(tempincludes)) + def forceincludematcher(matcher, includes): """Returns a matcher that returns true for any of the forced includes before testing against the actual matcher.""" @@ -268,6 +297,7 @@ includematcher = matchmod.includematcher('', kindpats) return matchmod.unionmatcher([includematcher, matcher]) + def matcher(repo, revs=None, includetemp=True): """Obtain a matcher for sparse working directories for the given revs. @@ -281,8 +311,11 @@ return matchmod.always() if not revs or revs == [None]: - revs = [repo.changelog.rev(node) - for node in repo.dirstate.parents() if node != nullid] + revs = [ + repo.changelog.rev(node) + for node in repo.dirstate.parents() + if node != nullid + ] signature = configsignature(repo, includetemp=includetemp) @@ -298,9 +331,14 @@ includes, excludes, profiles = patternsforrev(repo, rev) if includes or excludes: - matcher = matchmod.match(repo.root, '', [], - include=includes, exclude=excludes, - default='relpath') + matcher = matchmod.match( + repo.root, + '', + [], + include=includes, + exclude=excludes, + default='relpath', + ) matchers.append(matcher) except IOError: pass @@ -320,6 +358,7 @@ return result + def filterupdatesactions(repo, wctx, mctx, branchmerge, actions): """Filter updates to only lay out files that match the sparse rules.""" if not enabled: @@ -367,8 +406,13 @@ temporaryfiles.append(f1) if len(temporaryfiles) > 0: - repo.ui.status(_('temporarily included %d file(s) in the sparse ' - 'checkout for merging\n') % len(temporaryfiles)) + repo.ui.status( + _( + 'temporarily included %d file(s) in the sparse ' + 'checkout for merging\n' + ) + % len(temporaryfiles) + ) addtemporaryincludes(repo, temporaryfiles) # Add the new files to the working copy so they can be merged, etc @@ -382,8 +426,9 @@ typeactions = mergemod.emptyactions() typeactions['g'] = actions - mergemod.applyupdates(repo, typeactions, repo[None], repo['.'], - False, wantfiledata=False) + mergemod.applyupdates( + repo, typeactions, repo[None], repo['.'], False, wantfiledata=False + ) dirstate = repo.dirstate for file, flags, msg in actions: @@ -407,6 +452,7 @@ return prunedactions + def refreshwdir(repo, origstatus, origsparsematch, force=False): """Refreshes working directory by taking sparse config into account. @@ -430,8 +476,9 @@ abort = not force if abort: - raise error.Abort(_('could not update sparseness due to pending ' - 'changes')) + raise error.Abort( + _('could not update sparseness due to pending ' 'changes') + ) # Calculate actions dirstate = repo.dirstate @@ -470,9 +517,13 @@ repo.ui.warn(_("pending changes to '%s'\n") % file) abort = not force if abort: - raise error.Abort(_('cannot change sparseness due to pending ' - 'changes (delete the files or use ' - '--force to bring them back dirty)')) + raise error.Abort( + _( + 'cannot change sparseness due to pending ' + 'changes (delete the files or use ' + '--force to bring them back dirty)' + ) + ) # Check for files that were only in the dirstate. for file, state in dirstate.iteritems(): @@ -487,8 +538,9 @@ for f, (m, args, msg) in actions.iteritems(): typeactions[m].append((f, args, msg)) - mergemod.applyupdates(repo, typeactions, repo[None], repo['.'], False, - wantfiledata=False) + mergemod.applyupdates( + repo, typeactions, repo[None], repo['.'], False, wantfiledata=False + ) # Fix dirstate for file in added: @@ -503,6 +555,7 @@ return added, dropped, lookup + def aftercommit(repo, node): """Perform actions after a working directory commit.""" # This function is called unconditionally, even if sparse isn't @@ -519,8 +572,10 @@ prunetemporaryincludes(repo) -def _updateconfigandrefreshwdir(repo, includes, excludes, profiles, - force=False, removing=False): + +def _updateconfigandrefreshwdir( + repo, includes, excludes, profiles, force=False, removing=False +): """Update the sparse config and working directory state.""" raw = repo.vfs.tryread('sparse') oldincludes, oldexcludes, oldprofiles = parseconfig(repo.ui, raw, 'sparse') @@ -555,6 +610,7 @@ writeconfig(repo, oldincludes, oldexcludes, oldprofiles) raise + def clearrules(repo, force=False): """Clears include/exclude rules from the sparse config. @@ -570,6 +626,7 @@ _updateconfigandrefreshwdir(repo, set(), set(), profiles, force=force) + def importfromfiles(repo, opts, paths, force=False): """Import sparse config rules from files. @@ -589,8 +646,9 @@ with util.posixfile(util.expandpath(p), mode='rb') as fh: raw = fh.read() - iincludes, iexcludes, iprofiles = parseconfig(repo.ui, raw, - 'sparse') + iincludes, iexcludes, iprofiles = parseconfig( + repo.ui, raw, 'sparse' + ) oldsize = len(includes) + len(excludes) + len(profiles) includes.update(iincludes - aincludes) excludes.update(iexcludes - aexcludes) @@ -606,15 +664,31 @@ includecount = len(includes - aincludes) excludecount = len(excludes - aexcludes) - fcounts = map(len, _updateconfigandrefreshwdir( - repo, includes, excludes, profiles, force=force)) + fcounts = map( + len, + _updateconfigandrefreshwdir( + repo, includes, excludes, profiles, force=force + ), + ) + + printchanges( + repo.ui, opts, profilecount, includecount, excludecount, *fcounts + ) + - printchanges(repo.ui, opts, profilecount, includecount, excludecount, - *fcounts) - -def updateconfig(repo, pats, opts, include=False, exclude=False, reset=False, - delete=False, enableprofile=False, disableprofile=False, - force=False, usereporootpaths=False): +def updateconfig( + repo, + pats, + opts, + include=False, + exclude=False, + reset=False, + delete=False, + enableprofile=False, + disableprofile=False, + force=False, + usereporootpaths=False, +): """Perform a sparse config update. Only one of the actions may be performed. @@ -623,8 +697,9 @@ """ with repo.wlock(): raw = repo.vfs.tryread('sparse') - oldinclude, oldexclude, oldprofiles = parseconfig(repo.ui, raw, - 'sparse') + oldinclude, oldexclude, oldprofiles = parseconfig( + repo.ui, raw, 'sparse' + ) if reset: newinclude = set() @@ -645,8 +720,9 @@ for kindpat in pats: kind, pat = matchmod._patsplit(kindpat, None) if kind in matchmod.cwdrelativepatternkinds or kind is None: - ap = ((kind + ':' if kind else '') + - pathutil.canonpath(root, cwd, pat)) + ap = (kind + ':' if kind else '') + pathutil.canonpath( + root, cwd, pat + ) abspats.append(ap) else: abspats.append(kindpat) @@ -664,39 +740,78 @@ newinclude.difference_update(pats) newexclude.difference_update(pats) - profilecount = (len(newprofiles - oldprofiles) - - len(oldprofiles - newprofiles)) - includecount = (len(newinclude - oldinclude) - - len(oldinclude - newinclude)) - excludecount = (len(newexclude - oldexclude) - - len(oldexclude - newexclude)) + profilecount = len(newprofiles - oldprofiles) - len( + oldprofiles - newprofiles + ) + includecount = len(newinclude - oldinclude) - len( + oldinclude - newinclude + ) + excludecount = len(newexclude - oldexclude) - len( + oldexclude - newexclude + ) - fcounts = map(len, _updateconfigandrefreshwdir( - repo, newinclude, newexclude, newprofiles, force=force, - removing=reset)) + fcounts = map( + len, + _updateconfigandrefreshwdir( + repo, + newinclude, + newexclude, + newprofiles, + force=force, + removing=reset, + ), + ) - printchanges(repo.ui, opts, profilecount, includecount, - excludecount, *fcounts) + printchanges( + repo.ui, opts, profilecount, includecount, excludecount, *fcounts + ) + -def printchanges(ui, opts, profilecount=0, includecount=0, excludecount=0, - added=0, dropped=0, conflicting=0): +def printchanges( + ui, + opts, + profilecount=0, + includecount=0, + excludecount=0, + added=0, + dropped=0, + conflicting=0, +): """Print output summarizing sparse config changes.""" with ui.formatter('sparse', opts) as fm: fm.startitem() - fm.condwrite(ui.verbose, 'profiles_added', _('Profiles changed: %d\n'), - profilecount) - fm.condwrite(ui.verbose, 'include_rules_added', - _('Include rules changed: %d\n'), includecount) - fm.condwrite(ui.verbose, 'exclude_rules_added', - _('Exclude rules changed: %d\n'), excludecount) + fm.condwrite( + ui.verbose, + 'profiles_added', + _('Profiles changed: %d\n'), + profilecount, + ) + fm.condwrite( + ui.verbose, + 'include_rules_added', + _('Include rules changed: %d\n'), + includecount, + ) + fm.condwrite( + ui.verbose, + 'exclude_rules_added', + _('Exclude rules changed: %d\n'), + excludecount, + ) # In 'plain' verbose mode, mergemod.applyupdates already outputs what # files are added or removed outside of the templating formatter # framework. No point in repeating ourselves in that case. if not fm.isplain(): - fm.condwrite(ui.verbose, 'files_added', _('Files added: %d\n'), - added) - fm.condwrite(ui.verbose, 'files_dropped', _('Files dropped: %d\n'), - dropped) - fm.condwrite(ui.verbose, 'files_conflicting', - _('Files conflicting: %d\n'), conflicting) + fm.condwrite( + ui.verbose, 'files_added', _('Files added: %d\n'), added + ) + fm.condwrite( + ui.verbose, 'files_dropped', _('Files dropped: %d\n'), dropped + ) + fm.condwrite( + ui.verbose, + 'files_conflicting', + _('Files conflicting: %d\n'), + conflicting, + )