mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-07 23:19:28 +00:00
Merge pull request #3895 from pks-t/pks/negate-basename-in-subdirs
ignore: allow unignoring basenames in subdirectories
This commit is contained in:
commit
635a922274
61
src/ignore.c
61
src/ignore.c
@ -11,35 +11,64 @@
|
|||||||
#define GIT_IGNORE_DEFAULT_RULES ".\n..\n.git\n"
|
#define GIT_IGNORE_DEFAULT_RULES ".\n..\n.git\n"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A negative ignore pattern can match a positive one without
|
* A negative ignore pattern can negate a positive one without
|
||||||
* wildcards if its pattern equals the tail of the positive
|
* wildcards if it is a basename only and equals the basename of
|
||||||
* pattern. Thus
|
* the positive pattern. Thus
|
||||||
*
|
*
|
||||||
* foo/bar
|
* foo/bar
|
||||||
* !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)
|
static int does_negate_pattern(git_attr_fnmatch *rule, git_attr_fnmatch *neg)
|
||||||
{
|
{
|
||||||
|
git_attr_fnmatch *longer, *shorter;
|
||||||
char *p;
|
char *p;
|
||||||
|
|
||||||
if ((rule->flags & GIT_ATTR_FNMATCH_NEGATIVE) == 0
|
if ((rule->flags & GIT_ATTR_FNMATCH_NEGATIVE) == 0
|
||||||
&& (neg->flags & GIT_ATTR_FNMATCH_NEGATIVE) != 0) {
|
&& (neg->flags & GIT_ATTR_FNMATCH_NEGATIVE) != 0) {
|
||||||
/*
|
|
||||||
* no chance of matching if rule is shorter than
|
/* If lengths match we need to have an exact match */
|
||||||
* the negated one
|
if (rule->length == neg->length) {
|
||||||
*/
|
return strcmp(rule->pattern, neg->pattern) == 0;
|
||||||
if (rule->length < neg->length)
|
} 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;
|
return false;
|
||||||
|
|
||||||
/*
|
return memcmp(p, shorter->pattern, shorter->length) == 0;
|
||||||
* 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 false;
|
return false;
|
||||||
|
@ -945,6 +945,44 @@ void test_status_ignore__negative_directory_ignores(void)
|
|||||||
assert_is_ignored("padded_parent/child8/bar.txt");
|
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)
|
void test_status_ignore__filename_with_cr(void)
|
||||||
{
|
{
|
||||||
int ignored;
|
int ignored;
|
||||||
|
Loading…
Reference in New Issue
Block a user