Repository: iterative/dvc

Issue:
Can't use `repo.metrics.show()` when have a long current dir
This call combined with asserts prevents it:

https://github.com/iterative/dvc/blob/0deab0118a947f3c92cf8a7dd6c7daf0fac5988f/dvc/repo/metrics/show.py#L84

https://github.com/iterative/dvc/blob/0deab0118a947f3c92cf8a7dd6c7daf0fac5988f/dvc/fs/path.py#L83-L85

This blocks Studio updating to DVC 2.9.2.

**Suggested fix:** move relpath calculation to command code, remove asserts as having a longer base should be ok. Returning relpath from API code like `repo.metrics.show()` is unexpected, that code is not really aware about cwd, both absolute paths or paths relative to repo root make sense.


Discussion:

Resolving patch:
diff --git a/dvc/fs/path.py b/dvc/fs/path.py
--- a/dvc/fs/path.py
+++ b/dvc/fs/path.py
@@ -80,12 +80,9 @@ def overlaps(self, left, right):
         # pylint: disable=arguments-out-of-order
         return self.isin_or_eq(left, right) or self.isin(right, left)
 
-    def relpath(self, path, base):
-        assert len(path) > len(base)
-        assert path.startswith(base)
-        normpath = path.rstrip(self.flavour.sep)
-        normbase = base.rstrip(self.flavour.sep)
-        return normpath[len(normbase) + 1 :]
+    def relpath(self, path, start):
+        assert start
+        return self.flavour.relpath(path, start=start)
 
     def relparts(self, path, base):
         return self.parts(self.relpath(path, base))
diff --git a/dvc/repo/diff.py b/dvc/repo/diff.py
--- a/dvc/repo/diff.py
+++ b/dvc/repo/diff.py
@@ -170,13 +170,20 @@ def _to_path(output):
 
 
 def _dir_output_paths(fs, fs_path, obj, targets=None):
+    if fs.scheme == "local":
+        # NOTE: workaround for filesystems that are based on full local paths
+        # (e.g. gitfs, dvcfs, repofs). Proper solution is to use upcoming
+        # fsspec's prefixfs to use relpaths as fs_paths.
+        base = fs.path.relpath(fs_path, os.getcwd())
+    else:
+        base = fs_path
     for key, _, oid in obj:
         fname = fs.path.join(fs_path, *key)
         if targets is None or any(
             fs.path.isin_or_eq(fname, target) for target in targets
         ):
             # pylint: disable=no-member
-            yield fs.path.join(fs.path.name(fs_path), *key), oid.value
+            yield fs.path.join(base, *key), oid.value
 
 
 def _filter_missing(repo_fs, paths):


Test patch:
diff --git a/tests/func/test_diff.py b/tests/func/test_diff.py
--- a/tests/func/test_diff.py
+++ b/tests/func/test_diff.py
@@ -33,6 +33,28 @@ def test_added(tmp_dir, scm, dvc):
     }
 
 
+def test_added_deep(tmp_dir, scm, dvc):
+    tmp_dir.gen({"datas": {"data": {"file": "text"}}})
+    dvc.add(os.path.join("datas", "data"))
+
+    assert dvc.diff() == {
+        "added": [
+            {
+                "path": os.path.join("datas", "data" + os.sep),
+                "hash": "0dab3fae569586d4c33272e5011605bf.dir",
+            },
+            {
+                "path": os.path.join("datas", "data", "file"),
+                "hash": "1cb251ec0d568de6a929b520c4aed8d1",
+            },
+        ],
+        "deleted": [],
+        "modified": [],
+        "not in cache": [],
+        "renamed": [],
+    }
+
+
 def test_no_cache_entry(tmp_dir, scm, dvc):
     tmp_dir.dvc_gen("file", "first", commit="add a file")