Repository: iterative/dvc

Issue:
dvc metrics -T raises exception if there are tags unrelated to the DVC sub-directory
DVC version: 0.94.0
Python version: 3.6.7
Platform: Windows-10-10.0.18362-SP0
Binary: False
Package: pip
Supported remotes: http, https
Filesystem type (workspace): ('NTFS', 'C:\\')

```
NT> dvc metrics show -T
ERROR: unexpected error - [Errno 2] No such file
 
Having any troubles? Hit us up at https://dvc.org/support, we are always happy to help!
NT> dvc metrics show -T -v
2020-05-18 08:19:19,707 DEBUG: Trying to spawn '['c:\\program files\\python36\\python.exe', 'C:\\Program Files\\Python36
\\Scripts\\dvc', 'daemon', '-q', 'updater']'
2020-05-18 08:19:19,778 DEBUG: Spawned '['c:\\program files\\python36\\python.exe', 'C:\\Program Files\\Python36\\Script
s\\dvc', 'daemon', '-q', 'updater']'
2020-05-18 08:19:19,794 DEBUG: PRAGMA user_version;
2020-05-18 08:19:19,794 DEBUG: fetched: [(3,)]
2020-05-18 08:19:19,794 DEBUG: CREATE TABLE IF NOT EXISTS state (inode INTEGER PRIMARY KEY, mtime TEXT NOT NULL, size TE
XT NOT NULL, md5 TEXT NOT NULL, timestamp TEXT NOT NULL)
2020-05-18 08:19:19,825 DEBUG: CREATE TABLE IF NOT EXISTS state_info (count INTEGER)
2020-05-18 08:19:19,825 DEBUG: CREATE TABLE IF NOT EXISTS link_state (path TEXT PRIMARY KEY, inode INTEGER NOT NULL, mti
me TEXT NOT NULL)
2020-05-18 08:19:19,825 DEBUG: INSERT OR IGNORE INTO state_info (count) SELECT 0 WHERE NOT EXISTS (SELECT * FROM state_i
nfo)
2020-05-18 08:19:19,825 DEBUG: PRAGMA user_version = 3;
2020-05-18 08:19:19,965 DEBUG: SELECT count from state_info WHERE rowid=?
2020-05-18 08:19:19,981 DEBUG: fetched: [(38,)]
2020-05-18 08:19:19,981 DEBUG: UPDATE state_info SET count = ? WHERE rowid = ?
2020-05-18 08:19:20,059 ERROR: unexpected error - [Errno 2] No such file
------------------------------------------------------------
Traceback (most recent call last):
  File "c:\program files\python36\lib\site-packages\dvc\main.py", line 49, in main
    ret = cmd.run()
  File "c:\program files\python36\lib\site-packages\dvc\command\metrics.py", line 49, in run
    recursive=self.args.recursive,
  File "c:\program files\python36\lib\site-packages\dvc\repo\metrics\__init__.py", line 13, in show
    return show(self.repo, *args, **kwargs)
  File "c:\program files\python36\lib\site-packages\dvc\repo\__init__.py", line 30, in wrapper
    ret = f(repo, *args, **kwargs)
  File "c:\program files\python36\lib\site-packages\dvc\repo\metrics\show.py", line 99, in show
    metrics = _collect_metrics(repo, targets, recursive)
  File "c:\program files\python36\lib\site-packages\dvc\repo\metrics\show.py", line 18, in _collect_metrics
    for stage in repo.stages:
  File "c:\program files\python36\lib\site-packages\funcy\objects.py", line 28, in __get__
    res = instance.__dict__[self.fget.__name__] = self.fget(instance)
  File "c:\program files\python36\lib\site-packages\dvc\repo\__init__.py", line 424, in stages
    return self._collect_stages()
  File "c:\program files\python36\lib\site-packages\dvc\repo\__init__.py", line 432, in _collect_stages
    for root, dirs, files in self.tree.walk(self.root_dir):
  File "c:\program files\python36\lib\site-packages\dvc\ignore.py", line 137, in walk
    for root, dirs, files in self.tree.walk(top, topdown):
  File "c:\program files\python36\lib\site-packages\dvc\scm\git\tree.py", line 139, in walk
    raise IOError(errno.ENOENT, "No such file")
FileNotFoundError: [Errno 2] No such file
------------------------------------------------------------
 
Having any troubles? Hit us up at https://dvc.org/support, we are always happy to help!
 ```

