diff --git a/src/diff.c b/src/diff.c index c8670b53e..d5c0c8ba5 100644 --- a/src/diff.c +++ b/src/diff.c @@ -551,29 +551,27 @@ static int diff_from_iterators( * matched in old (and/or descend into directories as needed) */ else if (nitem && (!oitem || strcmp(oitem->path, nitem->path) > 0)) { - int is_ignored; - git_delta_t delta_type = GIT_DELTA_ADDED; + git_delta_t delta_type = GIT_DELTA_UNTRACKED; - /* contained in ignored parent directory, so this can be skipped. */ + /* check if contained in ignored parent directory */ if (git_buf_len(&ignore_prefix) && git__prefixcmp(nitem->path, git_buf_cstr(&ignore_prefix)) == 0) - { - if (git_iterator_advance(new_iter, &nitem) < 0) - goto fail; - - continue; - } - - is_ignored = git_iterator_current_is_ignored(new_iter); + delta_type = GIT_DELTA_IGNORED; if (S_ISDIR(nitem->mode)) { - /* recurse into directory if explicitly requested or - * if there are tracked items inside the directory + /* recurse into directory only if there are tracked items in + * it or if the user requested the contents of untracked + * directories and it is not under an ignored directory. */ - if ((diff->opts.flags & GIT_DIFF_RECURSE_UNTRACKED_DIRS) || - (oitem && git__prefixcmp(oitem->path, nitem->path) == 0)) + if ((oitem && git__prefixcmp(oitem->path, nitem->path) == 0) || + (delta_type == GIT_DELTA_UNTRACKED && + (diff->opts.flags & GIT_DIFF_RECURSE_UNTRACKED_DIRS) != 0)) { - if (is_ignored) + /* if this directory is ignored, remember it as the + * "ignore_prefix" for processing contained items + */ + if (delta_type == GIT_DELTA_UNTRACKED && + git_iterator_current_is_ignored(new_iter)) git_buf_sets(&ignore_prefix, nitem->path); if (git_iterator_advance_into_directory(new_iter, &nitem) < 0) @@ -581,12 +579,34 @@ static int diff_from_iterators( continue; } - delta_type = GIT_DELTA_UNTRACKED; } - else if (is_ignored) + + /* In core git, the next two "else if" clauses are effectively + * reversed -- i.e. when an untracked file contained in an + * ignored directory is individually ignored, it shows up as an + * ignored file in the diff list, even though other untracked + * files in the same directory are skipped completely. + * + * To me, this is odd. If the directory is ignored and the file + * is untracked, we should skip it consistently, regardless of + * whether it happens to match a pattern in the ignore file. + * + * To match the core git behavior, just reverse the following + * two "else if" cases so that individual file ignores are + * checked before container directory exclusions are used to + * skip the file. + */ + else if (delta_type == GIT_DELTA_IGNORED) { + if (git_iterator_advance(new_iter, &nitem) < 0) + goto fail; + continue; /* ignored parent directory, so skip completely */ + } + + else if (git_iterator_current_is_ignored(new_iter)) delta_type = GIT_DELTA_IGNORED; - else if (new_iter->type == GIT_ITERATOR_WORKDIR) - delta_type = GIT_DELTA_UNTRACKED; + + else if (new_iter->type != GIT_ITERATOR_WORKDIR) + delta_type = GIT_DELTA_ADDED; if (diff_delta__from_one(diff, delta_type, nitem) < 0 || git_iterator_advance(new_iter, &nitem) < 0) diff --git a/tests-clar/resources/issue_592b/.gitted/HEAD b/tests-clar/resources/issue_592b/.gitted/HEAD new file mode 100644 index 000000000..cb089cd89 --- /dev/null +++ b/tests-clar/resources/issue_592b/.gitted/HEAD @@ -0,0 +1 @@ +ref: refs/heads/master diff --git a/tests-clar/resources/issue_592b/.gitted/config b/tests-clar/resources/issue_592b/.gitted/config new file mode 100644 index 000000000..af107929f --- /dev/null +++ b/tests-clar/resources/issue_592b/.gitted/config @@ -0,0 +1,6 @@ +[core] + repositoryformatversion = 0 + filemode = true + bare = false + logallrefupdates = true + ignorecase = true diff --git a/tests-clar/resources/issue_592b/.gitted/description b/tests-clar/resources/issue_592b/.gitted/description new file mode 100644 index 000000000..498b267a8 --- /dev/null +++ b/tests-clar/resources/issue_592b/.gitted/description @@ -0,0 +1 @@ +Unnamed repository; edit this file 'description' to name the repository. diff --git a/tests-clar/resources/issue_592b/.gitted/hooks/post-update.sample b/tests-clar/resources/issue_592b/.gitted/hooks/post-update.sample new file mode 100755 index 000000000..ec17ec193 --- /dev/null +++ b/tests-clar/resources/issue_592b/.gitted/hooks/post-update.sample @@ -0,0 +1,8 @@ +#!/bin/sh +# +# An example hook script to prepare a packed repository for use over +# dumb transports. +# +# To enable this hook, rename this file to "post-update". + +exec git update-server-info diff --git a/tests-clar/resources/issue_592b/.gitted/index b/tests-clar/resources/issue_592b/.gitted/index new file mode 100644 index 000000000..596438216 Binary files /dev/null and b/tests-clar/resources/issue_592b/.gitted/index differ diff --git a/tests-clar/resources/issue_592b/.gitted/info/exclude b/tests-clar/resources/issue_592b/.gitted/info/exclude new file mode 100644 index 000000000..a5196d1be --- /dev/null +++ b/tests-clar/resources/issue_592b/.gitted/info/exclude @@ -0,0 +1,6 @@ +# git ls-files --others --exclude-from=.git/info/exclude +# Lines that start with '#' are comments. +# For a project mostly in C, the following would be a good set of +# exclude patterns (uncomment them if you want to use them): +# *.[oa] +# *~ diff --git a/tests-clar/resources/issue_592b/.gitted/logs/HEAD b/tests-clar/resources/issue_592b/.gitted/logs/HEAD new file mode 100644 index 000000000..6f3ba90cc --- /dev/null +++ b/tests-clar/resources/issue_592b/.gitted/logs/HEAD @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 3fbf1852f72fd268e36457b13a18cdd9a4c9ea35 Russell Belfer 1337205933 -0700 commit (initial): Initial commit diff --git a/tests-clar/resources/issue_592b/.gitted/logs/refs/heads/master b/tests-clar/resources/issue_592b/.gitted/logs/refs/heads/master new file mode 100644 index 000000000..6f3ba90cc --- /dev/null +++ b/tests-clar/resources/issue_592b/.gitted/logs/refs/heads/master @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 3fbf1852f72fd268e36457b13a18cdd9a4c9ea35 Russell Belfer 1337205933 -0700 commit (initial): Initial commit diff --git a/tests-clar/resources/issue_592b/.gitted/objects/3f/bf1852f72fd268e36457b13a18cdd9a4c9ea35 b/tests-clar/resources/issue_592b/.gitted/objects/3f/bf1852f72fd268e36457b13a18cdd9a4c9ea35 new file mode 100644 index 000000000..6eaf64b46 --- /dev/null +++ b/tests-clar/resources/issue_592b/.gitted/objects/3f/bf1852f72fd268e36457b13a18cdd9a4c9ea35 @@ -0,0 +1,2 @@ +xK +1]}%BwnAq xzVƃv ɂc&%9@9xdu.]".=EבO+ۘBEk\N_<>E U%9 \ No newline at end of file diff --git a/tests-clar/resources/issue_592b/.gitted/objects/6f/a891d3e578c83e1c03bdb9e0fdd8e6e934157f b/tests-clar/resources/issue_592b/.gitted/objects/6f/a891d3e578c83e1c03bdb9e0fdd8e6e934157f new file mode 100644 index 000000000..c4becfe2f Binary files /dev/null and b/tests-clar/resources/issue_592b/.gitted/objects/6f/a891d3e578c83e1c03bdb9e0fdd8e6e934157f differ diff --git a/tests-clar/resources/issue_592b/.gitted/objects/80/07d41d5794e6ce4d4d2c97e370d5a9aa6d5213 b/tests-clar/resources/issue_592b/.gitted/objects/80/07d41d5794e6ce4d4d2c97e370d5a9aa6d5213 new file mode 100644 index 000000000..aea14f2af Binary files /dev/null and b/tests-clar/resources/issue_592b/.gitted/objects/80/07d41d5794e6ce4d4d2c97e370d5a9aa6d5213 differ diff --git a/tests-clar/resources/issue_592b/.gitted/objects/a6/5fb6583a7c425284142f285bc359a2d6565513 b/tests-clar/resources/issue_592b/.gitted/objects/a6/5fb6583a7c425284142f285bc359a2d6565513 new file mode 100644 index 000000000..9b7407221 Binary files /dev/null and b/tests-clar/resources/issue_592b/.gitted/objects/a6/5fb6583a7c425284142f285bc359a2d6565513 differ diff --git a/tests-clar/resources/issue_592b/.gitted/objects/ae/be7a55922c7097ef91ca3a7bc327a901d87c2c b/tests-clar/resources/issue_592b/.gitted/objects/ae/be7a55922c7097ef91ca3a7bc327a901d87c2c new file mode 100644 index 000000000..1494ed822 Binary files /dev/null and b/tests-clar/resources/issue_592b/.gitted/objects/ae/be7a55922c7097ef91ca3a7bc327a901d87c2c differ diff --git a/tests-clar/resources/issue_592b/.gitted/objects/b3/44b055867fcdc1f01eaa75056a43e868eb4fbc b/tests-clar/resources/issue_592b/.gitted/objects/b3/44b055867fcdc1f01eaa75056a43e868eb4fbc new file mode 100644 index 000000000..7a6626636 Binary files /dev/null and b/tests-clar/resources/issue_592b/.gitted/objects/b3/44b055867fcdc1f01eaa75056a43e868eb4fbc differ diff --git a/tests-clar/resources/issue_592b/.gitted/objects/f7/d75fbfad8b1d2e307ced287ea78aad403cdce3 b/tests-clar/resources/issue_592b/.gitted/objects/f7/d75fbfad8b1d2e307ced287ea78aad403cdce3 new file mode 100644 index 000000000..65a1fd0d0 Binary files /dev/null and b/tests-clar/resources/issue_592b/.gitted/objects/f7/d75fbfad8b1d2e307ced287ea78aad403cdce3 differ diff --git a/tests-clar/resources/issue_592b/.gitted/refs/heads/master b/tests-clar/resources/issue_592b/.gitted/refs/heads/master new file mode 100644 index 000000000..c0a9ab495 --- /dev/null +++ b/tests-clar/resources/issue_592b/.gitted/refs/heads/master @@ -0,0 +1 @@ +3fbf1852f72fd268e36457b13a18cdd9a4c9ea35 diff --git a/tests-clar/resources/issue_592b/gitignore b/tests-clar/resources/issue_592b/gitignore new file mode 100644 index 000000000..8007d41d5 --- /dev/null +++ b/tests-clar/resources/issue_592b/gitignore @@ -0,0 +1 @@ +ignored/ diff --git a/tests-clar/resources/issue_592b/ignored/contained/ignored3.txt b/tests-clar/resources/issue_592b/ignored/contained/ignored3.txt new file mode 100644 index 000000000..b5dc7b073 --- /dev/null +++ b/tests-clar/resources/issue_592b/ignored/contained/ignored3.txt @@ -0,0 +1 @@ +I'm ignored diff --git a/tests-clar/resources/issue_592b/ignored/contained/tracked3.txt b/tests-clar/resources/issue_592b/ignored/contained/tracked3.txt new file mode 100644 index 000000000..b344b0558 --- /dev/null +++ b/tests-clar/resources/issue_592b/ignored/contained/tracked3.txt @@ -0,0 +1 @@ +You added me anyhow diff --git a/tests-clar/resources/issue_592b/ignored/ignored2.txt b/tests-clar/resources/issue_592b/ignored/ignored2.txt new file mode 100644 index 000000000..b5dc7b073 --- /dev/null +++ b/tests-clar/resources/issue_592b/ignored/ignored2.txt @@ -0,0 +1 @@ +I'm ignored diff --git a/tests-clar/resources/issue_592b/ignored/tracked2.txt b/tests-clar/resources/issue_592b/ignored/tracked2.txt new file mode 100644 index 000000000..6fa891d3e --- /dev/null +++ b/tests-clar/resources/issue_592b/ignored/tracked2.txt @@ -0,0 +1 @@ +You like me diff --git a/tests-clar/resources/issue_592b/ignored1.txt b/tests-clar/resources/issue_592b/ignored1.txt new file mode 100644 index 000000000..b5dc7b073 --- /dev/null +++ b/tests-clar/resources/issue_592b/ignored1.txt @@ -0,0 +1 @@ +I'm ignored diff --git a/tests-clar/resources/issue_592b/tracked1.txt b/tests-clar/resources/issue_592b/tracked1.txt new file mode 100644 index 000000000..6fa891d3e --- /dev/null +++ b/tests-clar/resources/issue_592b/tracked1.txt @@ -0,0 +1 @@ +You like me diff --git a/tests-clar/status/worktree.c b/tests-clar/status/worktree.c index e36f7e2ea..d94f004ba 100644 --- a/tests-clar/status/worktree.c +++ b/tests-clar/status/worktree.c @@ -45,9 +45,9 @@ void test_status_worktree__whole_repository(void) git_status_foreach(repo, cb_status__normal, &counts) ); - cl_assert(counts.entry_count == counts.expected_entry_count); - cl_assert(counts.wrong_status_flags_count == 0); - cl_assert(counts.wrong_sorted_path == 0); + cl_assert_equal_i(counts.expected_entry_count, counts.entry_count); + cl_assert_equal_i(0, counts.wrong_status_flags_count); + cl_assert_equal_i(0, counts.wrong_sorted_path); } /* this test is equivalent to t18-status.c:statuscb1 */ @@ -58,7 +58,7 @@ void test_status_worktree__empty_repository(void) cl_git_pass(git_status_foreach(repo, cb_status__count, &count)); - cl_assert(count == 0); + cl_assert_equal_i(0, count); } static int remove_file_cb(void *data, git_buf *file) @@ -100,9 +100,9 @@ void test_status_worktree__purged_worktree(void) git_status_foreach(repo, cb_status__normal, &counts) ); - cl_assert(counts.entry_count == counts.expected_entry_count); - cl_assert(counts.wrong_status_flags_count == 0); - cl_assert(counts.wrong_sorted_path == 0); + cl_assert_equal_i(counts.expected_entry_count, counts.entry_count); + cl_assert_equal_i(0, counts.wrong_status_flags_count); + cl_assert_equal_i(0, counts.wrong_sorted_path); } /* this test is similar to t18-status.c:statuscb3 */ @@ -135,10 +135,9 @@ void test_status_worktree__swap_subdir_and_file(void) git_status_foreach_ext(repo, &opts, cb_status__normal, &counts) ); - cl_assert(counts.entry_count == counts.expected_entry_count); - cl_assert(counts.wrong_status_flags_count == 0); - cl_assert(counts.wrong_sorted_path == 0); - + cl_assert_equal_i(counts.expected_entry_count, counts.entry_count); + cl_assert_equal_i(0, counts.wrong_status_flags_count); + cl_assert_equal_i(0, counts.wrong_sorted_path); } void test_status_worktree__swap_subdir_with_recurse_and_pathspec(void) @@ -171,9 +170,9 @@ void test_status_worktree__swap_subdir_with_recurse_and_pathspec(void) git_status_foreach_ext(repo, &opts, cb_status__normal, &counts) ); - cl_assert(counts.entry_count == counts.expected_entry_count); - cl_assert(counts.wrong_status_flags_count == 0); - cl_assert(counts.wrong_sorted_path == 0); + cl_assert_equal_i(counts.expected_entry_count, counts.entry_count); + cl_assert_equal_i(0, counts.wrong_status_flags_count); + cl_assert_equal_i(0, counts.wrong_sorted_path); } /* this test is equivalent to t18-status.c:singlestatus0 */ @@ -347,6 +346,65 @@ void test_status_worktree__issue_592_5(void) git_buf_free(&path); } +void test_status_worktree__issue_592_ignores_0(void) +{ + int count = 0; + status_entry_single st; + git_repository *repo = cl_git_sandbox_init("issue_592"); + + cl_git_pass(git_status_foreach(repo, cb_status__count, &count)); + cl_assert_equal_i(0, count); + + cl_git_rewritefile("issue_592/.gitignore", + ".gitignore\n*.txt\nc/\n[tT]*/\n"); + + cl_git_pass(git_status_foreach(repo, cb_status__count, &count)); + cl_assert_equal_i(1, count); + + /* This is a situation where the behavior of libgit2 is + * different from core git. Core git will show ignored.txt + * in the list of ignored files, even though the directory + * "t" is ignored and the file is untracked because we have + * the explicit "*.txt" ignore rule. Libgit2 just excludes + * all untracked files that are contained within ignored + * directories without explicitly listing them. + */ + cl_git_rewritefile("issue_592/t/ignored.txt", "ping"); + + memset(&st, 0, sizeof(st)); + cl_git_pass(git_status_foreach(repo, cb_status__single, &st)); + cl_assert_equal_i(1, st.count); + cl_assert(st.status == GIT_STATUS_IGNORED); + + cl_git_rewritefile("issue_592/c/ignored_by_dir", "ping"); + + memset(&st, 0, sizeof(st)); + cl_git_pass(git_status_foreach(repo, cb_status__single, &st)); + cl_assert_equal_i(1, st.count); + cl_assert(st.status == GIT_STATUS_IGNORED); + + cl_git_rewritefile("issue_592/t/ignored_by_dir_pattern", "ping"); + + memset(&st, 0, sizeof(st)); + cl_git_pass(git_status_foreach(repo, cb_status__single, &st)); + cl_assert_equal_i(1, st.count); + cl_assert(st.status == GIT_STATUS_IGNORED); +} + +void test_status_worktree__issue_592_ignored_dirs_with_tracked_content(void) +{ + int count = 0; + git_repository *repo = cl_git_sandbox_init("issue_592b"); + + cl_git_pass(git_status_foreach(repo, cb_status__count, &count)); + cl_assert_equal_i(1, count); + + /* if we are really mimicking core git, then only ignored1.txt + * at the top level will show up in the ignores list here. + * everything else will be unmodified or skipped completely. + */ +} + void test_status_worktree__cannot_retrieve_the_status_of_a_bare_repository(void) { git_repository *repo; @@ -374,7 +432,7 @@ void test_status_worktree__first_commit_in_progress(void) memset(&result, 0, sizeof(result)); cl_git_pass(git_status_foreach(repo, cb_status__single, &result)); - cl_assert(result.count == 1); + cl_assert_equal_i(1, result.count); cl_assert(result.status == GIT_STATUS_WT_NEW); cl_git_pass(git_repository_index(&index, repo)); @@ -383,7 +441,7 @@ void test_status_worktree__first_commit_in_progress(void) memset(&result, 0, sizeof(result)); cl_git_pass(git_status_foreach(repo, cb_status__single, &result)); - cl_assert(result.count == 1); + cl_assert_equal_i(1, result.count); cl_assert(result.status == GIT_STATUS_INDEX_NEW); git_index_free(index);