diff --git a/dvc/cache/base.py b/dvc/cache/base.py index ab8e0dabe..6a7aabf3c 100644 --- a/dvc/cache/base.py +++ b/dvc/cache/base.py @@ -414,7 +414,7 @@ class CloudCache: return self.changed_cache_file(hash_info) def already_cached(self, path_info): - _, current = self.tree.get_hash(path_info) + current = self.tree.get_hash(path_info) if not current: return False diff --git a/dvc/cache/local.py b/dvc/cache/local.py index 099086f01..8818ebe1b 100644 --- a/dvc/cache/local.py +++ b/dvc/cache/local.py @@ -94,11 +94,7 @@ class LocalCache(CloudCache): def already_cached(self, path_info): assert path_info.scheme in ["", "local"] - current = self.tree.get_hash(path_info) - if not current: - return False - - return not self.changed_cache(current) + return super().already_cached(path_info) def _verify_link(self, path_info, link_type): if link_type == "hardlink" and self.tree.getsize(path_info) == 0: diff --git a/tests/func/test_checkout.py b/tests/func/test_checkout.py index 6bcebb4d6..f1d962d77 100644 --- a/tests/func/test_checkout.py +++ b/tests/func/test_checkout.py @@ -850,3 +850,22 @@ def test_checkouts_for_pipeline_tracked_outs(tmp_dir, dvc, scm, run_copy): (tmp_dir / "ipsum").unlink() assert set(dvc.checkout()["added"]) == {"bar", "ipsum"} + + +@pytest.mark.parametrize( + "workspace", [pytest.lazy_fixture("s3")], indirect=True +) +def test_checkout_external_modified_file(tmp_dir, dvc, scm, mocker, workspace): + # regression: happened when file in external output changed and checkout + # was attempted without force, dvc checks if it's present in its cache + # before asking user to remove it. + workspace.gen("foo", "foo") + dvc.add(str(workspace / "foo"), external=True) + scm.add(["foo.dvc"]) + scm.commit("add foo") + + workspace.gen("foo", "foobar") # messing up the external outputs + mocker.patch("dvc.prompt.confirm", return_value=True) + dvc.checkout() + + assert (workspace / "foo").read_text() == "foo"