diff options
author | Carlos Martín Nieto <cmn@dwim.me> | 2015-06-02 20:24:57 +0200 |
---|---|---|
committer | Carlos Martín Nieto <cmn@dwim.me> | 2015-06-02 20:24:57 +0200 |
commit | 3b9d07177f68d71464f9b2ec883c1e3a376c16d1 (patch) | |
tree | a0b4bed04dfb164441fb294986da3031ccc9b144 | |
parent | fb6df50b7f250a4fd8b2fab257f119a5185e9bf5 (diff) | |
parent | ff19d60d68ef5f508a5a8ff30be6519598ff50e1 (diff) | |
download | libgit2-3b9d07177f68d71464f9b2ec883c1e3a376c16d1.tar.gz |
Merge pull request #3138 from libgit2/cmn/v22-update
Maintenance updates for v0.22
-rw-r--r-- | include/git2.h | 1 | ||||
-rw-r--r-- | src/attr.c | 10 | ||||
-rw-r--r-- | src/attr_file.c | 52 | ||||
-rw-r--r-- | src/checkout.c | 10 | ||||
-rw-r--r-- | src/config.c | 11 | ||||
-rw-r--r-- | src/config_file.c | 4 | ||||
-rw-r--r-- | src/diff_driver.c | 2 | ||||
-rw-r--r-- | src/diff_tform.c | 10 | ||||
-rw-r--r-- | src/ignore.c | 50 | ||||
-rw-r--r-- | src/reflog.c | 2 | ||||
-rw-r--r-- | src/revwalk.c | 28 | ||||
-rw-r--r-- | tests/attr/ignore.c | 79 | ||||
-rw-r--r-- | tests/checkout/tree.c | 29 | ||||
-rw-r--r-- | tests/config/read.c | 12 | ||||
-rw-r--r-- | tests/status/ignore.c | 53 |
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(®ex, regexp, REG_EXTENDED)) < 0) { + if ((error = regcomp(®ex, regexp, REG_EXTENDED)) != 0) { giterr_set_regex(®ex, error); regfree(®ex); 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; |