Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 4 additions & 11 deletions dvc/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
12 changes: 11 additions & 1 deletion dvc/repo/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
22 changes: 20 additions & 2 deletions dvc/repo/add.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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),
Expand Down
16 changes: 15 additions & 1 deletion tests/func/test_add.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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"))