Merge pull request #4378 from cjhoward92/fix/submodule-add-check-index submodule: check index for path and prefix before adding submodule
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 137 138 139 140 141 142 143 144 145 146 147 148 149
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);
+}