Repository: iterative/dvc

Issue:
import: behave like `add`, `move`
> More context around https://discordapp.com/channels/485586884165107732/485596304961962003/676398069469806603

Currently `dvc import` has a `-o` option to specify where to place the downloaded data, but the DVC-file itself gets created in the CWD. It would be more intuitive to have the DVC-file in the same `--out` location, similar to how `dvc add` behaves.

This is especially strange when using `dvc move` on an import stage, as it moves both the data and the DVC-file into the `dst` location.

May apply to `dvc import-url` as well, haven't checked.


Discussion:
I feel like this might be a bug that we didn't notice, as I do remember implementing this behavior for original `import`(now `import-url`). :confused: Will take a look.
As import is using `imp_url` fixing this issue for `imp_url` will probably fix it also for `imp`.
repro script
```
#!/bin/bash

rm -rf erepo repo
mkdir erepo repo
MAIN=$(pwd)

pushd erepo
git init --quiet
dvc init -q

echo data >> data
dvc add data
git add -A
git commit -am "init"

popd
pushd repo
git init --quiet && dvc init -q

mkdir dir
dvc import $MAIN/erepo data -o dir/data
ls
```
ls shows that `data.dvc` is in root project dir, while it should not be

Resolving patch:
diff --git a/dvc/repo/add.py b/dvc/repo/add.py
--- a/dvc/repo/add.py
+++ b/dvc/repo/add.py
@@ -105,7 +105,9 @@ def _create_stages(repo, targets, fname, pbar=None):
         disable=True if len(targets) < LARGE_DIR_SIZE else None,
         unit="file",
     ):
-        stage = Stage.create(repo, outs=[out], add=True, fname=fname)
+        stage = Stage.create(
+            repo, outs=[out], accompany_outs=True, fname=fname
+        )
 
         if not stage:
             if pbar is not None:
diff --git a/dvc/repo/imp_url.py b/dvc/repo/imp_url.py
--- a/dvc/repo/imp_url.py
+++ b/dvc/repo/imp_url.py
@@ -11,7 +11,13 @@ def imp_url(self, url, out=None, fname=None, erepo=None, locked=True):
     out = resolve_output(url, out)
 
     stage = Stage.create(
-        self, cmd=None, deps=[url], outs=[out], fname=fname, erepo=erepo
+        self,
+        cmd=None,
+        deps=[url],
+        outs=[out],
+        fname=fname,
+        erepo=erepo,
+        accompany_outs=True,
     )
 
     if stage is None:
diff --git a/dvc/stage.py b/dvc/stage.py
--- a/dvc/stage.py
+++ b/dvc/stage.py
@@ -502,12 +502,11 @@ def is_cached(self):
         return True
 
     @staticmethod
-    def create(repo, **kwargs):
+    def create(repo, accompany_outs=False, **kwargs):
 
         wdir = kwargs.get("wdir", None)
         cwd = kwargs.get("cwd", None)
         fname = kwargs.get("fname", None)
-        add = kwargs.get("add", False)
 
         # Backward compatibility for `cwd` option
         if wdir is None and cwd is not None:
@@ -539,12 +538,12 @@ def create(repo, **kwargs):
         stage._check_duplicated_arguments()
 
         if not fname:
-            fname = Stage._stage_fname(stage.outs, add)
+            fname = Stage._stage_fname(stage.outs, accompany_outs)
         stage._check_dvc_filename(fname)
 
         # Autodetecting wdir for add, we need to create outs first to do that,
         # so we start with wdir = . and remap out paths later.
-        if add and kwargs.get("wdir") is None and cwd is None:
+        if accompany_outs and kwargs.get("wdir") is None and cwd is None:
             wdir = os.path.dirname(fname)
 
             for out in chain(stage.outs, stage.deps):


Test patch:
diff --git a/tests/func/test_import_url.py b/tests/func/test_import_url.py
--- a/tests/func/test_import_url.py
+++ b/tests/func/test_import_url.py
@@ -92,3 +92,15 @@ def test_import_url_to_dir(dname, tmp_dir, dvc):
     assert stage.outs[0].path_info == dst
     assert os.path.isdir(dname)
     assert dst.read_text() == "file content"
+
+
+def test_import_stage_accompanies_target(tmp_dir, dvc, erepo_dir):
+    with erepo_dir.chdir():
+        erepo_dir.dvc_gen("file1", "file1 content", commit="commit file")
+
+    tmp_dir.gen({"dir": {}})
+    erepo = {"url": fspath(erepo_dir)}
+    dvc.imp_url("file1", out=os.path.join("dir", "imported_file"), erepo=erepo)
+
+    assert (tmp_dir / "dir" / "imported_file").exists()
+    assert (tmp_dir / "dir" / "imported_file.dvc").exists()