|
156 | 156 | import logging |
157 | 157 | import os |
158 | 158 | import re |
| 159 | +import sys |
159 | 160 | import tomllib |
160 | 161 | from collections.abc import Iterable |
161 | 162 | from enum import StrEnum |
162 | 163 | from functools import cached_property |
163 | 164 | from operator import itemgetter |
164 | | -from pathlib import Path, PureWindowsPath |
| 165 | +from pathlib import Path |
165 | 166 | from random import randint |
166 | 167 | from re import escape |
167 | 168 | from typing import Any, Final, cast |
168 | 169 |
|
| 170 | +import gitignore_parser |
169 | 171 | from bumpversion.config import get_configuration # type: ignore[import-untyped] |
170 | 172 | from bumpversion.config.files import find_config_file # type: ignore[import-untyped] |
171 | 173 | from bumpversion.show import resolve_name # type: ignore[import-untyped] |
172 | 174 | from extra_platforms import is_github_ci |
173 | | -from gitignore_parser import parse_gitignore |
174 | 175 | from packaging.specifiers import SpecifierSet |
175 | 176 | from packaging.version import Version |
176 | 177 | from pydriller import Commit, Git, Repository # type: ignore[import-untyped] |
@@ -349,6 +350,58 @@ class TargetVersion(StrEnum): |
349 | 350 | """ |
350 | 351 |
|
351 | 352 |
|
| 353 | +# XXX Patch gitignore-parser to support Windows paths. |
| 354 | +# Refs: https://github.com/mherrmann/gitignore_parser/pull/61 |
| 355 | + |
| 356 | +_OriginalIgnoreRule = gitignore_parser.IgnoreRule |
| 357 | + |
| 358 | + |
| 359 | +class PatchedIgnoreRule(_OriginalIgnoreRule): |
| 360 | + """Patch version of ``IgnoreRule`` to support Windows paths. |
| 361 | +
|
| 362 | + Taken from: https://github.com/mherrmann/gitignore_parser/pull/61/files |
| 363 | + """ |
| 364 | + |
| 365 | + @staticmethod |
| 366 | + def _count_trailing_symbol(symbol: str, text: str) -> int: |
| 367 | + """Count the number of trailing characters in a string.""" |
| 368 | + count = 0 |
| 369 | + for char in reversed(str(text)): |
| 370 | + if char == symbol: |
| 371 | + count += 1 |
| 372 | + else: |
| 373 | + break |
| 374 | + return count |
| 375 | + |
| 376 | + def match(self, abs_path: str | Path) -> bool: |
| 377 | + matched = False |
| 378 | + if self.base_path: |
| 379 | + rel_path = ( |
| 380 | + gitignore_parser._normalize_path(abs_path) |
| 381 | + .relative_to(self.base_path) |
| 382 | + .as_posix() |
| 383 | + ) |
| 384 | + else: |
| 385 | + rel_path = gitignore_parser._normalize_path(abs_path).as_posix() |
| 386 | + # Path() strips the trailing following symbols on windows, so we need to |
| 387 | + # preserve it: ' ', '.' |
| 388 | + if sys.platform.startswith("win"): |
| 389 | + rel_path += " " * self._count_trailing_symbol(" ", abs_path) |
| 390 | + rel_path += "." * self._count_trailing_symbol(".", abs_path) |
| 391 | + # Path() strips the trailing slash, so we need to preserve it |
| 392 | + # in case of directory-only negation |
| 393 | + if self.negation and type(abs_path) is str and abs_path[-1] == "/": |
| 394 | + rel_path += "/" |
| 395 | + if rel_path.startswith("./"): |
| 396 | + rel_path = rel_path[2:] |
| 397 | + if re.search(self.regex, rel_path): |
| 398 | + matched = True |
| 399 | + return matched |
| 400 | + |
| 401 | + |
| 402 | +gitignore_parser.IgnoreRule = PatchedIgnoreRule |
| 403 | + |
| 404 | + |
352 | 405 | class JSONMetadata(json.JSONEncoder): |
353 | 406 | """Custom JSON encoder for metadata serialization.""" |
354 | 407 |
|
@@ -737,7 +790,7 @@ def glob_files(self, *patterns: str) -> list[Path]: |
737 | 790 | gitignore = None |
738 | 791 | if self.gitignore_exists: |
739 | 792 | logging.debug(f"Load {GITIGNORE_PATH} to filter out ignored files.") |
740 | | - gitignore = parse_gitignore(GITIGNORE_PATH) |
| 793 | + gitignore = gitignore_parser.parse_gitignore(GITIGNORE_PATH) |
741 | 794 |
|
742 | 795 | for file_path in iglob( |
743 | 796 | patterns, |
@@ -768,15 +821,9 @@ def glob_files(self, *patterns: str) -> list[Path]: |
768 | 821 | continue |
769 | 822 |
|
770 | 823 | # Skip files that are ignored by .gitignore. |
771 | | - if gitignore: |
772 | | - if gitignore( |
773 | | - # gitignore does not support WindowsPath. |
774 | | - file_path.as_posix() |
775 | | - if isinstance(file_path, PureWindowsPath) |
776 | | - else file_path |
777 | | - ): |
778 | | - logging.debug(f"Skip file matching {GITIGNORE_PATH}: {file_path}") |
779 | | - continue |
| 824 | + if gitignore and gitignore(file_path): |
| 825 | + logging.debug(f"Skip file matching {GITIGNORE_PATH}: {file_path}") |
| 826 | + continue |
780 | 827 |
|
781 | 828 | seen.add(normalized_path) |
782 | 829 | return sorted(seen) |
|
0 commit comments