Commit 361e919280b848991108794ce6bb329d93eef676

Vicent Marti 2014-04-09T12:09:30

Merge pull request #2257 from libgit2/rb/fix-submodules-with-tracked-content Update treatment of submodule-like directories with tracked content in the parent

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);
+}
+