diff --git a/dvc/repo/__init__.py b/dvc/repo/__init__.py index a7c3d4d7ca..05b08f0a55 100644 --- a/dvc/repo/__init__.py +++ b/dvc/repo/__init__.py @@ -387,15 +387,24 @@ def get_data_index_entry( path: str, workspace: str = "repo", ) -> tuple["DataIndex", "DataIndexEntry"]: + abspath = path + if not os.path.isabs(abspath): + # `path` is interpreted as relative to the root of the repo, not + # the current working directory (which may be a subdirectory of + # the repo). See #11029. + abspath = self.fs.join(self.root_dir, abspath) if self.subrepos: - fs_path = self.dvcfs.from_os_path(path) + # `from_os_path` returns a path relative to the repo root. Anchor + # it at the filesystem root so it is not re-resolved against the + # filesystem's current working directory. + fs_path = self.dvcfs.from_os_path(abspath) fs = self.dvcfs.fs - key = fs._get_key_from_relative(fs_path) + key = fs._get_key_from_relative(fs.root_marker + fs_path) subrepo, _, key = fs._get_subrepo_info(key) index = subrepo.index.data[workspace] else: index = self.index.data[workspace] - key = self.fs.relparts(path, self.root_dir) + key = self.fs.relparts(abspath, self.root_dir) try: return index, index[key] diff --git a/tests/func/api/test_data.py b/tests/func/api/test_data.py index 48d4e98eed..d3769f0dd6 100644 --- a/tests/func/api/test_data.py +++ b/tests/func/api/test_data.py @@ -250,6 +250,22 @@ def test_get_url_granular(tmp_dir, dvc, cloud): assert api.get_url(os.path.join("dir", "nested", "file")) == expected_url +def test_get_url_from_subdir(tmp_dir, dvc, local_cloud): + tmp_dir.add_remote(config=local_cloud.config) + tmp_dir.dvc_gen({"foo": "foo", "subdir": {"bar": "bar"}}) + + expected_url = ( + local_cloud / "files" / "md5" / "ac" / "bd18db4cc2f85cedef654fccc4a4d8" + ).url + + # A relative path is relative to the root of the repo, even when the + # current working directory is a subdirectory of the repo (#11029). + subdir = tmp_dir / "subdir" + with subdir.chdir(): + assert api.get_url("foo") == expected_url + assert api.get_url("foo", repo=os.fspath(tmp_dir)) == expected_url + + def test_get_url_subrepos(tmp_dir, scm, local_cloud): subrepo = tmp_dir / "subrepo" make_subrepo(subrepo, scm, config=local_cloud.config)