Repository: iterative/dvc

Issue:
`stage add`: fails with dirs that will be created later by the `cmd`(s)
# Bug Report

## Description

If the stage being defined creates a dir and files in this dir are stage outputs, `stage add` will fail (because it can't write the corresponding .gitignore file).

### Reproduce

```console
$ dvc stage add -n hidir -o dir/hi 'mkdir dir && echo hi > dir/hi'
ERROR: unexpected error - [Errno 2] No such file or directory: '/.../dir/.gitignore'
```

### Expected

I guess the full path to the .gitignore in question should be created if needed. Or maybe create it at `repro`? Not sure how, but I would expect the stage definition not to fail for this.

### Environment information

**Output of `dvc doctor`:**

```console
$ dvc doctor
DVC version: 2.0.17 (pip)
---------------------------------
Platform: Python 3.6.9 on Linux-5.4.72-microsoft-standard-WSL2-x86_64-with-Ubuntu-18.04-bionic
Supports: gdrive, hdfs, http, https, s3, ssh, oss
Cache types: <https://error.dvc.org/no-dvc-cache>
Caches: local
Remotes: None
Workspace directory: ext4 on /dev/sdb
Repo: dvc, git
```



Discussion:
@skshetry This also can cause `dvc exp init` to fail:

```
$ dvc exp init --model models/model.h5 python src/train.py
ERROR: unexpected error - [Errno 2] No such file or directory: '/Users/dave/repo/models/.gitignore'

Having any troubles? Hit us up at https://dvc.org/support, we are always happy to help!
```
@skshetry I don't want to slow down other work for this, but this seems like a fairly serious bug for `exp init`. It actually breaks the example in https://dvc.org/doc/command-reference/exp/init#example-interactive-mode. What do you think about priority and solution?
@dberenbaum, for `exp init`, we can consider creating the parent directory of those outputs, where the `.gitignore` file will be added.

However, we can also choose not to generate `.gitignore` entries on `exp init`/`stage add` in these edge cases, as successive `dvc repro` and `dvc exp run` will try to generate them as well.

If we do want to always generate `.gitignore`, we need to fix how we create `.gitignore` files. We need to change the _gitignore-generator_ to backtrack up to the dvc's root directory, likewise on `dvc remove`.
> successive `dvc repro` and `dvc exp run` will try to generate them as well.

In that case, do you know why we try to create `.gitignore` entries on `stage add`?
Marking this as p1 until at least the `dvc exp init` scenario is resolved (see https://github.com/iterative/dvc.org/pull/3430#discussion_r870732773).
> In that case, do you know why we try to create .gitignore entries on stage add?

Any answer to this?
Bumping down to p2 following #7752 
> > In that case, do you know why we try to create .gitignore entries on stage add?
> 
> Any answer to this?

See https://github.com/iterative/dvc/pull/7740#issuecomment-1148588656

Resolving patch:
diff --git a/dvc/repo/stage.py b/dvc/repo/stage.py
--- a/dvc/repo/stage.py
+++ b/dvc/repo/stage.py
@@ -23,6 +23,7 @@
     OutputNotFoundError,
 )
 from dvc.repo import lock_repo
+from dvc.ui import ui
 from dvc.utils import as_posix, parse_target, relpath
 
 logger = logging.getLogger(__name__)
@@ -139,7 +140,14 @@ def add(
         )
         with self.repo.scm_context:
             stage.dump(update_lock=update_lock)
-            stage.ignore_outs()
+            try:
+                stage.ignore_outs()
+            except FileNotFoundError as exc:
+                ui.warn(
+                    f"Could not create .gitignore entry in {exc.filename}."
+                    " DVC will attempt to create .gitignore entry again when"
+                    " the stage is run."
+                )
 
         return stage
 


Test patch: