From b8450b9faee10f3a0a63378b58adde60a27964e0 Mon Sep 17 00:00:00 2001 From: Hadi Alqattan Date: Sun, 16 May 2021 20:51:27 +0300 Subject: [PATCH] Fix: black only respects the root gitignore. (#2225) MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Commit history before merge: Black now respects .gitignore files in all levels, not only root/.gitignore file (apply .gitignore rules like git does). * Fix: typo * Fix: respect .gitignore files in all levels. * Add: CHANGELOG note. * Fix: TypeError: unsupported operand type(s) for +: 'NoneType' and 'PathSpec' * Update docs. * Fix: no parent .gitignore * Add a comment since the if expression is a bit hard to understand * Update tests - conver no parent .gitignore case. * Use main's Pipfile.lock instead The original changes in Pipfile.lock are whitespace only. The changes turned the JSON's file indentation from 4 to 2. Effectively this happened: `json.dumps(json.loads(old_pipfile_lock), indent=2) + "\n"`. Just using main's Pipfile.lock instead of undoing the changes because 1) I don't know how to do that easily and quickly, and 2) there's a merge conflict. Co-authored-by: Richard Si <63936253+ichard26@users.noreply.github.com> * Merge remote-tracking branch 'upstream/main' into i1730 … conflicts for days ay? --- CHANGES.md | 2 ++ .../file_collection_and_discovery.md | 3 +- src/black/files.py | 4 ++- .../nested_gitignore_tests/pyproject.toml | 3 ++ .../nested_gitignore_tests/root/.gitignore | 1 + tests/data/nested_gitignore_tests/root/a.py | 1 + tests/data/nested_gitignore_tests/root/b.py | 1 + tests/data/nested_gitignore_tests/root/c.py | 1 + .../root/child/.gitignore | 1 + .../nested_gitignore_tests/root/child/a.py | 1 + .../nested_gitignore_tests/root/child/b.py | 1 + .../nested_gitignore_tests/root/child/c.py | 1 + tests/data/nested_gitignore_tests/x.py | 0 tests/test_black.py | 29 ++++++++++++++++++- 14 files changed, 45 insertions(+), 4 deletions(-) create mode 100644 tests/data/nested_gitignore_tests/pyproject.toml create mode 100644 tests/data/nested_gitignore_tests/root/.gitignore create mode 100644 tests/data/nested_gitignore_tests/root/a.py create mode 100644 tests/data/nested_gitignore_tests/root/b.py create mode 100644 tests/data/nested_gitignore_tests/root/c.py create mode 100644 tests/data/nested_gitignore_tests/root/child/.gitignore create mode 100644 tests/data/nested_gitignore_tests/root/child/a.py create mode 100644 tests/data/nested_gitignore_tests/root/child/b.py create mode 100644 tests/data/nested_gitignore_tests/root/child/c.py create mode 100644 tests/data/nested_gitignore_tests/x.py diff --git a/CHANGES.md b/CHANGES.md index f2c7954..603554c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -4,6 +4,8 @@ ### _Black_ +- Respect `.gitignore` files in all levels, not only `root/.gitignore` file (apply + `.gitignore` rules like `git` does) (#2225) - Restored compatibility with Click 8.0 on Python 3.6 when LANG=C used (#2227) ### _Blackd_ diff --git a/docs/usage_and_configuration/file_collection_and_discovery.md b/docs/usage_and_configuration/file_collection_and_discovery.md index 54c76cd..1f43618 100644 --- a/docs/usage_and_configuration/file_collection_and_discovery.md +++ b/docs/usage_and_configuration/file_collection_and_discovery.md @@ -30,8 +30,7 @@ then write the above files to `.cache/black//`. ## .gitignore If `--exclude` is not set, _Black_ will automatically ignore files and directories in -`.gitignore` file, if present. The `.gitignore` file must be in the project root to be -used and nested `.gitignore` aren't supported. +`.gitignore` file(s), if present. If you want _Black_ to continue using `.gitignore` while also configuring the exclusion rules, please use `--extend-exclude`. diff --git a/src/black/files.py b/src/black/files.py index 1be5606..a0c92e8 100644 --- a/src/black/files.py +++ b/src/black/files.py @@ -204,6 +204,8 @@ def gen_python_files( continue if child.is_dir(): + # If gitignore is None, gitignore usage is disabled, while a Falsey + # gitignore is when the directory doesn't have a .gitignore file. yield from gen_python_files( child.iterdir(), root, @@ -212,7 +214,7 @@ def gen_python_files( extend_exclude, force_exclude, report, - gitignore, + gitignore + get_gitignore(child) if gitignore is not None else None, ) elif child.is_file(): diff --git a/tests/data/nested_gitignore_tests/pyproject.toml b/tests/data/nested_gitignore_tests/pyproject.toml new file mode 100644 index 0000000..9ba7ec2 --- /dev/null +++ b/tests/data/nested_gitignore_tests/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["setuptools>=41.0", "setuptools-scm", "wheel"] +build-backend = "setuptools.build_meta" diff --git a/tests/data/nested_gitignore_tests/root/.gitignore b/tests/data/nested_gitignore_tests/root/.gitignore new file mode 100644 index 0000000..2987e7b --- /dev/null +++ b/tests/data/nested_gitignore_tests/root/.gitignore @@ -0,0 +1 @@ +a.py diff --git a/tests/data/nested_gitignore_tests/root/a.py b/tests/data/nested_gitignore_tests/root/a.py new file mode 100644 index 0000000..7135cfd --- /dev/null +++ b/tests/data/nested_gitignore_tests/root/a.py @@ -0,0 +1 @@ +# should be excluded (root/.gitignore) diff --git a/tests/data/nested_gitignore_tests/root/b.py b/tests/data/nested_gitignore_tests/root/b.py new file mode 100644 index 0000000..bdeeca3 --- /dev/null +++ b/tests/data/nested_gitignore_tests/root/b.py @@ -0,0 +1 @@ +# should be included diff --git a/tests/data/nested_gitignore_tests/root/c.py b/tests/data/nested_gitignore_tests/root/c.py new file mode 100644 index 0000000..bdeeca3 --- /dev/null +++ b/tests/data/nested_gitignore_tests/root/c.py @@ -0,0 +1 @@ +# should be included diff --git a/tests/data/nested_gitignore_tests/root/child/.gitignore b/tests/data/nested_gitignore_tests/root/child/.gitignore new file mode 100644 index 0000000..6df81dd --- /dev/null +++ b/tests/data/nested_gitignore_tests/root/child/.gitignore @@ -0,0 +1 @@ +b.py diff --git a/tests/data/nested_gitignore_tests/root/child/a.py b/tests/data/nested_gitignore_tests/root/child/a.py new file mode 100644 index 0000000..7135cfd --- /dev/null +++ b/tests/data/nested_gitignore_tests/root/child/a.py @@ -0,0 +1 @@ +# should be excluded (root/.gitignore) diff --git a/tests/data/nested_gitignore_tests/root/child/b.py b/tests/data/nested_gitignore_tests/root/child/b.py new file mode 100644 index 0000000..c91d479 --- /dev/null +++ b/tests/data/nested_gitignore_tests/root/child/b.py @@ -0,0 +1 @@ +# should be excluded (child/.gitignore) diff --git a/tests/data/nested_gitignore_tests/root/child/c.py b/tests/data/nested_gitignore_tests/root/child/c.py new file mode 100644 index 0000000..bdeeca3 --- /dev/null +++ b/tests/data/nested_gitignore_tests/root/child/c.py @@ -0,0 +1 @@ +# should be included diff --git a/tests/data/nested_gitignore_tests/x.py b/tests/data/nested_gitignore_tests/x.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_black.py b/tests/test_black.py index 5ab25cd..098a9ec 100644 --- a/tests/test_black.py +++ b/tests/test_black.py @@ -1406,7 +1406,7 @@ class BlackTestCase(BlackBaseTestCase): ) self.assertEqual(sorted(expected), sorted(sources)) - def test_gitingore_used_as_default(self) -> None: + def test_gitignore_used_as_default(self) -> None: path = Path(THIS_DIR / "data" / "include_exclude_tests") include = re.compile(r"\.pyi?$") extend_exclude = re.compile(r"/exclude/") @@ -1703,6 +1703,33 @@ class BlackTestCase(BlackBaseTestCase): ) self.assertEqual(sorted(expected), sorted(sources)) + def test_nested_gitignore(self) -> None: + path = Path(THIS_DIR / "data" / "nested_gitignore_tests") + include = re.compile(r"\.pyi?$") + exclude = re.compile(r"") + root_gitignore = black.files.get_gitignore(path) + report = black.Report() + expected: List[Path] = [ + Path(path / "x.py"), + Path(path / "root/b.py"), + Path(path / "root/c.py"), + Path(path / "root/child/c.py"), + ] + this_abs = THIS_DIR.resolve() + sources = list( + black.gen_python_files( + path.iterdir(), + this_abs, + include, + exclude, + None, + None, + report, + root_gitignore, + ) + ) + self.assertEqual(sorted(expected), sorted(sources)) + def test_empty_include(self) -> None: path = THIS_DIR / "data" / "include_exclude_tests" report = black.Report() -- 2.39.2