diff --git a/dvc/output.py b/dvc/output.py index 43387e784..fa73c4ba0 100644 --- a/dvc/output.py +++ b/dvc/output.py @@ -1090,7 +1090,7 @@ class Output: return len(obj) if obj else 0 def unprotect(self): - if self.exists: + if self.exists and self.use_cache: self.cache.unprotect(self.fs_path) def get_dir_cache(self, **kwargs) -> Optional["Tree"]: diff --git a/tests/func/repro/test_repro.py b/tests/func/repro/test_repro.py index 551e70527..5f68c1e60 100644 --- a/tests/func/repro/test_repro.py +++ b/tests/func/repro/test_repro.py @@ -1366,3 +1366,27 @@ def test_repro_ignore_errors(mocker, tmp_dir, dvc, copy_script): [bar_call, stage1_call, foo_call, stage2_call], [foo_call, bar_call, stage1_call, stage2_call], ) + + +@pytest.mark.parametrize("persist", [True, False]) +def test_repro_external_outputs(tmp_dir, dvc, local_workspace, persist): + local_workspace.gen("foo", "foo") + foo_path = str(local_workspace / "foo") + bar_path = str(local_workspace / "bar") + outs = {"outs_no_cache": [bar_path]} + if persist: + outs = {"outs_persist_no_cache": [bar_path]} + dvc.run( + name="mystage", + cmd=f"cp {foo_path} {bar_path}", + deps=[foo_path], + no_exec=True, + **outs, + ) + + dvc.reproduce() + dvc.reproduce(force=True) + + assert (local_workspace / "foo").read_text() == "foo" + assert (local_workspace / "bar").read_text() == "foo" + assert not (local_workspace / "cache").exists()