summaryrefslogtreecommitdiff
path: root/submodule.c
diff options
context:
space:
mode:
Diffstat (limited to 'submodule.c')
-rw-r--r--submodule.c121
1 files changed, 97 insertions, 24 deletions
diff --git a/submodule.c b/submodule.c
index 8e611fe..78aed03 100644
--- a/submodule.c
+++ b/submodule.c
@@ -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;
@@ -697,8 +725,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);
@@ -1819,14 +1859,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)
@@ -1922,20 +1964,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);
}
}
@@ -2050,7 +2094,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))
@@ -2068,14 +2112,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,
@@ -2086,6 +2129,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);
}
/*
@@ -2105,6 +2149,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 */
@@ -2126,8 +2171,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);
@@ -2278,9 +2324,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);
+}