diff --git a/pathspec/util.py b/pathspec/util.py index 52782d4..c108644 100644 --- a/pathspec/util.py +++ b/pathspec/util.py @@ -171,7 +171,7 @@ def detailed_match_files( else: # Remove files. for file in result_files: - del return_files[file] + return_files.pop(file, None) return return_files diff --git a/tests/test_01_util.py b/tests/test_01_util.py index b0e4ff8..a24f13a 100644 --- a/tests/test_01_util.py +++ b/tests/test_01_util.py @@ -26,6 +26,7 @@ from pathspec.util import ( RecursionError, check_match_file, + detailed_match_files, iter_tree_entries, iter_tree_files, match_file, @@ -115,6 +116,30 @@ def test_02_many(self): }) +class DetailedMatchFilesTest(unittest.TestCase): + """Test inclusion and exclusion bookkeeping for detailed matches.""" + + def test_negation_without_previous_match(self): + patterns = list(map(GitIgnoreBasicPattern, ['*.txt', '!*.log'])) + for all_matches in (False, True): + with self.subTest(all_matches=all_matches): + results = detailed_match_files(patterns, ['notes.txt', 'debug.log'], all_matches) + self.assertEqual(set(results), {'notes.txt'}) + self.assertEqual(results['notes.txt'].patterns, [patterns[0]]) + + def test_repeated_negation_and_reinclusion(self): + patterns = list(map(GitIgnoreBasicPattern, ['*.txt', '!notes.txt', '!notes.txt', 'notes.txt'])) + for all_matches in (False, True): + with self.subTest(all_matches=all_matches): + results = detailed_match_files(iter(patterns), iter(['notes.txt']), all_matches) + self.assertEqual(set(results), {'notes.txt'}) + self.assertEqual(results['notes.txt'].patterns, [patterns[-1]]) + + def test_negation_removes_previous_match(self): + patterns = list(map(GitIgnoreBasicPattern, ['*.txt', '!notes.txt'])) + self.assertEqual(detailed_match_files(patterns, ['notes.txt']), {}) + + class IterTreeTest(unittest.TestCase): """ The :class:`IterTreeTest` class tests :meth:`.iter_tree_entries` and