Skip to content

Commit 0e9d29a

Browse files
authored
Apply .gitignore correctly in every source entry (#3336)
When passing multiple src directories, the root gitignore was only applied to the first processed source. The reason is that, in the first source, exclude is `None`, but then the value gets overridden by `re_compile_maybe_verbose(DEFAULT_EXCLUDES)`, so in the next iteration where the source is a directory, the condition is not met and sets the value of `gitignore` to `None`. To fix this problem, we store a boolean indicating if `exclude` is `None` and set the value of `exclude` to its default value if that's the case. This makes sure that the flow enters the correct condition on following iterations and also keeps the original value if the condition is not met. Also, the value of `gitignore` is initialized as `None` and overriden if necessary. The value of `root_gitignore` is always calculated to avoid using additional variables (at the small cost of additional computations). Signed-off-by: Antonio Ossa Guerra <[email protected]>
1 parent 2704dc7 commit 0e9d29a

File tree

8 files changed

+26
-7
lines changed

8 files changed

+26
-7
lines changed

CHANGES.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222

2323
<!-- Changes to how Black can be configured -->
2424

25+
- Fix incorrectly ignoring .gitignore presence when more than one source directory is
26+
specified (#3336)
27+
2528
### Packaging
2629

2730
<!-- Changes to how Black is packaged, such as dependency requirements -->

src/black/__init__.py

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import click
3131
from click.core import ParameterSource
3232
from mypy_extensions import mypyc_attr
33+
from pathspec import PathSpec
3334
from pathspec.patterns.gitwildmatch import GitWildMatchPatternError
3435

3536
from _black_version import version as __version__
@@ -627,6 +628,11 @@ def get_sources(
627628
sources: Set[Path] = set()
628629
root = ctx.obj["root"]
629630

631+
exclude_is_None = exclude is None
632+
exclude = re_compile_maybe_verbose(DEFAULT_EXCLUDES) if exclude is None else exclude
633+
gitignore = None # type: Optional[PathSpec]
634+
root_gitignore = get_gitignore(root)
635+
630636
for s in src:
631637
if s == "-" and stdin_filename:
632638
p = Path(stdin_filename)
@@ -660,16 +666,14 @@ def get_sources(
660666

661667
sources.add(p)
662668
elif p.is_dir():
663-
if exclude is None:
664-
exclude = re_compile_maybe_verbose(DEFAULT_EXCLUDES)
665-
gitignore = get_gitignore(root)
669+
if exclude_is_None:
666670
p_gitignore = get_gitignore(p)
667671
# No need to use p's gitignore if it is identical to root's gitignore
668672
# (i.e. root and p point to the same directory).
669-
if gitignore != p_gitignore:
670-
gitignore += p_gitignore
671-
else:
672-
gitignore = None
673+
if root_gitignore == p_gitignore:
674+
gitignore = root_gitignore
675+
else:
676+
gitignore = root_gitignore + p_gitignore
673677
sources.update(
674678
gen_python_files(
675679
p.iterdir(),
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
a.py

tests/data/gitignore_used_on_multiple_sources/dir1/a.py

Whitespace-only changes.

tests/data/gitignore_used_on_multiple_sources/dir1/b.py

Whitespace-only changes.

tests/data/gitignore_used_on_multiple_sources/dir2/a.py

Whitespace-only changes.

tests/data/gitignore_used_on_multiple_sources/dir2/b.py

Whitespace-only changes.

tests/test_black.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1998,6 +1998,17 @@ def test_gitignore_used_as_default(self) -> None:
19981998
ctx.obj["root"] = base
19991999
assert_collected_sources(src, expected, ctx=ctx, extend_exclude=r"/exclude/")
20002000

2001+
def test_gitignore_used_on_multiple_sources(self) -> None:
2002+
root = Path(DATA_DIR / "gitignore_used_on_multiple_sources")
2003+
expected = [
2004+
root / "dir1" / "b.py",
2005+
root / "dir2" / "b.py",
2006+
]
2007+
ctx = FakeContext()
2008+
ctx.obj["root"] = root
2009+
src = [root / "dir1", root / "dir2"]
2010+
assert_collected_sources(src, expected, ctx=ctx)
2011+
20012012
@patch("black.find_project_root", lambda *args: (THIS_DIR.resolve(), None))
20022013
def test_exclude_for_issue_1572(self) -> None:
20032014
# Exclude shouldn't touch files that were explicitly given to Black through the

0 commit comments

Comments
 (0)