Skip to content

Commit a7b4b63

Browse files
author
buddyspike
committed
ignore: correct handling of nested rules overriding wild card unignore
problem: filesystem_iterator loads .gitignore files in top-down order. subsequently, ignore module evaluates them in the order they are loaded. this creates a problem if we have unignored a rule (using a wild card) in a sub dir and ignored it again in a level further below (see the test included in this patch). solution: process ignores in reverse order. closes libgit2#4963
1 parent 0f40e68 commit a7b4b63

File tree

2 files changed

+50
-3
lines changed

2 files changed

+50
-3
lines changed

src/ignore.c

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -444,7 +444,7 @@ static bool ignore_lookup_in_rules(
444444
int git_ignore__lookup(
445445
int *out, git_ignores *ignores, const char *pathname, git_dir_flag dir_flag)
446446
{
447-
unsigned int i;
447+
size_t i;
448448
git_attr_file *file;
449449
git_attr_path path;
450450

@@ -458,8 +458,11 @@ int git_ignore__lookup(
458458
if (ignore_lookup_in_rules(out, ignores->ign_internal, &path))
459459
goto cleanup;
460460

461-
/* next process files in the path */
462-
git_vector_foreach(&ignores->ign_path, i, file) {
461+
/* next process files in the path.
462+
* this process has to process ignores in reverse order
463+
* to ensure correct prioritization of rules
464+
*/
465+
git_vector_rforeach(&ignores->ign_path, i, file) {
463466
if (ignore_lookup_in_rules(out, file, &path))
464467
goto cleanup;
465468
}

tests/ignore/status.c

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1290,3 +1290,47 @@ void test_ignore_status__leading_spaces_are_significant(void)
12901290
assert_is_ignored(" # not a comment");
12911291
assert_is_ignored("d.test");
12921292
}
1293+
1294+
void test_ignore_status__override_nested_wildcard_unignore(void)
1295+
{
1296+
git_repository *repo = cl_git_sandbox_init("empty_standard_repo");
1297+
git_status_list *statuslist;
1298+
git_status_options opts = GIT_STATUS_OPTIONS_INIT;
1299+
const git_status_entry *status;
1300+
1301+
cl_git_pass(git_futils_mkdir_r("empty_standard_repo/dir", 0777));
1302+
cl_git_pass(git_futils_mkdir_r("empty_standard_repo/dir/subdir", 0777));
1303+
cl_git_mkfile("empty_standard_repo/.gitignore", "a.test\n");
1304+
cl_git_mkfile("empty_standard_repo/dir/.gitignore", "!*.test\n");
1305+
cl_git_mkfile("empty_standard_repo/dir/subdir/.gitignore", "a.test\n");
1306+
cl_git_mkfile("empty_standard_repo/dir/a.test", "pong");
1307+
cl_git_mkfile("empty_standard_repo/dir/subdir/a.test", "pong");
1308+
1309+
opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
1310+
opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED | GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
1311+
1312+
cl_git_pass(git_status_list_new(&statuslist, repo, &opts));
1313+
cl_assert_equal_sz(4, git_status_list_entrycount(statuslist));
1314+
1315+
status = git_status_byindex(statuslist, 0);
1316+
cl_assert(status != NULL);
1317+
cl_assert_equal_s(".gitignore", status->index_to_workdir->old_file.path);
1318+
cl_assert_equal_i(GIT_STATUS_WT_NEW, status->status);
1319+
1320+
status = git_status_byindex(statuslist, 1);
1321+
cl_assert(status != NULL);
1322+
cl_assert_equal_s("dir/.gitignore", status->index_to_workdir->old_file.path);
1323+
cl_assert_equal_i(GIT_STATUS_WT_NEW, status->status);
1324+
1325+
status = git_status_byindex(statuslist, 2);
1326+
cl_assert(status != NULL);
1327+
cl_assert_equal_s("dir/a.test", status->index_to_workdir->old_file.path);
1328+
cl_assert_equal_i(GIT_STATUS_WT_NEW, status->status);
1329+
1330+
status = git_status_byindex(statuslist, 3);
1331+
cl_assert(status != NULL);
1332+
cl_assert_equal_s("dir/subdir/.gitignore", status->index_to_workdir->old_file.path);
1333+
cl_assert_equal_i(GIT_STATUS_WT_NEW, status->status);
1334+
1335+
git_status_list_free(statuslist);
1336+
}

0 commit comments

Comments
 (0)