My repo has lots of other code including other tags unrelated to DVC. I have initialised DVC and set up a pipeline in a sub-directory. I have a single tag that relates to a metric file and a few other tags that existed before DVC was initialised.
 
Does it expect all tags to contain metrics? It would seem so, given my “fix” below.
 
If I insert some diagnostics I can see that it is looking at a tag unrelated to my DVC directory and finding that the sub-directory does not exist in the GIT tree for a tag.
 
If I insert a try/except in site-packages/dvc/ignore.py:CleanTree:walk(), I can get it to work. I don’t suppose this is the best fix for this issue, but I’m unfamiliar with this code, so I’m not sure where it is iterating through the tags and I shouldn’t spend much more time on this.

__Suggestion__:
You might imagine a requirement where you only want to see metrics in tags that were branched from where “dvc init” was called, or only tags that are related to the sub-directory where “dvc init” was called.

Discussion:

Patch:
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index 910a94380..f8eb17fd2 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -1,10 +1,17 @@
 ---
-name: Bug report
-about: Create a report to help us improve
-
+name: "\U0001F41B Bug Report"
+about: Create a bug report to help us improve DVC
+labels: bug, triage
 ---
 
+## Bug Report
+
+### Please provide information about your setup
+**Output of `dvc version`:**
+```sh
+$ dvc version
+```
 
-**Please provide information about your setup**
-DVC version(i.e. `dvc --version`), Platform and method of installation (pip, homebrew, pkg Mac, exe (Windows), DEB(Linux), RPM(Linux))
+**Additional Information (if any):**
 
+If applicable, please also provide a `--verbose` output of the command, eg: `dvc add --verbose`.
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
new file mode 100644
index 000000000..9ac0922e3
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -0,0 +1,5 @@
+blank_issues_enabled: false
+contact_links:
+  - name: "🤗 Need help?"
+    url: https://dvc.org/chat
+    about: If you have a question, ask us on Discord. Please join with this invite 👉
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
index d06eebb38..1829c929d 100644
--- a/.github/ISSUE_TEMPLATE/feature_request.md
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -1,6 +1,6 @@
 ---
-name: Feature request
+name: "\U0001F680 Feature Request"
 about: Suggest an idea for this project
-
+labels: feature request, triage
 ---
 
diff --git a/dvc/ignore.py b/dvc/ignore.py
index 9b42c7547..2cc35a816 100644
--- a/dvc/ignore.py
+++ b/dvc/ignore.py
@@ -115,8 +115,12 @@ class DvcIgnoreFilter:
 
 
 class CleanTree(BaseTree):
-    def __init__(self, tree):
+    def __init__(self, tree, tree_root=None):
         self.tree = tree
+        if tree_root:
+            self._tree_root = tree_root
+        else:
+            self._tree_root = self.tree.tree_root
 
     @cached_property
     def dvcignore(self):
@@ -124,7 +128,7 @@ class CleanTree(BaseTree):
 
     @property
     def tree_root(self):
-        return self.tree.tree_root
+        return self._tree_root
 
     def open(self, path, mode="r", encoding="utf-8"):
         if self.isfile(path):
@@ -146,8 +150,11 @@ class CleanTree(BaseTree):
         )
 
     def _valid_dirname(self, path):
-        dirname, basename = os.path.split(os.path.normpath(path))
-        dirs, _ = self.dvcignore(os.path.abspath(dirname), [basename], [])
+        path = os.path.abspath(path)
+        if path == self.tree_root:
+            return True
+        dirname, basename = os.path.split(path)
+        dirs, _ = self.dvcignore(dirname, [basename], [])
         if dirs:
             return True
         return False
