diff --git a/hgext/narrow/narrowcommands.py b/hgext/narrow/narrowcommands.py --- a/hgext/narrow/narrowcommands.py +++ b/hgext/narrow/narrowcommands.py @@ -22,6 +22,7 @@ from mercurial import ( hg, narrowspec, node, + pathutil, pycompat, registrar, repair, @@ -277,7 +278,7 @@ def _narrow( todelete.append(f) elif f.startswith(b'meta/'): dir = f[5:-13] - dirs = sorted(util.dirs({dir})) + [dir] + dirs = sorted(pathutil.dirs({dir})) + [dir] include = True for d in dirs: visit = newmatch.visitdir(d) diff --git a/hgext/uncommit.py b/hgext/uncommit.py --- a/hgext/uncommit.py +++ b/hgext/uncommit.py @@ -29,11 +29,11 @@ from mercurial import ( error, node, obsutil, + pathutil, pycompat, registrar, rewriteutil, scmutil, - util, ) cmdtable = {} @@ -185,7 +185,7 @@ def uncommit(ui, repo, *pats, **opts): # if not everything tracked in that directory can be # uncommitted. if badfiles: - badfiles -= {f for f in util.dirs(eligible)} + badfiles -= {f for f in pathutil.dirs(eligible)} for f in sorted(badfiles): if f in s.clean: diff --git a/mercurial/cmdutil.py b/mercurial/cmdutil.py --- a/mercurial/cmdutil.py +++ b/mercurial/cmdutil.py @@ -2606,7 +2606,7 @@ def remove( progress.complete() # warn about failure to delete explicit files/dirs - deleteddirs = util.dirs(deleted) + deleteddirs = pathutil.dirs(deleted) files = m.files() progress = ui.makeprogress( _(b'deleting'), total=len(files), unit=_(b'files') diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py --- a/mercurial/dirstate.py +++ b/mercurial/dirstate.py @@ -1514,11 +1514,11 @@ class dirstatemap(object): @propertycache def _dirs(self): - return util.dirs(self._map, b'r') + return pathutil.dirs(self._map, b'r') @propertycache def _alldirs(self): - return util.dirs(self._map) + return pathutil.dirs(self._map) def _opendirstatefile(self): fp, mode = txnutil.trypending(self._root, self._opener, self._filename) diff --git a/mercurial/manifest.py b/mercurial/manifest.py --- a/mercurial/manifest.py +++ b/mercurial/manifest.py @@ -23,6 +23,7 @@ from .pycompat import getattr from . import ( error, mdiff, + pathutil, policy, pycompat, revlog, @@ -494,7 +495,7 @@ class manifestdict(object): @propertycache def _dirs(self): - return util.dirs(self) + return pathutil.dirs(self) def dirs(self): return self._dirs @@ -1104,7 +1105,7 @@ class treemanifest(object): @propertycache def _alldirs(self): - return util.dirs(self) + return pathutil.dirs(self) def dirs(self): return self._alldirs diff --git a/mercurial/match.py b/mercurial/match.py --- a/mercurial/match.py +++ b/mercurial/match.py @@ -18,6 +18,7 @@ from . import ( encoding, error, pathutil, + pathutil, policy, pycompat, util, @@ -598,7 +599,7 @@ class patternmatcher(basematcher): @propertycache def _dirs(self): - return set(util.dirs(self._fileset)) + return set(pathutil.dirs(self._fileset)) def visitdir(self, dir): dir = normalizerootdir(dir, b'visitdir') @@ -629,9 +630,9 @@ class patternmatcher(basematcher): return b'' % pycompat.bytestr(self._pats) -# This is basically a reimplementation of util.dirs that stores the children -# instead of just a count of them, plus a small optional optimization to avoid -# some directories we don't need. +# This is basically a reimplementation of pathutil.dirs that stores the +# children instead of just a count of them, plus a small optional optimization +# to avoid some directories we don't need. class _dirchildren(object): def __init__(self, paths, onlyinclude=None): self._dirs = {} @@ -763,7 +764,7 @@ class exactmatcher(basematcher): @propertycache def _dirs(self): - return set(util.dirs(self._fileset)) + return set(pathutil.dirs(self._fileset)) def visitdir(self, dir): dir = normalizerootdir(dir, b'visitdir') @@ -1510,8 +1511,8 @@ def _rootsdirsandparents(kindpats): p = set() # Add the parents as non-recursive/exact directories, since they must be # scanned to get to either the roots or the other exact directories. - p.update(util.dirs(d)) - p.update(util.dirs(r)) + p.update(pathutil.dirs(d)) + p.update(pathutil.dirs(r)) # FIXME: all uses of this function convert these to sets, do so before # returning. diff --git a/mercurial/pathutil.py b/mercurial/pathutil.py --- a/mercurial/pathutil.py +++ b/mercurial/pathutil.py @@ -9,10 +9,14 @@ from .i18n import _ from . import ( encoding, error, + policy, pycompat, util, ) +rustdirs = policy.importrust('dirstate', 'Dirs') +parsers = policy.importmod('parsers') + def _lowerclean(s): return encoding.hfsignoreclean(s.lower()) @@ -271,6 +275,58 @@ def normasprefix(path): return path +class dirs(object): + '''a multiset of directory names from a set of file paths''' + + def __init__(self, map, skip=None): + self._dirs = {} + addpath = self.addpath + if isinstance(map, dict) and skip is not None: + for f, s in pycompat.iteritems(map): + if s[0] != skip: + addpath(f) + elif skip is not None: + raise error.ProgrammingError( + b"skip character is only supported with a dict source" + ) + else: + for f in map: + addpath(f) + + def addpath(self, path): + dirs = self._dirs + for base in util.finddirs(path): + if base.endswith(b'/'): + raise ValueError( + "found invalid consecutive slashes in path: %r" % base + ) + if base in dirs: + dirs[base] += 1 + return + dirs[base] = 1 + + def delpath(self, path): + dirs = self._dirs + for base in util.finddirs(path): + if dirs[base] > 1: + dirs[base] -= 1 + return + del dirs[base] + + def __iter__(self): + return iter(self._dirs) + + def __contains__(self, d): + return d in self._dirs + + +if util.safehasattr(parsers, 'dirs'): + dirs = parsers.dirs + +if rustdirs is not None: + dirs = rustdirs + + # forward two methods from posixpath that do what we need, but we'd # rather not let our internals know that we're thinking in posix terms # - instead we'll let them be oblivious. diff --git a/mercurial/repair.py b/mercurial/repair.py --- a/mercurial/repair.py +++ b/mercurial/repair.py @@ -24,6 +24,7 @@ from . import ( exchange, obsolete, obsutil, + pathutil, phases, pycompat, util, @@ -476,7 +477,7 @@ def rebuildfncache(ui, repo): if b'treemanifest' in repo.requirements: # This logic is safe if treemanifest isn't enabled, but also # pointless, so we skip it if treemanifest isn't enabled. - for dir in util.dirs(seenfiles): + for dir in pathutil.dirs(seenfiles): i = b'meta/%s/00manifest.i' % dir d = b'meta/%s/00manifest.d' % dir diff --git a/mercurial/util.py b/mercurial/util.py --- a/mercurial/util.py +++ b/mercurial/util.py @@ -57,11 +57,8 @@ from .utils import ( stringutil, ) -rustdirs = policy.importrust('dirstate', 'Dirs') - base85 = policy.importmod('base85') osutil = policy.importmod('osutil') -parsers = policy.importmod('parsers') b85decode = base85.b85decode b85encode = base85.b85encode @@ -3494,58 +3491,6 @@ def debugstacktrace( f.flush() -class dirs(object): - '''a multiset of directory names from a dirstate or manifest''' - - def __init__(self, map, skip=None): - self._dirs = {} - addpath = self.addpath - if isinstance(map, dict) and skip is not None: - for f, s in pycompat.iteritems(map): - if s[0] != skip: - addpath(f) - elif skip is not None: - raise error.ProgrammingError( - b"skip character is only supported with a dict source" - ) - else: - for f in map: - addpath(f) - - def addpath(self, path): - dirs = self._dirs - for base in finddirs(path): - if base.endswith(b'/'): - raise ValueError( - "found invalid consecutive slashes in path: %r" % base - ) - if base in dirs: - dirs[base] += 1 - return - dirs[base] = 1 - - def delpath(self, path): - dirs = self._dirs - for base in finddirs(path): - if dirs[base] > 1: - dirs[base] -= 1 - return - del dirs[base] - - def __iter__(self): - return iter(self._dirs) - - def __contains__(self, d): - return d in self._dirs - - -if safehasattr(parsers, 'dirs'): - dirs = parsers.dirs - -if rustdirs is not None: - dirs = rustdirs - - def finddirs(path): pos = path.rfind(b'/') while pos != -1: diff --git a/tests/test-dirs.py b/tests/test-dirs.py --- a/tests/test-dirs.py +++ b/tests/test-dirs.py @@ -4,7 +4,7 @@ import unittest import silenttestrunner -from mercurial import util +from mercurial import pathutil class dirstests(unittest.TestCase): @@ -13,13 +13,13 @@ class dirstests(unittest.TestCase): (b'a/a/a', [b'a', b'a/a', b'']), (b'alpha/beta/gamma', [b'', b'alpha', b'alpha/beta']), ]: - d = util.dirs({}) + d = pathutil.dirs({}) d.addpath(case) self.assertEqual(sorted(d), sorted(want)) def testinvalid(self): with self.assertRaises(ValueError): - d = util.dirs({}) + d = pathutil.dirs({}) d.addpath(b'a//b')