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 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): """