From 8992b2a3c3d1efc2e3588937fa6eb40af9573f94 Mon Sep 17 00:00:00 2001 From: Luxe7 <137878909+Luxe7@users.noreply.github.com> Date: Wed, 28 Oct 2026 13:27:35 +0800 Subject: [PATCH 1/2] Strip repeated current-directory prefixes before matching paths --- pathspec/util.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pathspec/util.py b/pathspec/util.py index bd2ae6d..b17322a 100644 --- a/pathspec/util.py +++ b/pathspec/util.py @@ -613,9 +613,11 @@ def normalize_file( # Make path relative. norm_file = norm_file[1:] - elif norm_file.startswith('./'): - # Remove current directory prefix. - norm_file = norm_file[2:] + # Remove all leading current directory prefixes without resolving '..'. + prefix_length = 0 + while norm_file.startswith('./', prefix_length): + prefix_length += 2 + norm_file = norm_file[prefix_length:] return norm_file From 5ef8ffda9ce22db55fd65162a4e031cd0bc4a577 Mon Sep 17 00:00:00 2001 From: Luxe7 <137878909+Luxe7@users.noreply.github.com> Date: Wed, 28 Oct 2026 13:29:01 +0800 Subject: [PATCH 2/2] Test repeated current-directory prefixes in path matching --- tests/test_01_util.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/test_01_util.py b/tests/test_01_util.py index 513c35c..340af8d 100644 --- a/tests/test_01_util.py +++ b/tests/test_01_util.py @@ -1010,6 +1010,27 @@ def test_01_purepath(self): second_spec = normalize_file('a.txt') self.assertEqual(first_spec, second_spec) + def test_repeated_current_directory_prefixes(self): + for file in ('./a.txt', '././a.txt', './././a.txt', '/././a.txt'): + with self.subTest(file=file): + self.assertEqual(normalize_file(file), 'a.txt') + + def test_current_directory_prefix_with_custom_separator(self): + self.assertEqual(normalize_file('.\\.\\a.txt', separators=('\\',)), 'a.txt') + + def test_preserve_other_dotted_paths(self): + for file in ('.hidden', '../a.txt', '.../a.txt', 'dir/../a.txt'): + with self.subTest(file=file): + self.assertEqual(normalize_file(file), file) + + def test_repeated_prefixes_with_anchored_patterns(self): + for spec_class in (pathspec.PathSpec, pathspec.GitIgnoreSpec): + with self.subTest(spec_class=spec_class): + spec = spec_class.from_lines('gitignore', ['/build/'], backend='simple') + file = '././build/output.txt' + self.assertTrue(spec.match_file(file)) + self.assertEqual(list(spec.match_files([file])), [file]) + class RecursionErrorDeprecationTest(TestCase): """