Skip to content

Commit

Permalink
Fix pattern with slash in range (#55)
Browse files Browse the repository at this point in the history
When a slash appears in a range, it should NOT match a directory
separator.

Quoting https://git-scm.com/docs/gitignore#_pattern_format:

  The range notation, e.g. [a-zA-Z], can be used to match one of the
  characters in a range. See fnmatch(3) and the FNM_PATHNAME flag for
  a more detailed description.

And quoting what fnmatch(3) has to say about FNM_PATHNAME:

  match a slash in string only with a slash in pattern and not by an
  asterisk (*) or a question mark (?) metacharacter, nor by a bracket
  expression ([]) containing a slash.

Hence, it seems to me that a slash inside a range should be disregarded.
  • Loading branch information
jherland authored Oct 3, 2023
1 parent ffbfd79 commit d45a085
Show file tree
Hide file tree
Showing 2 changed files with 10 additions and 1 deletion.
2 changes: 1 addition & 1 deletion gitignore_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ def fnmatch_pathname_to_regex(
if j >= n:
res.append('\\[')
else:
stuff = pattern[i:j].replace('\\', '\\\\')
stuff = pattern[i:j].replace('\\', '\\\\').replace('/', '')
i = j + 1
if stuff[0] == '!':
stuff = ''.join(['^', stuff[1:]])
Expand Down
9 changes: 9 additions & 0 deletions tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,15 @@ def test_supports_path_type_argument(self):
self.assertTrue(matches(Path('/home/michael/file1')))
self.assertFalse(matches(Path('/home/michael/file2')))

def test_slash_in_range_does_not_match_dirs(self):
matches = _parse_gitignore_string('abc[X-Z/]def', fake_base_dir='/home/michael')
self.assertFalse(matches('/home/michael/abcdef'))
self.assertTrue(matches('/home/michael/abcXdef'))
self.assertTrue(matches('/home/michael/abcYdef'))
self.assertTrue(matches('/home/michael/abcZdef'))
self.assertFalse(matches('/home/michael/abc/def'))
self.assertFalse(matches('/home/michael/abcXYZdef'))

def _parse_gitignore_string(data: str, fake_base_dir: str = None):
with patch('builtins.open', mock_open(read_data=data)):
success = parse_gitignore(f'{fake_base_dir}/.gitignore', fake_base_dir)
Expand Down

0 comments on commit d45a085

Please sign in to comment.