aboutsummaryrefslogtreecommitdiffstats
path: root/submodule.c
diff options
context:
space:
mode:
Diffstat (limited to 'submodule.c')
-rw-r--r--submodule.c207
1 files changed, 150 insertions, 57 deletions
diff --git a/submodule.c b/submodule.c
index f0f8788d2e..0530e8cf24 100644
--- a/submodule.c
+++ b/submodule.c
@@ -1,6 +1,8 @@
+#define USE_THE_REPOSITORY_VARIABLE
+#define DISABLE_SIGN_COMPARE_WARNINGS
+
#include "git-compat-util.h"
#include "abspath.h"
-#include "alloc.h"
#include "repository.h"
#include "config.h"
#include "submodule-config.h"
@@ -18,10 +20,8 @@
#include "string-list.h"
#include "oid-array.h"
#include "strvec.h"
-#include "blob.h"
#include "thread-utils.h"
#include "path.h"
-#include "quote.h"
#include "remote.h"
#include "worktree.h"
#include "parse-options.h"
@@ -31,7 +31,6 @@
#include "commit-reach.h"
#include "read-cache-ll.h"
#include "setup.h"
-#include "shallow.h"
#include "trace2.h"
static int config_update_recurse_submodules = RECURSE_SUBMODULES_OFF;
@@ -103,7 +102,8 @@ int is_staging_gitmodules_ok(struct index_state *istate)
static int for_each_remote_ref_submodule(const char *submodule,
each_ref_fn fn, void *cb_data)
{
- return refs_for_each_remote_ref(get_submodule_ref_store(submodule),
+ return refs_for_each_remote_ref(repo_get_submodule_ref_store(the_repository,
+ submodule),
fn, cb_data);
}
@@ -160,7 +160,7 @@ int remove_path_from_gitmodules(const char *path)
}
strbuf_addstr(&sect, "submodule.");
strbuf_addstr(&sect, submodule->name);
- if (git_config_rename_section_in_file(GITMODULES_FILE, sect.buf, NULL) < 0) {
+ if (repo_config_rename_section_in_file(the_repository, GITMODULES_FILE, sect.buf, NULL) < 0) {
/* Maybe the user already did that, don't error out here */
warning(_("Could not remove .gitmodules entry for %s"), path);
strbuf_release(&sect);
@@ -176,11 +176,11 @@ 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;
+static struct string_list added_submodule_odb_paths = STRING_LIST_INIT_DUP;
void add_submodule_odb_by_path(const char *path)
{
- string_list_insert(&added_submodule_odb_paths, xstrdup(path));
+ string_list_insert(&added_submodule_odb_paths, path);
}
int register_all_submodule_odb_as_alternates(void)
@@ -425,6 +425,11 @@ int parse_submodule_update_strategy(const char *value,
return 0;
}
+void submodule_update_strategy_release(struct submodule_update_strategy *strategy)
+{
+ free((char *) strategy->command);
+}
+
const char *submodule_update_type_to_string(enum submodule_update_type type)
{
switch (type) {
@@ -531,7 +536,8 @@ static struct repository *open_submodule(const char *path)
struct strbuf sb = STRBUF_INIT;
struct repository *out = xmalloc(sizeof(*out));
- if (submodule_to_gitdir(&sb, path) || repo_init(out, sb.buf, NULL)) {
+ if (submodule_to_gitdir(the_repository, &sb, path) ||
+ repo_init(out, sb.buf, NULL)) {
strbuf_release(&sb);
free(out);
return NULL;
@@ -596,7 +602,12 @@ static void show_submodule_header(struct diff_options *o,
(!is_null_oid(two) && !*right))
message = "(commits not present)";
- *merge_bases = repo_get_merge_bases(sub, *left, *right);
+ *merge_bases = NULL;
+ if (repo_get_merge_bases(sub, *left, *right, merge_bases) < 0) {
+ message = "(corrupt repository)";
+ goto output_header;
+ }
+
if (*merge_bases) {
if ((*merge_bases)->item == *left)
fast_forward = 1;
@@ -949,6 +960,7 @@ static void free_submodules_data(struct string_list *submodules)
}
static int has_remote(const char *refname UNUSED,
+ const char *referent UNUSED,
const struct object_id *oid UNUSED,
int flags UNUSED, void *cb_data UNUSED)
{
@@ -1014,6 +1026,9 @@ static int submodule_has_commits(struct repository *r,
.super_oid = super_oid
};
+ if (validate_submodule_path(path) < 0)
+ exit(128);
+
oid_array_for_each_unique(commits, check_has_commit, &has_commit);
if (has_commit.result) {
@@ -1136,6 +1151,9 @@ static int push_submodule(const char *path,
const struct string_list *push_options,
int dry_run)
{
+ if (validate_submodule_path(path) < 0)
+ exit(128);
+
if (for_each_remote_ref_submodule(path, has_remote, NULL) > 0) {
struct child_process cp = CHILD_PROCESS_INIT;
strvec_push(&cp.args, "push");
@@ -1158,8 +1176,8 @@ static int push_submodule(const char *path,
if (remote->origin != REMOTE_UNCONFIGURED) {
int i;
strvec_push(&cp.args, remote->name);
- for (i = 0; i < rs->raw_nr; i++)
- strvec_push(&cp.args, rs->raw[i]);
+ for (i = 0; i < rs->nr; i++)
+ strvec_push(&cp.args, rs->items[i].raw);
}
prepare_submodule_repo_env(&cp.env);
@@ -1185,13 +1203,16 @@ static void submodule_push_check(const char *path, const char *head,
struct child_process cp = CHILD_PROCESS_INIT;
int i;
+ if (validate_submodule_path(path) < 0)
+ exit(128);
+
strvec_push(&cp.args, "submodule--helper");
strvec_push(&cp.args, "push-check");
strvec_push(&cp.args, head);
strvec_push(&cp.args, remote->name);
- for (i = 0; i < rs->raw_nr; i++)
- strvec_push(&cp.args, rs->raw[i]);
+ for (i = 0; i < rs->nr; i++)
+ strvec_push(&cp.args, rs->items[i].raw);
prepare_submodule_repo_env(&cp.env);
cp.git_cmd = 1;
@@ -1232,7 +1253,8 @@ int push_unpushed_submodules(struct repository *r,
char *head;
struct object_id head_oid;
- head = resolve_refdup("HEAD", 0, &head_oid, NULL);
+ head = refs_resolve_refdup(get_main_ref_store(the_repository),
+ "HEAD", 0, &head_oid, NULL);
if (!head)
die(_("Failed to resolve HEAD as a valid ref."));
@@ -1259,6 +1281,7 @@ int push_unpushed_submodules(struct repository *r,
}
static int append_oid_to_array(const char *ref UNUSED,
+ const char *referent UNUSED,
const struct object_id *oid,
int flags UNUSED, void *data)
{
@@ -1270,7 +1293,8 @@ static int append_oid_to_array(const char *ref UNUSED,
void check_for_new_submodule_commits(struct object_id *oid)
{
if (!initialized_fetch_ref_tips) {
- for_each_ref(append_oid_to_array, &ref_tips_before_fetch);
+ refs_for_each_ref(get_main_ref_store(the_repository),
+ append_oid_to_array, &ref_tips_before_fetch);
initialized_fetch_ref_tips = 1;
}
@@ -1292,7 +1316,7 @@ static int repo_has_absorbed_submodules(struct repository *r)
int ret;
struct strbuf buf = STRBUF_INIT;
- strbuf_repo_git_path(&buf, r, "modules/");
+ repo_git_path_append(r, &buf, "modules/");
ret = file_exists(buf.buf) && !is_empty_dir(buf.buf);
strbuf_release(&buf);
return ret;
@@ -1467,21 +1491,20 @@ struct fetch_task {
*/
static const struct submodule *get_non_gitmodules_submodule(const char *path)
{
- struct submodule *ret = NULL;
+ struct submodule *ret;
const char *name = default_name_or_path(path);
if (!name)
return NULL;
- ret = xmalloc(sizeof(*ret));
- memset(ret, 0, sizeof(*ret));
+ CALLOC_ARRAY(ret, 1);
ret->path = name;
ret->name = name;
return (const struct submodule *) ret;
}
-static void fetch_task_release(struct fetch_task *p)
+static void fetch_task_free(struct fetch_task *p)
{
if (p->free_sub)
free((void*)p->sub);
@@ -1493,6 +1516,7 @@ static void fetch_task_release(struct fetch_task *p)
FREE_AND_NULL(p->repo);
strvec_clear(&p->git_args);
+ free(p);
}
static struct repository *get_submodule_repo_for(struct repository *r,
@@ -1513,8 +1537,12 @@ static struct fetch_task *fetch_task_create(struct submodule_parallel_fetch *spf
const char *path,
const struct object_id *treeish_name)
{
- struct fetch_task *task = xmalloc(sizeof(*task));
- memset(task, 0, sizeof(*task));
+ struct fetch_task *task;
+
+ CALLOC_ARRAY(task, 1);
+
+ if (validate_submodule_path(path) < 0)
+ exit(128);
task->sub = submodule_from_path(spf->r, treeish_name, path);
@@ -1558,8 +1586,7 @@ static struct fetch_task *fetch_task_create(struct submodule_parallel_fetch *spf
return task;
cleanup:
- fetch_task_release(task);
- free(task);
+ fetch_task_free(task);
return NULL;
}
@@ -1589,8 +1616,7 @@ get_fetch_task_from_index(struct submodule_parallel_fetch *spf,
} else {
struct strbuf empty_submodule_path = STRBUF_INIT;
- fetch_task_release(task);
- free(task);
+ fetch_task_free(task);
/*
* An empty directory is normal,
@@ -1636,8 +1662,7 @@ get_fetch_task_from_changed(struct submodule_parallel_fetch *spf,
cs_data->path,
repo_find_unique_abbrev(the_repository, cs_data->super_oid, DEFAULT_ABBREV));
- fetch_task_release(task);
- free(task);
+ fetch_task_free(task);
continue;
}
@@ -1691,8 +1716,6 @@ static int get_next_submodule(struct child_process *cp, struct strbuf *err,
task = get_fetch_task_from_changed(spf, err);
if (task) {
- struct strbuf submodule_prefix = STRBUF_INIT;
-
child_process_init(cp);
cp->dir = task->repo->gitdir;
prepare_submodule_repo_env_in_gitdir(&cp->env);
@@ -1702,15 +1725,11 @@ static int get_next_submodule(struct child_process *cp, struct strbuf *err,
strvec_pushv(&cp->args, task->git_args.v);
strvec_pushv(&cp->args, spf->args.v);
strvec_push(&cp->args, task->default_argv);
- strvec_push(&cp->args, "--submodule-prefix");
+ strvec_pushf(&cp->args, "--submodule-prefix=%s%s/",
+ spf->prefix, task->sub->path);
- strbuf_addf(&submodule_prefix, "%s%s/",
- spf->prefix,
- task->sub->path);
- strvec_push(&cp->args, submodule_prefix.buf);
*task_cb = task;
- strbuf_release(&submodule_prefix);
string_list_insert(&spf->seen_submodule_names, task->sub->name);
return 1;
}
@@ -1718,12 +1737,8 @@ static int get_next_submodule(struct child_process *cp, struct strbuf *err,
if (spf->oid_fetch_tasks_nr) {
struct fetch_task *task =
spf->oid_fetch_tasks[spf->oid_fetch_tasks_nr - 1];
- struct strbuf submodule_prefix = STRBUF_INIT;
spf->oid_fetch_tasks_nr--;
- strbuf_addf(&submodule_prefix, "%s%s/",
- spf->prefix, task->sub->path);
-
child_process_init(cp);
prepare_submodule_repo_env_in_gitdir(&cp->env);
cp->git_cmd = 1;
@@ -1732,8 +1747,8 @@ static int get_next_submodule(struct child_process *cp, struct strbuf *err,
strvec_init(&cp->args);
strvec_pushv(&cp->args, spf->args.v);
strvec_push(&cp->args, "on-demand");
- strvec_push(&cp->args, "--submodule-prefix");
- strvec_push(&cp->args, submodule_prefix.buf);
+ strvec_pushf(&cp->args, "--submodule-prefix=%s%s/",
+ spf->prefix, task->sub->path);
/* NEEDSWORK: have get_default_remote from submodule--helper */
strvec_push(&cp->args, "origin");
@@ -1741,7 +1756,6 @@ static int get_next_submodule(struct child_process *cp, struct strbuf *err,
append_oid_to_argv, &cp->args);
*task_cb = task;
- strbuf_release(&submodule_prefix);
return 1;
}
@@ -1756,7 +1770,7 @@ static int fetch_start_failure(struct strbuf *err UNUSED,
spf->result = 1;
- fetch_task_release(task);
+ fetch_task_free(task);
return 0;
}
@@ -1821,8 +1835,7 @@ static int fetch_finish(int retvalue, struct strbuf *err UNUSED,
}
out:
- fetch_task_release(task);
-
+ fetch_task_free(task);
return 0;
}
@@ -1876,6 +1889,9 @@ int fetch_submodules(struct repository *r,
strvec_clear(&spf.args);
out:
free_submodules_data(&spf.changed_submodule_names);
+ string_list_clear(&spf.seen_submodule_names, 0);
+ strbuf_release(&spf.submodules_with_errors);
+ free(spf.oid_fetch_tasks);
return spf.result;
}
@@ -1888,6 +1904,9 @@ unsigned is_submodule_modified(const char *path, int ignore_untracked)
const char *git_dir;
int ignore_cp_exit_code = 0;
+ if (validate_submodule_path(path) < 0)
+ exit(128);
+
strbuf_addf(&buf, "%s/.git", path);
git_dir = read_gitfile(buf.buf);
if (!git_dir)
@@ -1964,6 +1983,9 @@ int submodule_uses_gitfile(const char *path)
struct strbuf buf = STRBUF_INIT;
const char *git_dir;
+ if (validate_submodule_path(path) < 0)
+ exit(128);
+
strbuf_addf(&buf, "%s/.git", path);
git_dir = read_gitfile(buf.buf);
if (!git_dir) {
@@ -2003,6 +2025,9 @@ int bad_to_remove_submodule(const char *path, unsigned flags)
struct strbuf buf = STRBUF_INIT;
int ret = 0;
+ if (validate_submodule_path(path) < 0)
+ exit(128);
+
if (!file_exists(path) || is_empty_dir(path))
return 0;
@@ -2053,10 +2078,13 @@ void submodule_unset_core_worktree(const struct submodule *sub)
{
struct strbuf config_path = STRBUF_INIT;
+ if (validate_submodule_path(sub->path) < 0)
+ exit(128);
+
submodule_name_to_gitdir(&config_path, the_repository, sub->name);
strbuf_addstr(&config_path, "/config");
- if (git_config_set_in_file_gently(config_path.buf, "core.worktree", NULL))
+ if (git_config_set_in_file_gently(config_path.buf, "core.worktree", NULL, NULL))
warning(_("Could not unset core.worktree setting in submodule '%s'"),
sub->path);
@@ -2067,6 +2095,9 @@ static int submodule_has_dirty_index(const struct submodule *sub)
{
struct child_process cp = CHILD_PROCESS_INIT;
+ if (validate_submodule_path(sub->path) < 0)
+ exit(128);
+
prepare_submodule_repo_env(&cp.env);
cp.git_cmd = 1;
@@ -2084,6 +2115,10 @@ static int submodule_has_dirty_index(const struct submodule *sub)
static void submodule_reset_index(const char *path, const char *super_prefix)
{
struct child_process cp = CHILD_PROCESS_INIT;
+
+ if (validate_submodule_path(path) < 0)
+ exit(128);
+
prepare_submodule_repo_env(&cp.env);
cp.git_cmd = 1;
@@ -2095,7 +2130,7 @@ static void submodule_reset_index(const char *path, const char *super_prefix)
strvec_pushf(&cp.args, "--super-prefix=%s%s/",
(super_prefix ? super_prefix : ""), path);
- strvec_push(&cp.args, empty_tree_oid_hex());
+ strvec_push(&cp.args, empty_tree_oid_hex(the_repository->hash_algo));
if (run_command(&cp))
die(_("could not reset submodule index"));
@@ -2147,10 +2182,27 @@ int submodule_move_head(const char *path, const char *super_prefix,
if (!submodule_uses_gitfile(path))
absorb_git_dir_into_superproject(path,
super_prefix);
+ else {
+ char *dotgit = xstrfmt("%s/.git", path);
+ char *git_dir = xstrdup(read_gitfile(dotgit));
+
+ free(dotgit);
+ if (validate_submodule_git_dir(git_dir,
+ sub->name) < 0)
+ die(_("refusing to create/use '%s' in "
+ "another submodule's git dir"),
+ git_dir);
+ free(git_dir);
+ }
} else {
struct strbuf gitdir = STRBUF_INIT;
submodule_name_to_gitdir(&gitdir, the_repository,
sub->name);
+ if (validate_submodule_git_dir(gitdir.buf,
+ sub->name) < 0)
+ die(_("refusing to create/use '%s' in another "
+ "submodule's git dir"),
+ gitdir.buf);
connect_work_tree_and_git_dir(path, gitdir.buf, 0);
strbuf_release(&gitdir);
@@ -2188,9 +2240,9 @@ int submodule_move_head(const char *path, const char *super_prefix,
strvec_push(&cp.args, "-m");
if (!(flags & SUBMODULE_MOVE_HEAD_FORCE))
- strvec_push(&cp.args, old_head ? old_head : empty_tree_oid_hex());
+ strvec_push(&cp.args, old_head ? old_head : empty_tree_oid_hex(the_repository->hash_algo));
- strvec_push(&cp.args, new_head ? new_head : empty_tree_oid_hex());
+ strvec_push(&cp.args, new_head ? new_head : empty_tree_oid_hex(the_repository->hash_algo));
if (run_command(&cp)) {
ret = error(_("Submodule '%s' could not be updated."), path);
@@ -2271,6 +2323,34 @@ int validate_submodule_git_dir(char *git_dir, const char *submodule_name)
return 0;
}
+int validate_submodule_path(const char *path)
+{
+ char *p = xstrdup(path);
+ struct stat st;
+ int i, ret = 0;
+ char sep;
+
+ for (i = 0; !ret && p[i]; i++) {
+ if (!is_dir_sep(p[i]))
+ continue;
+
+ sep = p[i];
+ p[i] = '\0';
+ /* allow missing components, but no symlinks */
+ ret = lstat(p, &st) || !S_ISLNK(st.st_mode) ? 0 : -1;
+ p[i] = sep;
+ if (ret)
+ error(_("expected '%.*s' in submodule path '%s' not to "
+ "be a symbolic link"), i, p, p);
+ }
+ if (!lstat(p, &st) && S_ISLNK(st.st_mode))
+ ret = error(_("expected submodule path '%s' not to be a "
+ "symbolic link"), p);
+ free(p);
+ return ret;
+}
+
+
/*
* Embeds a single submodules git directory into the superprojects git dir,
* non recursively.
@@ -2282,6 +2362,9 @@ static void relocate_single_git_dir_into_superproject(const char *path,
struct strbuf new_gitdir = STRBUF_INIT;
const struct submodule *sub;
+ if (validate_submodule_path(path) < 0)
+ exit(128);
+
if (submodule_uses_worktrees(path))
die(_("relocate_gitdir for submodule '%s' with "
"more than one worktree not supported"), path);
@@ -2323,6 +2406,9 @@ static void absorb_git_dir_into_superproject_recurse(const char *path,
struct child_process cp = CHILD_PROCESS_INIT;
+ if (validate_submodule_path(path) < 0)
+ exit(128);
+
cp.dir = path;
cp.git_cmd = 1;
cp.no_stdin = 1;
@@ -2347,6 +2433,10 @@ void absorb_git_dir_into_superproject(const char *path,
int err_code;
const char *sub_git_dir;
struct strbuf gitdir = STRBUF_INIT;
+
+ if (validate_submodule_path(path) < 0)
+ exit(128);
+
strbuf_addf(&gitdir, "%s/.git", path);
sub_git_dir = resolve_gitdir_gently(gitdir.buf, &err_code);
@@ -2381,7 +2471,7 @@ void absorb_git_dir_into_superproject(const char *path,
} else {
/* Is it already absorbed into the superprojects git dir? */
char *real_sub_git_dir = real_pathdup(sub_git_dir, 1);
- char *real_common_git_dir = real_pathdup(get_git_common_dir(), 1);
+ char *real_common_git_dir = real_pathdup(repo_get_common_dir(the_repository), 1);
if (!starts_with(real_sub_git_dir, real_common_git_dir))
relocate_single_git_dir_into_superproject(path, super_prefix);
@@ -2483,12 +2573,16 @@ int get_superproject_working_tree(struct strbuf *buf)
* Put the gitdir for a submodule (given relative to the main
* repository worktree) into `buf`, or return -1 on error.
*/
-int submodule_to_gitdir(struct strbuf *buf, const char *submodule)
+int submodule_to_gitdir(struct repository *repo,
+ struct strbuf *buf, const char *submodule)
{
const struct submodule *sub;
const char *git_dir;
int ret = 0;
+ if (validate_submodule_path(submodule) < 0)
+ exit(128);
+
strbuf_reset(buf);
strbuf_addstr(buf, submodule);
strbuf_complete(buf, '/');
@@ -2500,14 +2594,13 @@ int submodule_to_gitdir(struct strbuf *buf, const char *submodule)
strbuf_addstr(buf, git_dir);
}
if (!is_git_directory(buf->buf)) {
- sub = submodule_from_path(the_repository, null_oid(),
- submodule);
+ sub = submodule_from_path(repo, null_oid(), submodule);
if (!sub) {
ret = -1;
goto cleanup;
}
strbuf_reset(buf);
- submodule_name_to_gitdir(buf, the_repository, sub->name);
+ submodule_name_to_gitdir(buf, repo, sub->name);
}
cleanup:
@@ -2537,6 +2630,6 @@ void submodule_name_to_gitdir(struct strbuf *buf, struct repository *r,
* 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/");
+ repo_git_path_append(r, buf, "modules/");
strbuf_addstr(buf, submodule_name);
}