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

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 45 additions & 16 deletions src/ignore.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,35 +11,64 @@
#define GIT_IGNORE_DEFAULT_RULES ".\n..\n.git\n"

/**
* A negative ignore pattern can match a positive one without
* wildcards if its pattern equals the tail of the positive
* pattern. Thus
* A negative ignore pattern can negate a positive one without
* wildcards if it is a basename only and equals the basename of
* the positive pattern. Thus
*
* foo/bar
* !bar
*
* would result in foo/bar being unignored again.
* would result in foo/bar being unignored again while
*
* moo/foo/bar
* !foo/bar
*
* would do nothing. The reverse also holds true: a positive
* basename pattern can be negated by unignoring the basename in
* subdirectories. Thus
*
* bar
* !foo/bar
*
* would result in foo/bar being unignored again. As with the
* first case,
*
* foo/bar
* !moo/foo/bar
*
* would do nothing, again.
*/
static int does_negate_pattern(git_attr_fnmatch *rule, git_attr_fnmatch *neg)
{
git_attr_fnmatch *longer, *shorter;
char *p;

if ((rule->flags & GIT_ATTR_FNMATCH_NEGATIVE) == 0
&& (neg->flags & GIT_ATTR_FNMATCH_NEGATIVE) != 0) {
/*
* no chance of matching if rule is shorter than
* the negated one
*/
if (rule->length < neg->length)

/* If lengths match we need to have an exact match */
if (rule->length == neg->length) {
return strcmp(rule->pattern, neg->pattern) == 0;
} else if (rule->length < neg->length) {
shorter = rule;
longer = neg;
} else {
shorter = neg;
longer = rule;
}

/* Otherwise, we need to check if the shorter
* rule is a basename only (that is, it contains
* no path separator) and, if so, if it
* matches the tail of the longer rule */
p = longer->pattern + longer->length - shorter->length;

if (p[-1] != '/')
return false;
if (memchr(shorter->pattern, '/', shorter->length) != NULL)
return false;

/*
* shift pattern so its tail aligns with the
* negated pattern
*/
p = rule->pattern + rule->length - neg->length;
if (strcmp(p, neg->pattern) == 0)
return true;
return memcmp(p, shorter->pattern, shorter->length) == 0;
}

return false;
Expand Down
38 changes: 38 additions & 0 deletions tests/status/ignore.c
Original file line number Diff line number Diff line change
Expand Up @@ -945,6 +945,44 @@ void test_status_ignore__negative_directory_ignores(void)
assert_is_ignored("padded_parent/child8/bar.txt");
}

void test_status_ignore__unignore_entry_in_ignored_dir(void)
{
static const char *test_files[] = {
"empty_standard_repo/bar.txt",
"empty_standard_repo/parent/bar.txt",
"empty_standard_repo/parent/child/bar.txt",
"empty_standard_repo/nested/parent/child/bar.txt",
NULL
};

make_test_data("empty_standard_repo", test_files);
cl_git_mkfile(
"empty_standard_repo/.gitignore",
"bar.txt\n"
"!parent/child/bar.txt\n");

assert_is_ignored("bar.txt");
assert_is_ignored("parent/bar.txt");
refute_is_ignored("parent/child/bar.txt");
assert_is_ignored("nested/parent/child/bar.txt");
}

void test_status_ignore__do_not_unignore_basename_prefix(void)
{
static const char *test_files[] = {
"empty_standard_repo/foo_bar.txt",
NULL
};

make_test_data("empty_standard_repo", test_files);
cl_git_mkfile(
"empty_standard_repo/.gitignore",
"foo_bar.txt\n"
"!bar.txt\n");

assert_is_ignored("foo_bar.txt");
}

void test_status_ignore__filename_with_cr(void)
{
int ignored;
Expand Down