diff --git a/dvc/repo/__init__.py b/dvc/repo/__init__.py
index 32030ea6a..3115bffe2 100644
--- a/dvc/repo/__init__.py
+++ b/dvc/repo/__init__.py
@@ -17,6 +17,7 @@ from dvc.exceptions import (
 from dvc.ignore import CleanTree
 from dvc.path_info import PathInfo
 from dvc.repo.tree import RepoTree
+from dvc.scm.tree import is_working_tree
 from dvc.utils.fs import path_isin
 
 from ..stage.exceptions import StageFileDoesNotExistError, StageNotFound
@@ -133,7 +134,13 @@ class Repo:
 
     @tree.setter
     def tree(self, tree):
-        self._tree = tree if isinstance(tree, CleanTree) else CleanTree(tree)
+        if is_working_tree(tree) or tree.tree_root == self.root_dir:
+            root = None
+        else:
+            root = self.root_dir
+        self._tree = (
+            tree if isinstance(tree, CleanTree) else CleanTree(tree, root)
+        )
         # Our graph cache is no longer valid, as it was based on the previous
         # tree.
         self._reset()
diff --git a/dvc/repo/brancher.py b/dvc/repo/brancher.py
index 0fbf2f403..103737464 100644
--- a/dvc/repo/brancher.py
+++ b/dvc/repo/brancher.py
@@ -48,6 +48,9 @@ def brancher(  # noqa: E302
         if revs:
             for sha, names in group_by(scm.resolve_rev, revs).items():
                 self.tree = scm.get_tree(sha)
-                yield ", ".join(names)
+                # ignore revs that don't contain repo root
+                # (i.e. revs from before a subdir=True repo was init'ed)
+                if self.tree.exists(self.root_dir):
+                    yield ", ".join(names)
     finally:
         self.tree = saved_tree
diff --git a/dvc/version.py b/dvc/version.py
index 23e8ed62b..9ad93fc1d 100644
--- a/dvc/version.py
+++ b/dvc/version.py
@@ -6,7 +6,7 @@
 import os
 import subprocess
 
-_BASE_VERSION = "1.0.0a5"
+_BASE_VERSION = "1.0.0a6"
 
 
 def _generate_version(base_version):


Test patch:
diff --git a/tests/func/metrics/test_show.py b/tests/func/metrics/test_show.py
index 3ff121f9b..268134984 100644
--- a/tests/func/metrics/test_show.py
+++ b/tests/func/metrics/test_show.py
@@ -1,5 +1,8 @@
+import os
+
 import pytest
 
+from dvc.repo import Repo
 from dvc.repo.metrics.show import NoMetricsError
 
 
@@ -56,3 +59,37 @@ def test_show_branch(tmp_dir, scm, dvc, run_copy_metrics):
         "working tree": {"metrics.yaml": {"foo": 1}},
         "branch": {"metrics.yaml": {"foo": 2}},
     }
+
+
+def test_show_subrepo_with_preexisting_tags(tmp_dir, scm):
+    tmp_dir.gen("foo", "foo")
+    scm.add("foo")
+    scm.commit("init")
+    scm.tag("no-metrics")
+
+    tmp_dir.gen({"subdir": {}})
+    subrepo_dir = tmp_dir / "subdir"
+    with subrepo_dir.chdir():
+        dvc = Repo.init(subdir=True)
+        scm.commit("init dvc")
+
+        dvc.run(
+            cmd="echo foo: 1 > metrics.yaml",
+            metrics=["metrics.yaml"],
+            single_stage=True,
+        )
+
+    scm.add(
+        [
+            str(subrepo_dir / "metrics.yaml"),
+            str(subrepo_dir / "metrics.yaml.dvc"),
+        ]
+    )
+    scm.commit("init metrics")
+    scm.tag("v1")
+
+    expected_path = os.path.join("subdir", "metrics.yaml")
+    assert dvc.metrics.show(all_tags=True) == {
+        "working tree": {expected_path: {"foo": 1}},
+        "v1": {expected_path: {"foo": 1}},
+    }