Repository: iterative/dvc

Issue:
Add a way to suppress dvc changes to .gitignore
At the moment, DVC make changes to the .gitignore after commands like `dvc add`. There are cases, like mine, where it's not needed as .gitignore already exclude whole directory where data is kept.
Add a configuration that allow to suppress dvc changes to .gitignore.


Discussion:
@piojanu I guess what we should do is check whether or not the data is already ignored by git and if it is then just not add it to yet another gitignore. Does that make sense for your scenario?
Hmm. You would have to check if e.g. whole directory, that the file is in, isn't ignored already. Then it would do fine.
@piojanu Sure. There is actually a `git check-ignore` command, which we could utilize, unless there is a more straightforward way in GitPython.
@piojanu Ok, there is actually a `repo.git.check_ignore()` supported already. So basically all we'll have to do is to add a check for it to `Git.ignore()` in https://github.com/iterative/dvc/blob/master/dvc/scm/git.py#L76 . Would you like to submit a PR for it? :slightly_smiling_face: 
I'd love too, but for now I have very little time to sit down to it.
@piojanu No worries :) We'll try to get to it when we have time then. Thanks for the feedback! :slightly_smiling_face: 
Hey @efiop and @piojanu. I can work on this after work tonight. Do you mind if I take this issue?
Hi @J0 ! Sure! Thanks a lot for looking into it! Let us know if you need any help and/or have any questions. :slightly_smiling_face: 
Anyone working on it ? or I can pick this up with some help 
@veera83372 Not sure if @J0 is still working on this. Let's see if he replies. 
@efiop I am still working on this issue. Sorry for the long delay — just opened a  PR
Is this still being worked on?
@J0 Are you still working on this?
@Aljo-Rovco I doubt. The PR (empty?) was closed a while ago. Feel free to pick it up and contribute and let us know if you'd need any help from us.
@shcheklein was planning to give 1 day for @J0 to reply and then give this a go, but ok :)
@Aljo-Rovco Looks like @J0 is not responding, so indeed, please feel free to take this one. Thanks a lot for looking into it! 🙂 
@Aljo-Rovco Are you working on this?
Hey sorry, was swamped. Not sure how well I'm suited to contribute, but I can try. Would it just be a check here: https://github.com/iterative/dvc/blob/master/dvc/scm/git/__init__.py#L137 ?
@Aljo-Rovco No worries. That would be the place to check, yes :slightly_smiling_face: 

Resolving patch:
diff --git a/dvc/scm/git/__init__.py b/dvc/scm/git/__init__.py
--- a/dvc/scm/git/__init__.py
+++ b/dvc/scm/git/__init__.py
@@ -18,7 +18,6 @@
 from dvc.utils import relpath
 from dvc.utils.fs import path_isin
 
-
 logger = logging.getLogger(__name__)
 
 
@@ -133,20 +132,19 @@ def _get_gitignore(self, path):
 
         return entry, gitignore
 
-    @staticmethod
-    def _ignored(entry, gitignore_path):
-        if os.path.exists(gitignore_path):
-            with open(gitignore_path, "r") as fobj:
-                ignore_list = fobj.readlines()
-            return any(
-                filter(lambda x: x.strip() == entry.strip(), ignore_list)
-            )
-        return False
+    def _ignored(self, path):
+        from git.exc import GitCommandError
+
+        try:
+            self.repo.git.check_ignore(path)
+            return True
+        except GitCommandError:
+            return False
 
     def ignore(self, path):
         entry, gitignore = self._get_gitignore(path)
 
-        if self._ignored(entry, gitignore):
+        if self._ignored(path):
             return
 
         msg = "Adding '{}' to '{}'.".format(relpath(path), relpath(gitignore))


Test patch:
diff --git a/tests/dir_helpers.py b/tests/dir_helpers.py
--- a/tests/dir_helpers.py
+++ b/tests/dir_helpers.py
@@ -45,6 +45,7 @@
 
 import os
 import pathlib
+import logging
 from contextlib import contextmanager
 
 import pytest
@@ -65,6 +66,11 @@
 ]
 
 
+# see https://github.com/iterative/dvc/issues/3167
+git_logger = logging.getLogger("git")
+git_logger.setLevel(logging.CRITICAL)
+
+
 class TmpDir(pathlib.Path):
     def __new__(cls, *args, **kwargs):
         if cls is TmpDir:
diff --git a/tests/func/test_get.py b/tests/func/test_get.py
--- a/tests/func/test_get.py
+++ b/tests/func/test_get.py
@@ -163,11 +163,7 @@ def test_get_from_non_dvc_master(tmp_dir, git_dir, caplog):
 
     caplog.clear()
 
-    # removing `git` import in conftest resulted in unexpected logs from
-    # that package, see https://github.com/iterative/dvc/issues/3167
-    with caplog.at_level(logging.INFO, logger="git"), caplog.at_level(
-        logging.INFO, logger="dvc"
-    ):
+    with caplog.at_level(logging.INFO, logger="dvc"):
         Repo.get(fspath(git_dir), "some_file", out="some_dst", rev="branch")
 
     assert caplog.text == ""
diff --git a/tests/func/test_scm.py b/tests/func/test_scm.py
--- a/tests/func/test_scm.py
+++ b/tests/func/test_scm.py
@@ -83,6 +83,14 @@ def test_ignore(tmp_dir, scm):
     assert _count_gitignore_entries(target) == 0
 
 
+def test_ignored(tmp_dir, scm):
+    tmp_dir.gen({"dir1": {"file1.jpg": "cont", "file2.txt": "cont"}})
+    tmp_dir.gen({".gitignore": "dir1/*.jpg"})
+
+    assert scm._ignored(fspath(tmp_dir / "dir1" / "file1.jpg"))
+    assert not scm._ignored(fspath(tmp_dir / "dir1" / "file2.txt"))
+
+
 def test_get_gitignore(tmp_dir, scm):
     tmp_dir.gen({"file1": "contents", "dir": {}})