Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 635a922

Browse files
author
Edward Thomson
authored
Merge pull request libgit2#3895 from pks-t/pks/negate-basename-in-subdirs
ignore: allow unignoring basenames in subdirectories
2 parents 26a8617 + fcb2c1c commit 635a922

File tree

2 files changed

+83
-16
lines changed

2 files changed

+83
-16
lines changed

src/ignore.c

Lines changed: 45 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,35 +11,64 @@
1111
#define GIT_IGNORE_DEFAULT_RULES ".\n..\n.git\n"
1212

1313
/**
14-
* A negative ignore pattern can match a positive one without
15-
* wildcards if its pattern equals the tail of the positive
16-
* pattern. Thus
14+
* A negative ignore pattern can negate a positive one without
15+
* wildcards if it is a basename only and equals the basename of
16+
* the positive pattern. Thus
1717
*
1818
* foo/bar
1919
* !bar
2020
*
21-
* would result in foo/bar being unignored again.
21+
* would result in foo/bar being unignored again while
22+
*
23+
* moo/foo/bar
24+
* !foo/bar
25+
*
26+
* would do nothing. The reverse also holds true: a positive
27+
* basename pattern can be negated by unignoring the basename in
28+
* subdirectories. Thus
29+
*
30+
* bar
31+
* !foo/bar
32+
*
33+
* would result in foo/bar being unignored again. As with the
34+
* first case,
35+
*
36+
* foo/bar
37+
* !moo/foo/bar
38+
*
39+
* would do nothing, again.
2240
*/
2341
static int does_negate_pattern(git_attr_fnmatch *rule, git_attr_fnmatch *neg)
2442
{
43+
git_attr_fnmatch *longer, *shorter;
2544
char *p;
2645

2746
if ((rule->flags & GIT_ATTR_FNMATCH_NEGATIVE) == 0
2847
&& (neg->flags & GIT_ATTR_FNMATCH_NEGATIVE) != 0) {
29-
/*
30-
* no chance of matching if rule is shorter than
31-
* the negated one
32-
*/
33-
if (rule->length < neg->length)
48+
49+
/* If lengths match we need to have an exact match */
50+
if (rule->length == neg->length) {
51+
return strcmp(rule->pattern, neg->pattern) == 0;
52+
} else if (rule->length < neg->length) {
53+
shorter = rule;
54+
longer = neg;
55+
} else {
56+
shorter = neg;
57+
longer = rule;
58+
}
59+
60+
/* Otherwise, we need to check if the shorter
61+
* rule is a basename only (that is, it contains
62+
* no path separator) and, if so, if it
63+
* matches the tail of the longer rule */
64+
p = longer->pattern + longer->length - shorter->length;
65+
66+
if (p[-1] != '/')
67+
return false;
68+
if (memchr(shorter->pattern, '/', shorter->length) != NULL)
3469
return false;
3570

36-
/*
37-
* shift pattern so its tail aligns with the
38-
* negated pattern
39-
*/
40-
p = rule->pattern + rule->length - neg->length;
41-
if (strcmp(p, neg->pattern) == 0)
42-
return true;
71+
return memcmp(p, shorter->pattern, shorter->length) == 0;
4372
}
4473

4574
return false;

tests/status/ignore.c

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -945,6 +945,44 @@ void test_status_ignore__negative_directory_ignores(void)
945945
assert_is_ignored("padded_parent/child8/bar.txt");
946946
}
947947

948+
void test_status_ignore__unignore_entry_in_ignored_dir(void)
949+
{
950+
static const char *test_files[] = {
951+
"empty_standard_repo/bar.txt",
952+
"empty_standard_repo/parent/bar.txt",
953+
"empty_standard_repo/parent/child/bar.txt",
954+
"empty_standard_repo/nested/parent/child/bar.txt",
955+
NULL
956+
};
957+
958+
make_test_data("empty_standard_repo", test_files);
959+
cl_git_mkfile(
960+
"empty_standard_repo/.gitignore",
961+
"bar.txt\n"
962+
"!parent/child/bar.txt\n");
963+
964+
assert_is_ignored("bar.txt");
965+
assert_is_ignored("parent/bar.txt");
966+
refute_is_ignored("parent/child/bar.txt");
967+
assert_is_ignored("nested/parent/child/bar.txt");
968+
}
969+
970+
void test_status_ignore__do_not_unignore_basename_prefix(void)
971+
{
972+
static const char *test_files[] = {
973+
"empty_standard_repo/foo_bar.txt",
974+
NULL
975+
};
976+
977+
make_test_data("empty_standard_repo", test_files);
978+
cl_git_mkfile(
979+
"empty_standard_repo/.gitignore",
980+
"foo_bar.txt\n"
981+
"!bar.txt\n");
982+
983+
assert_is_ignored("foo_bar.txt");
984+
}
985+
948986
void test_status_ignore__filename_with_cr(void)
949987
{
950988
int ignored;

0 commit comments

Comments
 (0)