diff --git a/dvc/ignore.py b/dvc/ignore.py
--- a/dvc/ignore.py
+++ b/dvc/ignore.py
@@ -92,13 +92,14 @@ def is_dvc_repo(directory):
 
 
 class DvcIgnoreFilter:
-    def __init__(self, tree):
+    def __init__(self, tree, root_dir):
         self.tree = tree
+        self.root_dir = root_dir
         self.ignores = {
             DvcIgnoreDirs([".git", ".hg", ".dvc"]),
             DvcIgnoreRepo(),
         }
-        for root, dirs, files in self.tree.walk(self.tree.tree_root):
+        for root, dirs, files in self.tree.walk(self.root_dir):
             self._update(root)
             dirs[:], files[:] = self(root, dirs, files)
 
@@ -124,7 +125,7 @@ def __init__(self, tree, tree_root=None):
 
     @cached_property
     def dvcignore(self):
-        return DvcIgnoreFilter(self.tree)
+        return DvcIgnoreFilter(self.tree, self.tree_root)
 
     @property
     def tree_root(self):

diff --git a/tests/func/test_ignore.py b/tests/func/test_ignore.py
--- a/tests/func/test_ignore.py
+++ b/tests/func/test_ignore.py
@@ -11,6 +11,7 @@
     DvcIgnoreRepo,
 )
 from dvc.remote import LocalRemote
+from dvc.repo import Repo
 from dvc.scm.tree import WorkingTree
 from dvc.utils import relpath
 from dvc.utils.fs import get_mtime_and_size
@@ -151,3 +152,20 @@ def test_ignore_external(tmp_dir, scm, dvc, tmp_path_factory):
     remote = LocalRemote(dvc, {})
     result = {relpath(f, ext_dir) for f in remote.walk_files(ext_dir)}
     assert result == {"y.backup", "tmp"}
+
+
+def test_ignore_subrepo(tmp_dir, scm, dvc):
+    tmp_dir.gen({".dvcignore": "foo", "subdir": {"foo": "foo"}})
+    scm.add([".dvcignore"])
+    scm.commit("init parent dvcignore")
+
+    subrepo_dir = tmp_dir / "subdir"
+    assert not dvc.tree.exists(subrepo_dir / "foo")
+
+    with subrepo_dir.chdir():
+        subrepo = Repo.init(subdir=True)
+        scm.add(str(subrepo_dir / "foo"))
+        scm.commit("subrepo init")
+
+    for _ in subrepo.brancher(all_commits=True):
+        assert subrepo.tree.exists(subrepo_dir / "foo")

brancher: dvcignore does not work as expected for subrepos when using brancher
As noted by @efiop in #3883, in CleanTree when we do
```py
    @cached_property
    def dvcignore(self):
        return DvcIgnoreFilter(self.tree)
```
`DvcIgnoreFilter` will use `WorkingTree.tree_root` or `GitTree.tree_root` as the root DVC repo directory.

For DVC subrepos, if the tree is a GitTree from brancher, this causes dvcignore to use the host/parent repo root `.dvcignore` rather than the subrepo root `.dvcignore`, since `GitTree.tree_root` will be the (parent/host level) git/scm root directory and not the DVC subrepo directory. For working trees this is not an issue, since the working tree root will be set to the correct `subrepo.root_dir`.

Some investigation should also be done to verify whether or not this issue (using host DVC repo root instead of subrepo root for brancher GitTrees) affects other places in DVC as well.

tests/func/test_ignore.py::test_ignore_subrepo
tests/func/test_ignore.py::test_ignore_on_branch tests/func/test_ignore.py::test_rename_file tests/func/test_ignore.py::test_ignore tests/func/test_ignore.py::test_match_nested tests/func/test_ignore.py::test_rename_ignored_file tests/func/test_ignore.py::test_remove_file tests/func/test_ignore.py::test_ignore_collecting_dvcignores[dir/subdir] tests/func/test_ignore.py::test_ignore_collecting_dvcignores[dir] tests/func/test_ignore.py::test_ignore_external tests/func/test_ignore.py::test_ignore_unicode tests/func/test_ignore.py::test_remove_ignored_file