diff --git a/mercurial/match.py b/mercurial/match.py --- a/mercurial/match.py +++ b/mercurial/match.py @@ -86,17 +86,25 @@ class match(object): self._always = False self._pathrestricted = bool(include or exclude or patterns) self._warn = warn + self._includeroots = set() + self._includedirs = set(['.']) + self._excluderoots = set() matchfns = [] if include: kindpats = self._normalize(include, 'glob', root, cwd, auditor) self.includepat, im = _buildmatch(ctx, kindpats, '(?:/|$)', listsubrepos) + self._includeroots.update(_roots(kindpats)) + self._includeroots.discard('.') + self._includedirs.update(util.dirs(self._includeroots)) matchfns.append(im) if exclude: kindpats = self._normalize(exclude, 'glob', root, cwd, auditor) self.excludepat, em = _buildmatch(ctx, kindpats, '(?:/|$)', listsubrepos) + self._excluderoots.update(_roots(kindpats)) + self._excluderoots.discard('.') matchfns.append(lambda f: not em(f)) if exact: if isinstance(patterns, list): @@ -177,10 +185,25 @@ class match(object): return set(util.dirs(self._fileroots)) | set(['.']) def visitdir(self, dir): + '''Decides whether a directory should be visited based on whether it + has potential matches in it or one of its subdirectories. This is + based on the match's primary, included, and excluded patterns. + + This function's behavior is undefined if it has returned False for + one of the dir's parent directories. + ''' + if dir in self._excluderoots: + return False + parentdirs = None + if (self._includeroots and dir not in self._includeroots and + dir not in self._includedirs): + parentdirs = util.finddirs(dir) + if not any(parent in self._includeroots for parent in parentdirs): + return False return (not self._fileroots or '.' in self._fileroots or dir in self._fileroots or dir in self._dirs or any(parentdir in self._fileroots - for parentdir in util.finddirs(dir))) + for parentdir in parentdirs or util.finddirs(dir))) def exact(self, f): '''Returns True if f is in .files().''' diff --git a/tests/test-treemanifest.t b/tests/test-treemanifest.t --- a/tests/test-treemanifest.t +++ b/tests/test-treemanifest.t @@ -280,3 +280,100 @@ Turning off treemanifest config has no e 0 0 125 0 4 63c9c0557d24 000000000000 000000000000 1 125 109 0 5 23d12a1f6e0e 000000000000 000000000000 2 234 55 0 6 3cb2d87b4250 23d12a1f6e0e 000000000000 + +Create deeper repo with tree manifests. + + $ cd .. + $ hg --config experimental.treemanifest=True init deeprepo + $ cd deeprepo + + $ mkdir a + $ mkdir b + $ mkdir b/bar + $ mkdir b/bar/orange + $ mkdir b/bar/orange/fly + $ mkdir b/foo + $ mkdir b/foo/apple + $ mkdir b/foo/apple/bees + + $ touch a/one.txt + $ touch a/two.txt + $ touch b/bar/fruits.txt + $ touch b/bar/orange/fly/gnat.py + $ touch b/bar/orange/fly/housefly.txt + $ touch b/foo/apple/bees/flower.py + $ touch c.txt + $ touch d.py + + $ hg ci -Aqm 'initial' + +We'll see that visitdir works by removing some treemanifest revlogs and running +the files command with various parameters. + +Test files from the root. + + $ hg files -r . + a/one.txt + a/two.txt + b/bar/fruits.txt + b/bar/orange/fly/gnat.py + b/bar/orange/fly/housefly.txt + b/foo/apple/bees/flower.py + c.txt + d.py + +Test files for a subdirectory. + + $ mv .hg/store/meta/a oldmf + $ hg files -r . b + b/bar/fruits.txt + b/bar/orange/fly/gnat.py + b/bar/orange/fly/housefly.txt + b/foo/apple/bees/flower.py + $ mv oldmf .hg/store/meta/a + +Test files with just includes and excludes. + + $ mv .hg/store/meta/a oldmf + $ mv .hg/store/meta/b/bar/orange/fly oldmf2 + $ mv .hg/store/meta/b/foo/apple/bees oldmf3 + $ hg files -r . -I b/bar -X b/bar/orange/fly -I b/foo -X b/foo/apple/bees + b/bar/fruits.txt + $ mv oldmf .hg/store/meta/a + $ mv oldmf2 .hg/store/meta/b/bar/orange/fly + $ mv oldmf3 .hg/store/meta/b/foo/apple/bees + +Test files for a subdirectory, excluding a directory within it. + + $ mv .hg/store/meta/a oldmf + $ mv .hg/store/meta/b/foo oldmf2 + $ hg files -r . -X b/foo b + b/bar/fruits.txt + b/bar/orange/fly/gnat.py + b/bar/orange/fly/housefly.txt + $ mv oldmf .hg/store/meta/a + $ mv oldmf2 .hg/store/meta/b/foo + +Test files for a sub directory, including only a directory within it, and +including an unrelated directory. + + $ mv .hg/store/meta/a oldmf + $ mv .hg/store/meta/b/foo oldmf2 + $ hg files -r . -I b/bar/orange -I a b + b/bar/orange/fly/gnat.py + b/bar/orange/fly/housefly.txt + $ mv oldmf .hg/store/meta/a + $ mv oldmf2 .hg/store/meta/b/foo + +Test files for a pattern, including a directory, and excluding a directory +within that. + + $ mv .hg/store/meta/a oldmf + $ mv .hg/store/meta/b/foo oldmf2 + $ mv .hg/store/meta/b/bar/orange oldmf3 + $ hg files -r . glob:**.txt -I b/bar -X b/bar/orange + b/bar/fruits.txt + $ mv oldmf .hg/store/meta/a + $ mv oldmf2 .hg/store/meta/b/foo + $ mv oldmf3 .hg/store/meta/b/bar/orange +