From bc81d96fe100e5b40f38efdbc7dc5ace0835d321 Mon Sep 17 00:00:00 2001 From: pawel Date: Mon, 10 Feb 2020 15:39:28 +0100 Subject: [PATCH] add: meaningful message upon adding overlapping paths --- dvc/exceptions.py | 15 ++++----------- dvc/repo/__init__.py | 12 +++++++++++- dvc/repo/add.py | 22 ++++++++++++++++++++-- tests/func/test_add.py | 16 +++++++++++++++- 4 files changed, 50 insertions(+), 15 deletions(-) diff --git a/dvc/exceptions.py b/dvc/exceptions.py index 4af6111080..4459121e92 100644 --- a/dvc/exceptions.py +++ b/dvc/exceptions.py @@ -188,17 +188,10 @@ def __init__(self): class OverlappingOutputPathsError(DvcException): - def __init__(self, out_1, out_2): - super().__init__( - "Paths for outs:\n'{}'('{}')\n'{}'('{}')\noverlap. To avoid " - "unpredictable behaviour, rerun command with non overlapping outs " - "paths.".format( - str(out_1), - out_1.stage.relpath, - str(out_2), - out_2.stage.relpath, - ) - ) + def __init__(self, parent, overlapping_out, message): + self.parent = parent + self.overlapping_out = overlapping_out + super().__init__(message) class CheckoutErrorSuggestGit(DvcException): diff --git a/dvc/repo/__init__.py b/dvc/repo/__init__.py index 16062042fd..f46ea73803 100644 --- a/dvc/repo/__init__.py +++ b/dvc/repo/__init__.py @@ -334,7 +334,17 @@ def _collect_graph(self, stages=None): for out in stage.outs: for p in out.path_info.parents: if p in outs: - raise OverlappingOutputPathsError(outs[p], out) + msg = ( + "Paths for outs:\n'{}'('{}')\n'{}'('{}')\n" + "overlap. To avoid unpredictable behaviour, " + "rerun command with non overlapping outs paths." + ).format( + str(outs[p]), + outs[p].stage.relpath, + str(out), + out.stage.relpath, + ) + raise OverlappingOutputPathsError(outs[p], out, msg) for stage in stages: stage_path_info = PathInfo(stage.path) diff --git a/dvc/repo/add.py b/dvc/repo/add.py index e469119b93..66fbea0c7f 100644 --- a/dvc/repo/add.py +++ b/dvc/repo/add.py @@ -4,7 +4,10 @@ import colorama from . import locked -from ..exceptions import RecursiveAddingWhileUsingFilename +from ..exceptions import ( + RecursiveAddingWhileUsingFilename, + OverlappingOutputPathsError, +) from ..output.base import OutputDoesNotExistError from ..progress import Tqdm from ..repo.scm_context import scm_context @@ -49,7 +52,22 @@ def add(repo, targets, recursive=False, no_commit=False, fname=None): stages = _create_stages(repo, sub_targets, fname, pbar=pbar) - repo.check_modified_graph(stages) + try: + repo.check_modified_graph(stages) + except OverlappingOutputPathsError as exc: + msg = ( + "Cannot add '{out}', because it is overlapping with other " + "DVC tracked output: '{parent}'.\n" + "To include '{out}' in '{parent}', run " + "'dvc commit {parent_stage}'" + ).format( + out=exc.overlapping_out.path_info, + parent=exc.parent.path_info, + parent_stage=exc.parent.stage.relpath, + ) + raise OverlappingOutputPathsError( + exc.parent, exc.overlapping_out, msg + ) with Tqdm( total=len(stages), diff --git a/tests/func/test_add.py b/tests/func/test_add.py index 002722f52b..b2e6dfb1b4 100644 --- a/tests/func/test_add.py +++ b/tests/func/test_add.py @@ -11,7 +11,7 @@ import dvc as dvc_module from dvc.cache import Cache -from dvc.exceptions import DvcException +from dvc.exceptions import DvcException, OverlappingOutputPathsError from dvc.exceptions import RecursiveAddingWhileUsingFilename from dvc.exceptions import StageFileCorruptedError from dvc.main import main @@ -641,3 +641,17 @@ def test_escape_gitignore_entries(tmp_dir, scm, dvc): tmp_dir.dvc_gen(fname, "...") assert ignored_fname in get_gitignore_content() + + +def test_add_from_data_dir(tmp_dir, scm, dvc): + tmp_dir.dvc_gen({"dir": {"file1": "file1 content"}}) + + tmp_dir.gen({"dir": {"file2": "file2 content"}}) + + with pytest.raises(OverlappingOutputPathsError) as e: + dvc.add(os.path.join("dir", "file2")) + assert str(e.value) == ( + "Cannot add '{out}', because it is overlapping with other DVC " + "tracked output: 'dir'.\n" + "To include '{out}' in 'dir', run 'dvc commit dir.dvc'" + ).format(out=os.path.join("dir", "file2"))