Merge pull request #2257 from libgit2/rb/fix-submodules-with-tracked-content Update treatment of submodule-like directories with tracked content in the parent
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
diff --git a/src/diff.c b/src/diff.c
index 484273f..e62f45c 100644
--- a/src/diff.c
+++ b/src/diff.c
@@ -876,7 +876,7 @@ static int handle_unmatched_new_item(
DIFF_FLAG_IS_SET(diff, GIT_DIFF_RECURSE_IGNORED_DIRS));
/* do not advance into directories that contain a .git file */
- if (recurse_into_dir) {
+ if (recurse_into_dir && !contains_oitem) {
git_buf *full = NULL;
if (git_iterator_current_workdir_path(&full, info->new_iter) < 0)
return -1;
@@ -969,6 +969,16 @@ static int handle_unmatched_new_item(
if (git_submodule_lookup(NULL, info->repo, nitem->path) != 0) {
giterr_clear();
delta_type = GIT_DELTA_IGNORED;
+
+ /* if this contains a tracked item, treat as normal TREE */
+ if (contains_oitem) {
+ error = git_iterator_advance_into(&info->nitem, info->new_iter);
+ if (error != GIT_ENOTFOUND)
+ return error;
+
+ giterr_clear();
+ return git_iterator_advance(&info->nitem, info->new_iter);
+ }
}
}
diff --git a/tests/status/submodules.c b/tests/status/submodules.c
index 6d0d63a..63cf73f 100644
--- a/tests/status/submodules.c
+++ b/tests/status/submodules.c
@@ -1,7 +1,5 @@
#include "clar_libgit2.h"
-#include "buffer.h"
-#include "path.h"
-#include "posix.h"
+#include "fileops.h"
#include "status_helpers.h"
#include "../submodule/submodule_helpers.h"
@@ -389,3 +387,92 @@ void test_status_submodules__contained_untracked_repo(void)
g_repo, &opts, cb_status__match, &counts));
cl_assert_equal_i(5, counts.entry_count);
}
+
+void test_status_submodules__broken_stuff_that_git_allows(void)
+{
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ status_entry_counts counts;
+ git_repository *contained;
+ static const char *expected_files_with_broken[] = {
+ ".gitmodules",
+ "added",
+ "broken/tracked",
+ "deleted",
+ "ignored",
+ "modified",
+ "untracked"
+ };
+ static unsigned int expected_status_with_broken[] = {
+ GIT_STATUS_WT_MODIFIED,
+ GIT_STATUS_INDEX_NEW,
+ GIT_STATUS_INDEX_NEW,
+ GIT_STATUS_INDEX_DELETED,
+ GIT_STATUS_IGNORED,
+ GIT_STATUS_WT_MODIFIED,
+ GIT_STATUS_WT_NEW,
+ };
+
+ g_repo = setup_fixture_submodules();
+
+ opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
+ GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS |
+ GIT_STATUS_OPT_INCLUDE_IGNORED;
+
+ /* make a directory and stick a tracked item into the index */
+ {
+ git_index *idx;
+ cl_must_pass(p_mkdir("submodules/broken", 0777));
+ cl_git_mkfile("submodules/broken/tracked", "tracked content");
+ cl_git_pass(git_repository_index(&idx, g_repo));
+ cl_git_pass(git_index_add_bypath(idx, "broken/tracked"));
+ cl_git_pass(git_index_write(idx));
+ git_index_free(idx);
+ }
+
+ status_counts_init(
+ counts, expected_files_with_broken, expected_status_with_broken);
+ cl_git_pass(git_status_foreach_ext(
+ g_repo, &opts, cb_status__match, &counts));
+ cl_assert_equal_i(7, counts.entry_count);
+
+ /* directory with tracked items that looks a little bit like a repo */
+
+ cl_must_pass(p_mkdir("submodules/broken/.git", 0777));
+ cl_must_pass(p_mkdir("submodules/broken/.git/info", 0777));
+ cl_git_mkfile("submodules/broken/.git/info/exclude", "# bogus");
+
+ status_counts_init(
+ counts, expected_files_with_broken, expected_status_with_broken);
+ cl_git_pass(git_status_foreach_ext(
+ g_repo, &opts, cb_status__match, &counts));
+ cl_assert_equal_i(7, counts.entry_count);
+
+ /* directory with tracked items that is a repo */
+
+ cl_git_pass(git_futils_rmdir_r(
+ "submodules/broken/.git", NULL, GIT_RMDIR_REMOVE_FILES));
+ cl_git_pass(git_repository_init(&contained, "submodules/broken", false));
+ git_repository_free(contained);
+
+ status_counts_init(
+ counts, expected_files_with_broken, expected_status_with_broken);
+ cl_git_pass(git_status_foreach_ext(
+ g_repo, &opts, cb_status__match, &counts));
+ cl_assert_equal_i(7, counts.entry_count);
+
+ /* directory with tracked items that claims to be a submodule but is not */
+
+ cl_git_pass(git_futils_rmdir_r(
+ "submodules/broken/.git", NULL, GIT_RMDIR_REMOVE_FILES));
+ cl_git_append2file("submodules/.gitmodules",
+ "\n[submodule \"broken\"]\n"
+ "\tpath = broken\n"
+ "\turl = https://github.com/not/used\n\n");
+
+ status_counts_init(
+ counts, expected_files_with_broken, expected_status_with_broken);
+ cl_git_pass(git_status_foreach_ext(
+ g_repo, &opts, cb_status__match, &counts));
+ cl_assert_equal_i(7, counts.entry_count);
+}
+