Commit dc27772ce2b3d9e34c5e97fb0eb010e21c96ee33

Patrick Steinhardt 2018-03-30T13:12:26

Merge pull request #4378 from cjhoward92/fix/submodule-add-check-index submodule: check index for path and prefix before adding submodule

diff --git a/src/submodule.c b/src/submodule.c
index 3ec0307..3743466 100644
--- a/src/submodule.c
+++ b/src/submodule.c
@@ -149,6 +149,51 @@ static int find_by_path(const git_config_entry *entry, void *payload)
 	return 0;
 }
 
+/*
+ * Checks to see if the submodule shares its name with a file or directory that
+ * already exists on the index. If so, the submodule cannot be added.
+ */
+static int is_path_occupied(bool *occupied, git_repository *repo, const char *path)
+{
+	int error = 0;
+	git_index *index;
+	git_buf dir = GIT_BUF_INIT;
+	*occupied = false;
+
+	if ((error = git_repository_index__weakptr(&index, repo)) < 0)
+		goto out;
+
+	if ((error = git_index_find(NULL, index, path)) != GIT_ENOTFOUND) {
+		if (!error) {
+			giterr_set(GITERR_SUBMODULE,
+				"File '%s' already exists in the index", path);
+			*occupied = true;
+		}
+		goto out;
+	}
+
+	if ((error = git_buf_sets(&dir, path)) < 0)
+		goto out;
+
+	if ((error = git_path_to_dir(&dir)) < 0)
+		goto out;
+
+	if ((error = git_index_find_prefix(NULL, index, dir.ptr)) != GIT_ENOTFOUND) {
+		if (!error) {
+			giterr_set(GITERR_SUBMODULE,
+				"Directory '%s' already exists in the index", path);
+			*occupied = true;
+		}
+		goto out;
+	}
+
+	error = 0;
+
+out:
+	git_buf_free(&dir);
+	return error;
+}
+
 /**
  * Release the name map returned by 'load_submodule_names'.
  */
@@ -664,6 +709,7 @@ int git_submodule_add_setup(
 	git_submodule *sm = NULL;
 	git_buf name = GIT_BUF_INIT, real_url = GIT_BUF_INIT;
 	git_repository *subrepo = NULL;
+	bool path_occupied;
 
 	assert(repo && url && path);
 
@@ -688,6 +734,14 @@ int git_submodule_add_setup(
 		goto cleanup;
 	}
 
+	if ((error = is_path_occupied(&path_occupied, repo, path)) < 0)
+		goto cleanup;
+
+	if (path_occupied) {
+		error = GIT_EEXISTS;
+		goto cleanup;
+	}
+
 	/* update .gitmodules */
 
 	if (!(mods = open_gitmodules(repo, GITMODULES_CREATE))) {
diff --git a/tests/submodule/add.c b/tests/submodule/add.c
index c3b3e63..ebb9d62 100644
--- a/tests/submodule/add.c
+++ b/tests/submodule/add.c
@@ -7,6 +7,7 @@
 #include "repository.h"
 
 static git_repository *g_repo = NULL;
+static const char *valid_blob_id = "fa49b077972391ad58037050f2a75f74e3671e92";
 
 void test_submodule_add__cleanup(void)
 {
@@ -128,3 +129,57 @@ void test_submodule_add__url_relative_to_workdir(void)
 
 	assert_submodule_url("TestGitRepository", git_repository_workdir(g_repo));
 }
+
+static void test_add_entry(
+	git_index *index,
+	const char *idstr,
+	const char *path,
+	git_filemode_t mode)
+{
+	git_index_entry entry = {{0}};
+
+	cl_git_pass(git_oid_fromstr(&entry.id, idstr));
+
+	entry.path = path;
+	entry.mode = mode;
+
+	cl_git_pass(git_index_add(index, &entry));
+}
+
+void test_submodule_add__path_exists_in_index(void)
+{
+	git_index *index;
+	git_submodule *sm;
+	git_buf filename = GIT_BUF_INIT;
+
+	g_repo = cl_git_sandbox_init("testrepo");
+
+	cl_git_pass(git_buf_joinpath(&filename, "subdirectory", "test.txt"));
+
+	cl_git_pass(git_repository_index__weakptr(&index, g_repo));
+
+	test_add_entry(index, valid_blob_id, filename.ptr, GIT_FILEMODE_BLOB);
+
+	cl_git_fail_with(git_submodule_add_setup(&sm, g_repo, "./", "subdirectory", 1), GIT_EEXISTS);
+
+	git_submodule_free(sm);
+	git_buf_free(&filename);
+}
+
+void test_submodule_add__file_exists_in_index(void)
+{
+	git_index *index;
+	git_submodule *sm;
+	git_buf name = GIT_BUF_INIT;
+
+	g_repo = cl_git_sandbox_init("testrepo");
+
+	cl_git_pass(git_repository_index__weakptr(&index, g_repo));
+
+	test_add_entry(index, valid_blob_id, "subdirectory", GIT_FILEMODE_BLOB);
+
+	cl_git_fail_with(git_submodule_add_setup(&sm, g_repo, "./", "subdirectory", 1), GIT_EEXISTS);
+
+	git_submodule_free(sm);
+	git_buf_free(&name);
+}