diff --git a/.gitignore b/.gitignore index 6333faa59..91becd9ad 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ neatlynx/__pycache__ *.pyc .env/ .env2.7/ +.python-version .dvc.conf.lock .DS_Store diff --git a/dvc/repo/init.py b/dvc/repo/init.py index 2712b2279..87b5549ec 100644 --- a/dvc/repo/init.py +++ b/dvc/repo/init.py @@ -45,19 +45,22 @@ def init(root_dir=os.curdir, no_scm=False, force=False, subdir=False): scm = SCM(root_dir, search_parent_directories=subdir, no_scm=no_scm) except SCMError: raise InitError( - "{repo} is not tracked by any supported SCM tool (e.g. Git). " + f"{root_dir} is not tracked by any supported SCM tool (e.g. Git). " "Use `--no-scm` if you don't want to use any SCM or " "`--subdir` if initializing inside a subdirectory of a parent SCM " - "repository.".format(repo=root_dir) + "repository." + ) + + if scm.is_ignored(dvc_dir): + raise InitError( + f"{dvc_dir} is ignored by your SCM tool. \n" + "Make sure that it's tracked, " + "for example, by adding '!.dvc' to .gitignore." ) if os.path.isdir(dvc_dir): if not force: - raise InitError( - "'{repo}' exists. Use `-f` to force.".format( - repo=relpath(dvc_dir) - ) - ) + raise InitError(f"'{relpath(dvc_dir)}' exists. Use `-f` to force.") remove(dvc_dir) diff --git a/dvc/scm/base.py b/dvc/scm/base.py index d37abbcd9..3a7c3f067 100644 --- a/dvc/scm/base.py +++ b/dvc/scm/base.py @@ -66,6 +66,10 @@ class Base: """ return True + def is_ignored(self, path): # pylint: disable=unused-argument + """Returns whether or not path is ignored by SCM.""" + return False + def ignore(self, path): # pylint: disable=unused-argument """Makes SCM ignore a specified path.""" diff --git a/dvc/scm/git.py b/dvc/scm/git.py index aa93d715b..7de2736cb 100644 --- a/dvc/scm/git.py +++ b/dvc/scm/git.py @@ -183,7 +183,7 @@ class Git(Base): return entry, gitignore - def _ignored(self, path): + def is_ignored(self, path): from dulwich import ignore from dulwich.repo import Repo @@ -194,7 +194,7 @@ class Git(Base): def ignore(self, path): entry, gitignore = self._get_gitignore(path) - if self._ignored(path): + if self.is_ignored(path): return msg = "Adding '{}' to '{}'.".format(relpath(path), relpath(gitignore)) diff --git a/tests/func/test_init.py b/tests/func/test_init.py index 4ea9c9101..79f7dc4ab 100644 --- a/tests/func/test_init.py +++ b/tests/func/test_init.py @@ -111,3 +111,17 @@ def test_gen_dvcignore(tmp_dir): "# https://dvc.org/doc/user-guide/dvcignore\n" ) assert text == (tmp_dir / ".dvcignore").read_text() + + +def test_init_when_ignored_by_git(tmp_dir, scm, caplog): + # https://github.com/iterative/dvc/issues/3738 + tmp_dir.gen({".gitignore": ".*"}) + with caplog.at_level(logging.ERROR, logger="dvc"): + assert main(["init"]) == 1 + assert ( + "{dvc_dir} is ignored by your SCM tool. \n" + "Make sure that it's tracked, " + "for example, by adding '!.dvc' to .gitignore.".format( + dvc_dir=tmp_dir / DvcRepo.DVC_DIR + ) + ) in caplog.text diff --git a/tests/func/test_scm.py b/tests/func/test_scm.py index 97530ac74..bdfb5a971 100644 --- a/tests/func/test_scm.py +++ b/tests/func/test_scm.py @@ -92,8 +92,8 @@ 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(os.fspath(tmp_dir / "dir1" / "file1.jpg")) - assert not scm._ignored(os.fspath(tmp_dir / "dir1" / "file2.txt")) + assert scm.is_ignored(tmp_dir / "dir1" / "file1.jpg") + assert not scm.is_ignored(tmp_dir / "dir1" / "file2.txt") def test_get_gitignore(tmp_dir, scm):