Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Apply .gitignore correctly in every source entry #3336

Merged
merged 3 commits into from Nov 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGES.md
Expand Up @@ -18,6 +18,9 @@

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

- Fix incorrectly ignoring .gitignore presence when more than one source directory is
specified (#3336)

### Packaging

<!-- Changes to how Black is packaged, such as dependency requirements -->
Expand Down
18 changes: 11 additions & 7 deletions src/black/__init__.py
Expand Up @@ -30,6 +30,7 @@
import click
from click.core import ParameterSource
from mypy_extensions import mypyc_attr
from pathspec import PathSpec
from pathspec.patterns.gitwildmatch import GitWildMatchPatternError

from _black_version import version as __version__
Expand Down Expand Up @@ -625,6 +626,11 @@ def get_sources(
sources: Set[Path] = set()
root = ctx.obj["root"]

exclude_is_None = exclude is None
exclude = re_compile_maybe_verbose(DEFAULT_EXCLUDES) if exclude is None else exclude
gitignore = None # type: Optional[PathSpec]
root_gitignore = get_gitignore(root)
aaossa marked this conversation as resolved.
Show resolved Hide resolved

for s in src:
if s == "-" and stdin_filename:
p = Path(stdin_filename)
Expand Down Expand Up @@ -658,16 +664,14 @@ def get_sources(

sources.add(p)
elif p.is_dir():
if exclude is None:
exclude = re_compile_maybe_verbose(DEFAULT_EXCLUDES)
gitignore = get_gitignore(root)
if exclude_is_None:
aaossa marked this conversation as resolved.
Show resolved Hide resolved
p_gitignore = get_gitignore(p)
# No need to use p's gitignore if it is identical to root's gitignore
# (i.e. root and p point to the same directory).
if gitignore != p_gitignore:
gitignore += p_gitignore
else:
gitignore = None
if root_gitignore == p_gitignore:
gitignore = root_gitignore
else:
gitignore = root_gitignore + p_gitignore
sources.update(
gen_python_files(
p.iterdir(),
Expand Down
1 change: 1 addition & 0 deletions tests/data/gitignore_used_on_multiple_sources/.gitignore
@@ -0,0 +1 @@
a.py
Empty file.
Empty file.
Empty file.
Empty file.
11 changes: 11 additions & 0 deletions tests/test_black.py
Expand Up @@ -1956,6 +1956,17 @@ def test_gitignore_used_as_default(self) -> None:
ctx.obj["root"] = base
assert_collected_sources(src, expected, ctx=ctx, extend_exclude=r"/exclude/")

def test_gitignore_used_on_multiple_sources(self) -> None:
root = Path(DATA_DIR / "gitignore_used_on_multiple_sources")
expected = [
root / "dir1" / "b.py",
root / "dir2" / "b.py",
]
ctx = FakeContext()
ctx.obj["root"] = root
src = [root / "dir1", root / "dir2"]
assert_collected_sources(src, expected, ctx=ctx)

@patch("black.find_project_root", lambda *args: (THIS_DIR.resolve(), None))
def test_exclude_for_issue_1572(self) -> None:
# Exclude shouldn't touch files that were explicitly given to Black through the
Expand Down