diff --git a/dvc/fs/path.py b/dvc/fs/path.py index cde2b44f2d..b0c02db8c7 100644 --- 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 index e0ddfb21e2..f2295aeeb4 100644 --- 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): diff --git a/tests/func/test_diff.py b/tests/func/test_diff.py index f18ab6032c..0fad451629 100644 --- 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")