]> git.ipfire.org Git - thirdparty/git.git/blobdiff - submodule.c
Merge branch 'en/removing-untracked-fixes'
[thirdparty/git.git] / submodule.c
index a9b71d585cf636269e71919eda365345c34561c9..f3c99634a974a5e3f2f4b68836782ad74a2fbd1d 100644 (file)
@@ -165,6 +165,8 @@ void stage_updated_gitmodules(struct index_state *istate)
                die(_("staging updated .gitmodules failed"));
 }
 
+static struct string_list added_submodule_odb_paths = STRING_LIST_INIT_NODUP;
+
 /* TODO: remove this function, use repo_submodule_init instead. */
 int add_submodule_odb(const char *path)
 {
@@ -178,12 +180,33 @@ int add_submodule_odb(const char *path)
                ret = -1;
                goto done;
        }
-       add_to_alternates_memory(objects_directory.buf);
+       string_list_insert(&added_submodule_odb_paths,
+                          strbuf_detach(&objects_directory, NULL));
 done:
        strbuf_release(&objects_directory);
        return ret;
 }
 
+void add_submodule_odb_by_path(const char *path)
+{
+       string_list_insert(&added_submodule_odb_paths, xstrdup(path));
+}
+
+int register_all_submodule_odb_as_alternates(void)
+{
+       int i;
+       int ret = added_submodule_odb_paths.nr;
+
+       for (i = 0; i < added_submodule_odb_paths.nr; i++)
+               add_to_alternates_memory(added_submodule_odb_paths.items[i].string);
+       if (ret) {
+               string_list_clear(&added_submodule_odb_paths, 0);
+               if (git_env_bool("GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB", 0))
+                       BUG("register_all_submodule_odb_as_alternates() called");
+       }
+       return ret;
+}
+
 void set_diffopt_flags_from_submodule_config(struct diff_options *diffopt,
                                             const char *path)
 {
@@ -237,6 +260,11 @@ int option_parse_recurse_submodules_worktree_updater(const struct option *opt,
 /*
  * Determine if a submodule has been initialized at a given 'path'
  */
+/*
+ * NEEDSWORK: Emit a warning if submodule.active exists, but is valueless,
+ * ie, the config looks like: "[submodule] active\n".
+ * Since that is an invalid pathspec, we should inform the user.
+ */
 int is_submodule_active(struct repository *repo, const char *path)
 {
        int ret = 0;
@@ -497,9 +525,6 @@ static void prepare_submodule_repo_env_in_gitdir(struct strvec *out)
 /*
  * Initialize a repository struct for a submodule based on the provided 'path'.
  *
- * Unlike repo_submodule_init, this tolerates submodules not present
- * in .gitmodules. This function exists only to preserve historical behavior,
- *
  * Returns the repository struct on success,
  * NULL when the submodule is not present.
  */
@@ -697,8 +722,20 @@ void show_submodule_inline_diff(struct diff_options *o, const char *path,
                strvec_push(&cp.args, oid_to_hex(new_oid));
 
        prepare_submodule_repo_env(&cp.env_array);
-       if (start_command(&cp))
+
+       if (!is_directory(path)) {
+               /* fall back to absorbed git dir, if any */
+               if (!sub)
+                       goto done;
+               cp.dir = sub->gitdir;
+               strvec_push(&cp.env_array, GIT_DIR_ENVIRONMENT "=.");
+               strvec_push(&cp.env_array, GIT_WORK_TREE_ENVIRONMENT "=.");
+       }
+
+       if (start_command(&cp)) {
                diff_emit_submodule_error(o, "(diff failed)\n");
+               goto done;
+       }
 
        while (strbuf_getwholeline_fd(&sb, cp.out, '\n') != EOF)
                diff_emit_submodule_pipethrough(o, sb.buf, sb.len);
@@ -1281,9 +1318,11 @@ struct submodule_parallel_fetch {
 
        struct strbuf submodules_with_errors;
 };
-#define SPF_INIT {0, STRVEC_INIT, NULL, NULL, 0, 0, 0, 0, \
-                 STRING_LIST_INIT_DUP, \
-                 NULL, 0, 0, STRBUF_INIT}
+#define SPF_INIT { \
+       .args = STRVEC_INIT, \
+       .changed_submodule_names = STRING_LIST_INIT_DUP, \
+       .submodules_with_errors = STRBUF_INIT, \
+}
 
 static int get_fetch_recurse_config(const struct submodule *submodule,
                                    struct submodule_parallel_fetch *spf)
@@ -1381,24 +1420,13 @@ static void fetch_task_release(struct fetch_task *p)
 }
 
 static struct repository *get_submodule_repo_for(struct repository *r,
-                                                const struct submodule *sub)
+                                                const char *path)
 {
        struct repository *ret = xmalloc(sizeof(*ret));
 
-       if (repo_submodule_init(ret, r, sub)) {
-               /*
-                * No entry in .gitmodules? Technically not a submodule,
-                * but historically we supported repositories that happen to be
-                * in-place where a gitlink is. Keep supporting them.
-                */
-               struct strbuf gitdir = STRBUF_INIT;
-               strbuf_repo_worktree_path(&gitdir, r, "%s/.git", sub->path);
-               if (repo_init(ret, gitdir.buf, NULL)) {
-                       strbuf_release(&gitdir);
-                       free(ret);
-                       return NULL;
-               }
-               strbuf_release(&gitdir);
+       if (repo_submodule_init(ret, r, path, null_oid())) {
+               free(ret);
+               return NULL;
        }
 
        return ret;
@@ -1440,7 +1468,7 @@ static int get_next_submodule(struct child_process *cp,
                        continue;
                }
 
-               task->repo = get_submodule_repo_for(spf->r, task->sub);
+               task->repo = get_submodule_repo_for(spf->r, task->sub->path);
                if (task->repo) {
                        struct strbuf submodule_prefix = STRBUF_INIT;
                        child_process_init(cp);
@@ -1819,14 +1847,16 @@ out:
 
 void submodule_unset_core_worktree(const struct submodule *sub)
 {
-       char *config_path = xstrfmt("%s/modules/%s/config",
-                                   get_git_dir(), sub->name);
+       struct strbuf config_path = STRBUF_INIT;
+
+       submodule_name_to_gitdir(&config_path, the_repository, sub->name);
+       strbuf_addstr(&config_path, "/config");
 
-       if (git_config_set_in_file_gently(config_path, "core.worktree", NULL))
+       if (git_config_set_in_file_gently(config_path.buf, "core.worktree", NULL))
                warning(_("Could not unset core.worktree setting in submodule '%s'"),
                          sub->path);
 
-       free(config_path);
+       strbuf_release(&config_path);
 }
 
 static const char *get_super_prefix_or_empty(void)
@@ -1923,20 +1953,22 @@ int submodule_move_head(const char *path,
                                absorb_git_dir_into_superproject(path,
                                        ABSORB_GITDIR_RECURSE_SUBMODULES);
                } else {
-                       char *gitdir = xstrfmt("%s/modules/%s",
-                                   get_git_dir(), sub->name);
-                       connect_work_tree_and_git_dir(path, gitdir, 0);
-                       free(gitdir);
+                       struct strbuf gitdir = STRBUF_INIT;
+                       submodule_name_to_gitdir(&gitdir, the_repository,
+                                                sub->name);
+                       connect_work_tree_and_git_dir(path, gitdir.buf, 0);
+                       strbuf_release(&gitdir);
 
                        /* make sure the index is clean as well */
                        submodule_reset_index(path);
                }
 
                if (old_head && (flags & SUBMODULE_MOVE_HEAD_FORCE)) {
-                       char *gitdir = xstrfmt("%s/modules/%s",
-                                   get_git_dir(), sub->name);
-                       connect_work_tree_and_git_dir(path, gitdir, 1);
-                       free(gitdir);
+                       struct strbuf gitdir = STRBUF_INIT;
+                       submodule_name_to_gitdir(&gitdir, the_repository,
+                                                sub->name);
+                       connect_work_tree_and_git_dir(path, gitdir.buf, 1);
+                       strbuf_release(&gitdir);
                }
        }
 
@@ -2051,7 +2083,7 @@ int validate_submodule_git_dir(char *git_dir, const char *submodule_name)
 static void relocate_single_git_dir_into_superproject(const char *path)
 {
        char *old_git_dir = NULL, *real_old_git_dir = NULL, *real_new_git_dir = NULL;
-       char *new_git_dir;
+       struct strbuf new_gitdir = STRBUF_INIT;
        const struct submodule *sub;
 
        if (submodule_uses_worktrees(path))
@@ -2069,14 +2101,13 @@ static void relocate_single_git_dir_into_superproject(const char *path)
        if (!sub)
                die(_("could not lookup name for submodule '%s'"), path);
 
-       new_git_dir = git_pathdup("modules/%s", sub->name);
-       if (validate_submodule_git_dir(new_git_dir, sub->name) < 0)
+       submodule_name_to_gitdir(&new_gitdir, the_repository, sub->name);
+       if (validate_submodule_git_dir(new_gitdir.buf, sub->name) < 0)
                die(_("refusing to move '%s' into an existing git dir"),
                    real_old_git_dir);
-       if (safe_create_leading_directories_const(new_git_dir) < 0)
-               die(_("could not create directory '%s'"), new_git_dir);
-       real_new_git_dir = real_pathdup(new_git_dir, 1);
-       free(new_git_dir);
+       if (safe_create_leading_directories_const(new_gitdir.buf) < 0)
+               die(_("could not create directory '%s'"), new_gitdir.buf);
+       real_new_git_dir = real_pathdup(new_gitdir.buf, 1);
 
        fprintf(stderr, _("Migrating git directory of '%s%s' from\n'%s' to\n'%s'\n"),
                get_super_prefix_or_empty(), path,
@@ -2087,6 +2118,7 @@ static void relocate_single_git_dir_into_superproject(const char *path)
        free(old_git_dir);
        free(real_old_git_dir);
        free(real_new_git_dir);
+       strbuf_release(&new_gitdir);
 }
 
 /*
@@ -2106,6 +2138,7 @@ void absorb_git_dir_into_superproject(const char *path,
        /* Not populated? */
        if (!sub_git_dir) {
                const struct submodule *sub;
+               struct strbuf sub_gitdir = STRBUF_INIT;
 
                if (err_code == READ_GITFILE_ERR_STAT_FAILED) {
                        /* unpopulated as expected */
@@ -2127,8 +2160,9 @@ void absorb_git_dir_into_superproject(const char *path,
                sub = submodule_from_path(the_repository, null_oid(), path);
                if (!sub)
                        die(_("could not lookup name for submodule '%s'"), path);
-               connect_work_tree_and_git_dir(path,
-                       git_path("modules/%s", sub->name), 0);
+               submodule_name_to_gitdir(&sub_gitdir, the_repository, sub->name);
+               connect_work_tree_and_git_dir(path, sub_gitdir.buf, 0);
+               strbuf_release(&sub_gitdir);
        } else {
                /* Is it already absorbed into the superprojects git dir? */
                char *real_sub_git_dir = real_pathdup(sub_git_dir, 1);
@@ -2279,9 +2313,36 @@ int submodule_to_gitdir(struct strbuf *buf, const char *submodule)
                        goto cleanup;
                }
                strbuf_reset(buf);
-               strbuf_git_path(buf, "%s/%s", "modules", sub->name);
+               submodule_name_to_gitdir(buf, the_repository, sub->name);
        }
 
 cleanup:
        return ret;
 }
+
+void submodule_name_to_gitdir(struct strbuf *buf, struct repository *r,
+                             const char *submodule_name)
+{
+       /*
+        * NEEDSWORK: The current way of mapping a submodule's name to
+        * its location in .git/modules/ has problems with some naming
+        * schemes. For example, if a submodule is named "foo" and
+        * another is named "foo/bar" (whether present in the same
+        * superproject commit or not - the problem will arise if both
+        * superproject commits have been checked out at any point in
+        * time), or if two submodule names only have different cases in
+        * a case-insensitive filesystem.
+        *
+        * There are several solutions, including encoding the path in
+        * some way, introducing a submodule.<name>.gitdir config in
+        * .git/config (not .gitmodules) that allows overriding what the
+        * gitdir of a submodule would be (and teach Git, upon noticing
+        * a clash, to automatically determine a non-clashing name and
+        * to write such a config), or introducing a
+        * submodule.<name>.gitdir config in .gitmodules that repo
+        * administrators can explicitly set. Nothing has been decided,
+        * so for now, just append the name at the end of the path.
+        */
+       strbuf_repo_git_path(buf, r, "modules/");
+       strbuf_addstr(buf, submodule_name);
+}