summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarlos Martín Nieto <cmn@dwim.me>2015-06-02 20:24:57 +0200
committerCarlos Martín Nieto <cmn@dwim.me>2015-06-02 20:24:57 +0200
commit3b9d07177f68d71464f9b2ec883c1e3a376c16d1 (patch)
treea0b4bed04dfb164441fb294986da3031ccc9b144
parentfb6df50b7f250a4fd8b2fab257f119a5185e9bf5 (diff)
parentff19d60d68ef5f508a5a8ff30be6519598ff50e1 (diff)
downloadlibgit2-3b9d07177f68d71464f9b2ec883c1e3a376c16d1.tar.gz
Merge pull request #3138 from libgit2/cmn/v22-update
Maintenance updates for v0.22
-rw-r--r--include/git2.h1
-rw-r--r--src/attr.c10
-rw-r--r--src/attr_file.c52
-rw-r--r--src/checkout.c10
-rw-r--r--src/config.c11
-rw-r--r--src/config_file.c4
-rw-r--r--src/diff_driver.c2
-rw-r--r--src/diff_tform.c10
-rw-r--r--src/ignore.c50
-rw-r--r--src/reflog.c2
-rw-r--r--src/revwalk.c28
-rw-r--r--tests/attr/ignore.c79
-rw-r--r--tests/checkout/tree.c29
-rw-r--r--tests/config/read.c12
-rw-r--r--tests/status/ignore.c53
15 files changed, 293 insertions, 60 deletions
diff --git a/include/git2.h b/include/git2.h
index cf6b5cb89..d141c59d3 100644
--- a/include/git2.h
+++ b/include/git2.h
@@ -58,6 +58,7 @@
#include "git2/submodule.h"
#include "git2/tag.h"
#include "git2/transport.h"
+#include "git2/transaction.h"
#include "git2/tree.h"
#include "git2/types.h"
#include "git2/version.h"
diff --git a/src/attr.c b/src/attr.c
index a02172689..19a5a6157 100644
--- a/src/attr.c
+++ b/src/attr.c
@@ -254,13 +254,9 @@ static int attr_setup(git_repository *repo)
repo, GIT_ATTR_FILE__FROM_FILE, NULL, sys.ptr);
git_buf_free(&sys);
}
- if (error < 0) {
- if (error == GIT_ENOTFOUND) {
- giterr_clear();
- error = 0;
- } else
- return error;
- }
+
+ if (error != GIT_ENOTFOUND)
+ return error;
if ((error = preload_attr_file(
repo, GIT_ATTR_FILE__FROM_FILE,
diff --git a/src/attr_file.c b/src/attr_file.c
index 5b008b0e3..48c22e718 100644
--- a/src/attr_file.c
+++ b/src/attr_file.c
@@ -344,6 +344,7 @@ bool git_attr_fnmatch__match(
git_attr_fnmatch *match,
git_attr_path *path)
{
+ const char *relpath = path->path;
const char *filename;
int flags = 0;
@@ -360,6 +361,8 @@ bool git_attr_fnmatch__match(
if (git__prefixcmp(path->path, match->containing_dir))
return 0;
}
+
+ relpath += match->containing_dir_length;
}
if (match->flags & GIT_ATTR_FNMATCH_ICASE)
@@ -368,7 +371,7 @@ bool git_attr_fnmatch__match(
flags |= FNM_LEADING_DIR;
if (match->flags & GIT_ATTR_FNMATCH_FULLPATH) {
- filename = path->path;
+ filename = relpath;
flags |= FNM_PATHNAME;
} else {
filename = path->basename;
@@ -378,28 +381,33 @@ bool git_attr_fnmatch__match(
}
if ((match->flags & GIT_ATTR_FNMATCH_DIRECTORY) && !path->is_dir) {
- int matchval;
+ bool samename;
/* for attribute checks or root ignore checks, fail match */
if (!(match->flags & GIT_ATTR_FNMATCH_IGNORE) ||
path->basename == path->path)
return false;
- /* for ignore checks, use container of current item for check */
- path->basename[-1] = '\0';
flags |= FNM_LEADING_DIR;
- matchval = p_fnmatch(match->pattern, path->path, flags);
- path->basename[-1] = '/';
- return (matchval != FNM_NOMATCH);
+
+ /* fail match if this is a file with same name as ignored folder */
+ samename = (match->flags & GIT_ATTR_FNMATCH_ICASE) ?
+ !strcasecmp(match->pattern, relpath) :
+ !strcmp(match->pattern, relpath);
+
+ if (samename)
+ return false;
+
+ return (p_fnmatch(match->pattern, relpath, flags) != FNM_NOMATCH);
}
/* if path is a directory prefix of a negated pattern, then match */
if ((match->flags & GIT_ATTR_FNMATCH_NEGATIVE) && path->is_dir) {
- size_t pathlen = strlen(path->path);
+ size_t pathlen = strlen(relpath);
bool prefixed = (pathlen <= match->length) &&
((match->flags & GIT_ATTR_FNMATCH_ICASE) ?
- !strncasecmp(match->pattern, path->path, pathlen) :
- !strncmp(match->pattern, path->path, pathlen));
+ !strncasecmp(match->pattern, relpath, pathlen) :
+ !strncmp(match->pattern, relpath, pathlen));
if (prefixed && git_path_at_end_of_segment(&match->pattern[pathlen]))
return true;
@@ -604,7 +612,7 @@ int git_attr_fnmatch__parse(
}
if (context) {
- char *slash = strchr(context, '/');
+ char *slash = strrchr(context, '/');
size_t len;
if (slash) {
/* include the slash for easier matching */
@@ -614,27 +622,7 @@ int git_attr_fnmatch__parse(
}
}
- if ((spec->flags & GIT_ATTR_FNMATCH_FULLPATH) != 0 &&
- context != NULL && git_path_root(pattern) < 0)
- {
- /* use context path minus the trailing filename */
- char *slash = strrchr(context, '/');
- size_t contextlen = slash ? slash - context + 1 : 0;
-
- /* given an unrooted fullpath match from a file inside a repo,
- * prefix the pattern with the relative directory of the source file
- */
- spec->pattern = git_pool_malloc(
- pool, (uint32_t)(contextlen + spec->length + 1));
- if (spec->pattern) {
- memcpy(spec->pattern, context, contextlen);
- memcpy(spec->pattern + contextlen, pattern, spec->length);
- spec->length += contextlen;
- spec->pattern[spec->length] = '\0';
- }
- } else {
- spec->pattern = git_pool_strndup(pool, pattern, spec->length);
- }
+ spec->pattern = git_pool_strndup(pool, pattern, spec->length);
if (!spec->pattern) {
*base = git__next_line(pattern);
diff --git a/src/checkout.c b/src/checkout.c
index 6357579cc..458305aae 100644
--- a/src/checkout.c
+++ b/src/checkout.c
@@ -1749,6 +1749,9 @@ static int checkout_create_the_new(
int error = 0;
git_diff_delta *delta;
size_t i;
+ int caps = git_index_caps(data->index);
+
+ git_index_set_caps(data->index, caps & ~GIT_INDEXCAP_NO_FILEMODE);
git_vector_foreach(&data->diff->deltas, i, delta) {
if (actions[i] & CHECKOUT_ACTION__DEFER_REMOVE) {
@@ -1770,6 +1773,8 @@ static int checkout_create_the_new(
}
}
+ git_index_set_caps(data->index, caps);
+
return 0;
}
@@ -2471,7 +2476,12 @@ int git_checkout_iterator(
cleanup:
if (!error && data.index != NULL &&
(data.strategy & GIT_CHECKOUT_DONT_UPDATE_INDEX) == 0)
+ {
+ int caps = git_index_caps(data.index);
+ git_index_set_caps(data.index, caps & ~GIT_INDEXCAP_NO_FILEMODE);
error = git_index_write(data.index);
+ git_index_set_caps(data.index, caps);
+ }
git_diff_free(data.diff);
git_iterator_free(workdir);
diff --git a/src/config.c b/src/config.c
index 0f8c24465..e2c7563c2 100644
--- a/src/config.c
+++ b/src/config.c
@@ -335,7 +335,6 @@ typedef struct {
git_config_iterator *current;
const git_config *cfg;
regex_t regex;
- int has_regex;
size_t i;
} all_iter;
@@ -470,9 +469,8 @@ int git_config_iterator_glob_new(git_config_iterator **out, const git_config *cf
iter = git__calloc(1, sizeof(all_iter));
GITERR_CHECK_ALLOC(iter);
- if ((result = regcomp(&iter->regex, regexp, REG_EXTENDED)) < 0) {
+ if ((result = regcomp(&iter->regex, regexp, REG_EXTENDED)) != 0) {
giterr_set_regex(&iter->regex, result);
- regfree(&iter->regex);
git__free(iter);
return -1;
}
@@ -505,7 +503,7 @@ int git_config_backend_foreach_match(
int error = 0;
if (regexp != NULL) {
- if ((error = regcomp(&regex, regexp, REG_EXTENDED)) < 0) {
+ if ((error = regcomp(&regex, regexp, REG_EXTENDED)) != 0) {
giterr_set_regex(&regex, error);
regfree(&regex);
return -1;
@@ -895,7 +893,8 @@ void multivar_iter_free(git_config_iterator *_iter)
iter->iter->free(iter->iter);
git__free(iter->name);
- regfree(&iter->regex);
+ if (iter->have_regex)
+ regfree(&iter->regex);
git__free(iter);
}
@@ -916,7 +915,7 @@ int git_config_multivar_iterator_new(git_config_iterator **out, const git_config
if (regexp != NULL) {
error = regcomp(&iter->regex, regexp, REG_EXTENDED);
- if (error < 0) {
+ if (error != 0) {
giterr_set_regex(&iter->regex, error);
error = -1;
regfree(&iter->regex);
diff --git a/src/config_file.c b/src/config_file.c
index 268cced09..d19f3c4e9 100644
--- a/src/config_file.c
+++ b/src/config_file.c
@@ -568,7 +568,7 @@ static int config_set_multivar(
}
result = regcomp(&preg, regexp, REG_EXTENDED);
- if (result < 0) {
+ if (result != 0) {
giterr_set_regex(&preg, result);
result = -1;
goto out;
@@ -654,7 +654,7 @@ static int config_delete_multivar(git_config_backend *cfg, const char *name, con
refcounted_strmap_free(map);
result = regcomp(&preg, regexp, REG_EXTENDED);
- if (result < 0) {
+ if (result != 0) {
giterr_set_regex(&preg, result);
result = -1;
goto out;
diff --git a/src/diff_driver.c b/src/diff_driver.c
index c3c5f365b..a397c7206 100644
--- a/src/diff_driver.c
+++ b/src/diff_driver.c
@@ -116,7 +116,7 @@ static int diff_driver_add_patterns(
if (error < 0)
break;
- if ((error = regcomp(&pat->re, buf.ptr, regex_flags)) < 0) {
+ if ((error = regcomp(&pat->re, buf.ptr, regex_flags)) != 0) {
/* if regex fails to compile, warn? fail? */
error = giterr_set_regex(&pat->re, error);
regfree(&pat->re);
diff --git a/src/diff_tform.c b/src/diff_tform.c
index d576317f0..37740a52a 100644
--- a/src/diff_tform.c
+++ b/src/diff_tform.c
@@ -84,11 +84,14 @@ static git_diff_delta *diff_delta__merge_like_cgit(
* index (i.e. not in HEAD nor workdir) is given as empty.
*/
if (dup->status == GIT_DELTA_DELETED) {
- if (a->status == GIT_DELTA_ADDED)
+ if (a->status == GIT_DELTA_ADDED) {
dup->status = GIT_DELTA_UNMODIFIED;
+ dup->nfiles = 2;
+ }
/* else don't overwrite DELETE status */
} else {
dup->status = a->status;
+ dup->nfiles = a->nfiles;
}
git_oid_cpy(&dup->old_file.id, &a->old_file.id);
@@ -118,10 +121,13 @@ static git_diff_delta *diff_delta__merge_like_cgit_reversed(
return dup;
if (dup->status == GIT_DELTA_DELETED) {
- if (b->status == GIT_DELTA_ADDED)
+ if (b->status == GIT_DELTA_ADDED) {
dup->status = GIT_DELTA_UNMODIFIED;
+ dup->nfiles = 2;
+ }
} else {
dup->status = b->status;
+ dup->nfiles = b->nfiles;
}
git_oid_cpy(&dup->old_file.id, &b->old_file.id);
diff --git a/src/ignore.c b/src/ignore.c
index 6e00f7db5..7b483ddf1 100644
--- a/src/ignore.c
+++ b/src/ignore.c
@@ -11,6 +11,41 @@
#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
+ *
+ * foo/bar
+ * !bar
+ *
+ * would result in foo/bar being unignored again.
+ */
+static int does_negate_pattern(git_attr_fnmatch *rule, git_attr_fnmatch *neg)
+{
+ 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)
+ 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 false;
+}
+
+/**
* A negative ignore can only unignore a file which is given explicitly before, thus
*
* foo
@@ -31,6 +66,8 @@ static int does_negate_rule(int *out, git_vector *rules, git_attr_fnmatch *match
char *path;
git_buf buf = GIT_BUF_INIT;
+ *out = 0;
+
/* path of the file relative to the workdir, so we match the rules in subdirs */
if (match->containing_dir) {
git_buf_puts(&buf, match->containing_dir);
@@ -41,9 +78,14 @@ static int does_negate_rule(int *out, git_vector *rules, git_attr_fnmatch *match
path = git_buf_detach(&buf);
git_vector_foreach(rules, i, rule) {
- /* no chance of matching w/o a wilcard */
- if (!(rule->flags & GIT_ATTR_FNMATCH_HASWILD))
- continue;
+ if (!(rule->flags & GIT_ATTR_FNMATCH_HASWILD)) {
+ if (does_negate_pattern(rule, match)) {
+ *out = 1;
+ goto out;
+ }
+ else
+ continue;
+ }
/*
* If we're dealing with a directory (which we know via the
@@ -62,7 +104,6 @@ static int does_negate_rule(int *out, git_vector *rules, git_attr_fnmatch *match
if (error < 0)
goto out;
-
if ((error = p_fnmatch(git_buf_cstr(&buf), path, FNM_PATHNAME)) < 0) {
giterr_set(GITERR_INVALID, "error matching pattern");
goto out;
@@ -76,7 +117,6 @@ static int does_negate_rule(int *out, git_vector *rules, git_attr_fnmatch *match
}
}
- *out = 0;
error = 0;
out:
diff --git a/src/reflog.c b/src/reflog.c
index 22aa7d8ed..9ce9aee6f 100644
--- a/src/reflog.c
+++ b/src/reflog.c
@@ -193,7 +193,7 @@ int git_reflog_drop(git_reflog *reflog, size_t idx, int rewrite_previous_entry)
entry = (git_reflog_entry *)git_reflog_entry_byindex(reflog, idx);
if (entry == NULL) {
- giterr_set(GITERR_REFERENCE, "No reflog entry at index "PRIuZ, idx);
+ giterr_set(GITERR_REFERENCE, "No reflog entry at index %"PRIuZ, idx);
return GIT_ENOTFOUND;
}
diff --git a/src/revwalk.c b/src/revwalk.c
index e44385d48..83c2ff8c7 100644
--- a/src/revwalk.c
+++ b/src/revwalk.c
@@ -39,11 +39,31 @@ git_commit_list_node *git_revwalk__commit_lookup(
return commit;
}
+typedef git_array_t(git_commit_list_node*) commit_list_node_array;
+
+static bool interesting_arr(commit_list_node_array arr)
+{
+ git_commit_list_node **n;
+ size_t i = 0, size;
+
+ size = git_array_size(arr);
+ for (i = 0; i < size; i++) {
+ n = git_array_get(arr, i);
+ if (!*n)
+ break;
+
+ if (!(*n)->uninteresting)
+ return true;
+ }
+
+ return false;
+}
+
static int mark_uninteresting(git_revwalk *walk, git_commit_list_node *commit)
{
int error;
unsigned short i;
- git_array_t(git_commit_list_node *) pending = GIT_ARRAY_INIT;
+ commit_list_node_array pending = GIT_ARRAY_INIT;
git_commit_list_node **tmp;
assert(commit);
@@ -64,7 +84,7 @@ static int mark_uninteresting(git_revwalk *walk, git_commit_list_node *commit)
tmp = git_array_pop(pending);
commit = tmp ? *tmp : NULL;
- } while (commit != NULL);
+ } while (commit != NULL && !interesting_arr(pending));
git_array_clear(pending);
@@ -142,6 +162,10 @@ static int push_commit(git_revwalk *walk, const git_oid *oid, int uninteresting,
if (commit == NULL)
return -1; /* error already reported by failed lookup */
+ /* A previous hide already told us we don't want this commit */
+ if (commit->uninteresting)
+ return 0;
+
if (uninteresting)
walk->did_hide = 1;
else
diff --git a/tests/attr/ignore.c b/tests/attr/ignore.c
index b187db01c..7d1eef593 100644
--- a/tests/attr/ignore.c
+++ b/tests/attr/ignore.c
@@ -146,6 +146,24 @@ void test_attr_ignore__skip_gitignore_directory(void)
assert_is_ignored(true, "NewFolder/NewFolder/File.txt");
}
+void test_attr_ignore__subdirectory_gitignore(void)
+{
+ p_unlink("attr/.gitignore");
+ cl_assert(!git_path_exists("attr/.gitignore"));
+ cl_git_mkfile(
+ "attr/.gitignore",
+ "file1\n");
+ p_mkdir("attr/dir", 0777);
+ cl_git_mkfile(
+ "attr/dir/.gitignore",
+ "file2/\n");
+
+ assert_is_ignored(true, "file1");
+ assert_is_ignored(true, "dir/file1");
+ assert_is_ignored(true, "dir/file2/actual_file"); /* in ignored dir */
+ assert_is_ignored(false, "dir/file3");
+}
+
void test_attr_ignore__expand_tilde_to_homedir(void)
{
git_config *cfg;
@@ -173,3 +191,64 @@ void test_attr_ignore__expand_tilde_to_homedir(void)
assert_is_ignored(false, "example.global_with_tilde");
}
+
+/* Ensure that the .gitignore in the subdirectory only affects
+ * items in the subdirectory. */
+void test_attr_ignore__gitignore_in_subdir(void)
+{
+ cl_git_pass(p_unlink("attr/.gitignore"));
+
+ cl_must_pass(p_mkdir("attr/dir1", 0777));
+ cl_must_pass(p_mkdir("attr/dir1/dir2", 0777));
+ cl_must_pass(p_mkdir("attr/dir1/dir2/dir3", 0777));
+
+ cl_git_mkfile("attr/dir1/dir2/dir3/.gitignore", "dir1/\ndir1/subdir/");
+
+ assert_is_ignored(false, "dir1/file");
+ assert_is_ignored(false, "dir1/dir2/file");
+ assert_is_ignored(false, "dir1/dir2/dir3/file");
+ assert_is_ignored(true, "dir1/dir2/dir3/dir1/file");
+ assert_is_ignored(true, "dir1/dir2/dir3/dir1/subdir/foo");
+
+ if (cl_repo_get_bool(g_repo, "core.ignorecase")) {
+ cl_git_mkfile("attr/dir1/dir2/dir3/.gitignore", "DiR1/\nDiR1/subdir/\n");
+
+ assert_is_ignored(false, "dir1/file");
+ assert_is_ignored(false, "dir1/dir2/file");
+ assert_is_ignored(false, "dir1/dir2/dir3/file");
+ assert_is_ignored(true, "dir1/dir2/dir3/dir1/file");
+ assert_is_ignored(true, "dir1/dir2/dir3/dir1/subdir/foo");
+ }
+}
+
+/* Ensure that files do not match folder cases */
+void test_attr_ignore__dont_ignore_files_for_folder(void)
+{
+ cl_git_pass(p_unlink("attr/.gitignore"));
+
+ cl_git_mkfile("attr/dir/.gitignore", "test/\n");
+
+ /* Create "test" as a file; ensure it is not ignored. */
+ cl_git_mkfile("attr/dir/test", "This is a file.");
+
+ assert_is_ignored(false, "dir/test");
+ if (cl_repo_get_bool(g_repo, "core.ignorecase"))
+ assert_is_ignored(false, "dir/TeSt");
+
+ /* Create "test" as a directory; ensure it is ignored. */
+ cl_git_pass(p_unlink("attr/dir/test"));
+ cl_must_pass(p_mkdir("attr/dir/test", 0777));
+
+ assert_is_ignored(true, "dir/test");
+ if (cl_repo_get_bool(g_repo, "core.ignorecase"))
+ assert_is_ignored(true, "dir/TeSt");
+
+ /* Remove "test" entirely; ensure it is not ignored.
+ * (As it doesn't exist, it is not a directory.)
+ */
+ cl_must_pass(p_rmdir("attr/dir/test"));
+
+ assert_is_ignored(false, "dir/test");
+ if (cl_repo_get_bool(g_repo, "core.ignorecase"))
+ assert_is_ignored(false, "dir/TeSt");
+}
diff --git a/tests/checkout/tree.c b/tests/checkout/tree.c
index 7e10b61af..7d99acb37 100644
--- a/tests/checkout/tree.c
+++ b/tests/checkout/tree.c
@@ -923,18 +923,43 @@ void test_checkout_tree__filemode_preserved_in_index(void)
git_index *index;
const git_index_entry *entry;
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
cl_git_pass(git_repository_index(&index, g_repo));
+ /* test a freshly added executable */
cl_git_pass(git_oid_fromstr(&executable_oid, "afe4393b2b2a965f06acf2ca9658eaa01e0cd6b6"));
cl_git_pass(git_commit_lookup(&commit, g_repo, &executable_oid));
- opts.checkout_strategy = GIT_CHECKOUT_FORCE;
-
cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts));
cl_assert(entry = git_index_get_bypath(index, "executable.txt", 0));
cl_assert_equal_i(0100755, entry->mode);
git_commit_free(commit);
+
+
+ /* Now start with a commit which has a text file */
+ cl_git_pass(git_oid_fromstr(&executable_oid, "cf80f8de9f1185bf3a05f993f6121880dd0cfbc9"));
+ cl_git_pass(git_commit_lookup(&commit, g_repo, &executable_oid));
+
+ cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts));
+ cl_assert(entry = git_index_get_bypath(index, "a/b.txt", 0));
+ cl_assert_equal_i(0100644, entry->mode);
+
+ git_commit_free(commit);
+
+
+ /* And then check out to a commit which converts the text file to an executable */
+ cl_git_pass(git_oid_fromstr(&executable_oid, "144344043ba4d4a405da03de3844aa829ae8be0e"));
+ cl_git_pass(git_commit_lookup(&commit, g_repo, &executable_oid));
+
+ cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts));
+ cl_assert(entry = git_index_get_bypath(index, "a/b.txt", 0));
+ cl_assert_equal_i(0100755, entry->mode);
+
+ git_commit_free(commit);
+
+
git_index_free(index);
}
diff --git a/tests/config/read.c b/tests/config/read.c
index 25672729f..b61a2be04 100644
--- a/tests/config/read.c
+++ b/tests/config/read.c
@@ -343,6 +343,18 @@ static void check_glob_iter(git_config *cfg, const char *regexp, int expected)
git_config_iterator_free(iter);
}
+void test_config_read__iterator_invalid_glob(void)
+{
+ git_config *cfg;
+ git_config_iterator *iter;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config9")));
+
+ cl_git_fail(git_config_iterator_glob_new(&iter, cfg, "*"));
+
+ git_config_free(cfg);
+}
+
void test_config_read__iterator_glob(void)
{
git_config *cfg;
diff --git a/tests/status/ignore.c b/tests/status/ignore.c
index a15b11d1d..3193d318e 100644
--- a/tests/status/ignore.c
+++ b/tests/status/ignore.c
@@ -892,6 +892,59 @@ void test_status_ignore__negative_ignores_without_trailing_slash_inside_ignores(
cl_assert(found_parent_child2_file);
}
+void test_status_ignore__negative_directory_ignores(void)
+{
+ static const char *test_files[] = {
+ "empty_standard_repo/parent/child1/bar.txt",
+ "empty_standard_repo/parent/child2/bar.txt",
+ "empty_standard_repo/parent/child3/foo.txt",
+ "empty_standard_repo/parent/child4/bar.txt",
+ "empty_standard_repo/parent/nested/child5/bar.txt",
+ "empty_standard_repo/parent/nested/child6/bar.txt",
+ "empty_standard_repo/parent/nested/child7/bar.txt",
+ "empty_standard_repo/padded_parent/child8/bar.txt",
+ NULL
+ };
+
+ make_test_data("empty_standard_repo", test_files);
+ cl_git_mkfile(
+ "empty_standard_repo/.gitignore",
+ "foo.txt\n"
+ "parent/child1\n"
+ "parent/child2\n"
+ "parent/child4\n"
+ "parent/nested/child5\n"
+ "nested/child6\n"
+ "nested/child7\n"
+ "padded_parent/child8\n"
+ /* test simple exact match */
+ "!parent/child1\n"
+ /* test negating file without negating dir */
+ "!parent/child2/bar.txt\n"
+ /* test negative pattern on dir with its content
+ * being ignored */
+ "!parent/child3\n"
+ /* test with partial match at end */
+ "!child4\n"
+ /* test with partial match with '/' at end */
+ "!nested/child5\n"
+ /* test with complete match */
+ "!nested/child6\n"
+ /* test with trailing '/' */
+ "!child7/\n"
+ /* test with partial dir match */
+ "!_parent/child8\n");
+
+ refute_is_ignored("parent/child1/bar.txt");
+ assert_is_ignored("parent/child2/bar.txt");
+ assert_is_ignored("parent/child3/foo.txt");
+ refute_is_ignored("parent/child4/bar.txt");
+ assert_is_ignored("parent/nested/child5/bar.txt");
+ refute_is_ignored("parent/nested/child6/bar.txt");
+ refute_is_ignored("parent/nested/child7/bar.txt");
+ assert_is_ignored("padded_parent/child8/bar.txt");
+}
+
void test_status_ignore__filename_with_cr(void)
{
int ignored;