utils: move the `dirs` definition in pathutil (API)
Before this change, the `dirs` class was accessible through the `mercurial.util`
module. That module is expected to stay free of scm specific content.
The `pathutil` destination has been selection by Martin von Zweigbergk.
This work is part of a refactoring to unify the revlog index and the nodemap.
This unification prepare the use of a persistent nodemap.
Differential Revision: https://phab.mercurial-scm.org/D7311
--- a/hgext/narrow/narrowcommands.py Wed Oct 23 12:15:42 2019 -0700
+++ b/hgext/narrow/narrowcommands.py Wed Nov 06 14:13:19 2019 +0100
@@ -22,6 +22,7 @@
hg,
narrowspec,
node,
+ pathutil,
pycompat,
registrar,
repair,
@@ -277,7 +278,7 @@
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)
--- a/hgext/uncommit.py Wed Oct 23 12:15:42 2019 -0700
+++ b/hgext/uncommit.py Wed Nov 06 14:13:19 2019 +0100
@@ -29,11 +29,11 @@
error,
node,
obsutil,
+ pathutil,
pycompat,
registrar,
rewriteutil,
scmutil,
- util,
)
cmdtable = {}
@@ -185,7 +185,7 @@
# 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:
--- a/mercurial/cmdutil.py Wed Oct 23 12:15:42 2019 -0700
+++ b/mercurial/cmdutil.py Wed Nov 06 14:13:19 2019 +0100
@@ -2606,7 +2606,7 @@
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')
--- a/mercurial/dirstate.py Wed Oct 23 12:15:42 2019 -0700
+++ b/mercurial/dirstate.py Wed Nov 06 14:13:19 2019 +0100
@@ -1514,11 +1514,11 @@
@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)
--- a/mercurial/manifest.py Wed Oct 23 12:15:42 2019 -0700
+++ b/mercurial/manifest.py Wed Nov 06 14:13:19 2019 +0100
@@ -23,6 +23,7 @@
from . import (
error,
mdiff,
+ pathutil,
policy,
pycompat,
revlog,
@@ -494,7 +495,7 @@
@propertycache
def _dirs(self):
- return util.dirs(self)
+ return pathutil.dirs(self)
def dirs(self):
return self._dirs
@@ -1104,7 +1105,7 @@
@propertycache
def _alldirs(self):
- return util.dirs(self)
+ return pathutil.dirs(self)
def dirs(self):
return self._alldirs
--- a/mercurial/match.py Wed Oct 23 12:15:42 2019 -0700
+++ b/mercurial/match.py Wed Nov 06 14:13:19 2019 +0100
@@ -18,6 +18,7 @@
encoding,
error,
pathutil,
+ pathutil,
policy,
pycompat,
util,
@@ -598,7 +599,7 @@
@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 @@
return b'<patternmatcher patterns=%r>' % 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 @@
@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 @@
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.
--- a/mercurial/pathutil.py Wed Oct 23 12:15:42 2019 -0700
+++ b/mercurial/pathutil.py Wed Nov 06 14:13:19 2019 +0100
@@ -9,10 +9,14 @@
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 @@
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.
--- a/mercurial/repair.py Wed Oct 23 12:15:42 2019 -0700
+++ b/mercurial/repair.py Wed Nov 06 14:13:19 2019 +0100
@@ -24,6 +24,7 @@
exchange,
obsolete,
obsutil,
+ pathutil,
phases,
pycompat,
util,
@@ -476,7 +477,7 @@
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
--- a/mercurial/util.py Wed Oct 23 12:15:42 2019 -0700
+++ b/mercurial/util.py Wed Nov 06 14:13:19 2019 +0100
@@ -57,11 +57,8 @@
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 @@
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:
--- a/tests/test-dirs.py Wed Oct 23 12:15:42 2019 -0700
+++ b/tests/test-dirs.py Wed Nov 06 14:13:19 2019 +0100
@@ -4,7 +4,7 @@
import silenttestrunner
-from mercurial import util
+from mercurial import pathutil
class dirstests(unittest.TestCase):
@@ -13,13 +13,13 @@
(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')