diff options
Diffstat (limited to 'submodule.c')
| -rw-r--r-- | submodule.c | 263 |
1 files changed, 177 insertions, 86 deletions
diff --git a/submodule.c b/submodule.c index bf7a2c7918..f6313cd99f 100644 --- a/submodule.c +++ b/submodule.c @@ -1,5 +1,5 @@ - -#include "cache.h" +#include "git-compat-util.h" +#include "abspath.h" #include "repository.h" #include "config.h" #include "submodule-config.h" @@ -7,6 +7,9 @@ #include "dir.h" #include "diff.h" #include "commit.h" +#include "environment.h" +#include "gettext.h" +#include "hex.h" #include "revision.h" #include "run-command.h" #include "diffcore.h" @@ -14,15 +17,18 @@ #include "string-list.h" #include "oid-array.h" #include "strvec.h" -#include "blob.h" #include "thread-utils.h" -#include "quote.h" +#include "path.h" #include "remote.h" #include "worktree.h" #include "parse-options.h" -#include "object-store.h" +#include "object-file.h" +#include "object-name.h" +#include "object-store-ll.h" #include "commit-reach.h" -#include "shallow.h" +#include "read-cache-ll.h" +#include "setup.h" +#include "trace2.h" static int config_update_recurse_submodules = RECURSE_SUBMODULES_OFF; static int initialized_fetch_ref_tips; @@ -65,7 +71,7 @@ int is_writing_gitmodules_ok(void) { struct object_id oid; return file_exists(GITMODULES_FILE) || - (get_oid(GITMODULES_INDEX, &oid) < 0 && get_oid(GITMODULES_HEAD, &oid) < 0); + (repo_get_oid(the_repository, GITMODULES_INDEX, &oid) < 0 && repo_get_oid(the_repository, GITMODULES_HEAD, &oid) < 0); } /* @@ -274,8 +280,7 @@ int is_tree_submodule_active(struct repository *repo, free(key); /* submodule.active is set */ - sl = repo_config_get_value_multi(repo, "submodule.active"); - if (sl) { + if (!repo_config_get_string_multi(repo, "submodule.active", &sl)) { struct pathspec ps; struct strvec args = STRVEC_INIT; const struct string_list_item *item; @@ -587,7 +592,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; @@ -832,7 +842,7 @@ static void changed_submodule_data_clear(struct changed_submodule_data *cs_data) } static void collect_changed_submodules_cb(struct diff_queue_struct *q, - struct diff_options *options, + struct diff_options *options UNUSED, void *data) { struct collect_changed_submodules_cb_data *me = data; @@ -1005,6 +1015,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) { @@ -1127,9 +1140,18 @@ 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"); + /* + * When recursing into a submodule, treat any "only" configurations as "on- + * demand", since "only" would not work (we need all submodules to be pushed + * in order to be able to push the superproject). + */ + strvec_push(&cp.args, "--recurse-submodules=only-is-on-demand"); if (dry_run) strvec_push(&cp.args, "--dry-run"); @@ -1170,6 +1192,9 @@ 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); @@ -1217,7 +1242,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.")); @@ -1255,7 +1281,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; } @@ -1501,6 +1528,9 @@ static struct fetch_task *fetch_task_create(struct submodule_parallel_fetch *spf struct fetch_task *task = xmalloc(sizeof(*task)); memset(task, 0, sizeof(*task)); + if (validate_submodule_path(path) < 0) + exit(128); + task->sub = submodule_from_path(spf->r, treeish_name, path); if (!task->sub) { @@ -1619,7 +1649,7 @@ get_fetch_task_from_changed(struct submodule_parallel_fetch *spf, if (!task->repo) { strbuf_addf(err, _("Could not access submodule '%s' at commit %s\n"), cs_data->path, - find_unique_abbrev(cs_data->super_oid, DEFAULT_ABBREV)); + repo_find_unique_abbrev(the_repository, cs_data->super_oid, DEFAULT_ABBREV)); fetch_task_release(task); free(task); @@ -1630,8 +1660,8 @@ get_fetch_task_from_changed(struct submodule_parallel_fetch *spf, strbuf_addf(err, _("Fetching submodule %s%s at commit %s\n"), spf->prefix, task->sub->path, - find_unique_abbrev(cs_data->super_oid, - DEFAULT_ABBREV)); + repo_find_unique_abbrev(the_repository, cs_data->super_oid, + DEFAULT_ABBREV)); spf->changed_count++; /* @@ -1676,8 +1706,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); @@ -1687,15 +1715,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; } @@ -1703,12 +1727,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; @@ -1717,8 +1737,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"); @@ -1726,14 +1746,13 @@ 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; } return 0; } -static int fetch_start_failure(struct strbuf *err, +static int fetch_start_failure(struct strbuf *err UNUSED, void *cb, void *task_cb) { struct submodule_parallel_fetch *spf = cb; @@ -1754,7 +1773,7 @@ static int commit_missing_in_sub(const struct object_id *oid, void *data) return type != OBJ_COMMIT; } -static int fetch_finish(int retvalue, struct strbuf *err, +static int fetch_finish(int retvalue, struct strbuf *err UNUSED, void *cb, void *task_cb) { struct submodule_parallel_fetch *spf = cb; @@ -1819,6 +1838,17 @@ int fetch_submodules(struct repository *r, { int i; struct submodule_parallel_fetch spf = SPF_INIT; + const struct run_process_parallel_opts opts = { + .tr2_category = "submodule", + .tr2_label = "parallel/fetch", + + .processes = max_parallel_jobs, + + .get_next_task = get_next_submodule, + .start_failure = fetch_start_failure, + .task_finished = fetch_finish, + .data = &spf, + }; spf.r = r; spf.command_line_option = command_line_option; @@ -1840,12 +1870,7 @@ int fetch_submodules(struct repository *r, calculate_changed_submodule_paths(r, &spf.changed_submodule_names); string_list_sort(&spf.changed_submodule_names); - run_processes_parallel_tr2(max_parallel_jobs, - get_next_submodule, - fetch_start_failure, - fetch_finish, - &spf, - "submodule", "parallel/fetch"); + run_processes_parallel(&opts); if (spf.submodules_with_errors.len > 0) fprintf(stderr, _("Errors during submodule fetch:\n%s"), @@ -1867,6 +1892,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) @@ -1943,6 +1971,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) { @@ -1982,6 +2013,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; @@ -2032,28 +2066,26 @@ 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); strbuf_release(&config_path); } -static const char *get_super_prefix_or_empty(void) -{ - const char *s = get_super_prefix(); - if (!s) - s = ""; - return s; -} - 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; @@ -2068,19 +2100,23 @@ static int submodule_has_dirty_index(const struct submodule *sub) return finish_command(&cp); } -static void submodule_reset_index(const char *path) +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; cp.no_stdin = 1; cp.dir = path; - strvec_pushf(&cp.args, "--super-prefix=%s%s/", - get_super_prefix_or_empty(), path); /* TODO: determine if this might overwright untracked files */ strvec_pushl(&cp.args, "read-tree", "-u", "--reset", NULL); + strvec_pushf(&cp.args, "--super-prefix=%s%s/", + (super_prefix ? super_prefix : ""), path); strvec_push(&cp.args, empty_tree_oid_hex()); @@ -2093,10 +2129,9 @@ static void submodule_reset_index(const char *path) * For edge cases (a submodule coming into existence or removing a submodule) * pass NULL for old or new respectively. */ -int submodule_move_head(const char *path, - const char *old_head, - const char *new_head, - unsigned flags) +int submodule_move_head(const char *path, const char *super_prefix, + const char *old_head, const char *new_head, + unsigned flags) { int ret = 0; struct child_process cp = CHILD_PROCESS_INIT; @@ -2134,16 +2169,33 @@ int submodule_move_head(const char *path, if (old_head) { if (!submodule_uses_gitfile(path)) absorb_git_dir_into_superproject(path, - ABSORB_GITDIR_RECURSE_SUBMODULES); + 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); /* make sure the index is clean as well */ - submodule_reset_index(path); + submodule_reset_index(path, super_prefix); } if (old_head && (flags & SUBMODULE_MOVE_HEAD_FORCE)) { @@ -2161,9 +2213,9 @@ int submodule_move_head(const char *path, cp.no_stdin = 1; cp.dir = path; - strvec_pushf(&cp.args, "--super-prefix=%s%s/", - get_super_prefix_or_empty(), path); strvec_pushl(&cp.args, "read-tree", "--recurse-submodules", NULL); + strvec_pushf(&cp.args, "--super-prefix=%s%s/", + (super_prefix ? super_prefix : ""), path); if (flags & SUBMODULE_MOVE_HEAD_DRY_RUN) strvec_push(&cp.args, "-n"); @@ -2259,16 +2311,48 @@ 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. */ -static void relocate_single_git_dir_into_superproject(const char *path) +static void relocate_single_git_dir_into_superproject(const char *path, + const char *super_prefix) { char *old_git_dir = NULL, *real_old_git_dir = NULL, *real_new_git_dir = NULL; 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); @@ -2293,7 +2377,7 @@ static void relocate_single_git_dir_into_superproject(const char *path) 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, + super_prefix ? super_prefix : "", path, real_old_git_dir, real_new_git_dir); relocate_gitdir(path, real_old_git_dir, real_new_git_dir); @@ -2304,17 +2388,43 @@ static void relocate_single_git_dir_into_superproject(const char *path) strbuf_release(&new_gitdir); } +static void absorb_git_dir_into_superproject_recurse(const char *path, + const char *super_prefix) +{ + + 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; + strvec_pushl(&cp.args, "submodule--helper", + "absorbgitdirs", NULL); + strvec_pushf(&cp.args, "--super-prefix=%s%s/", super_prefix ? + super_prefix : "", path); + + prepare_submodule_repo_env(&cp.env); + if (run_command(&cp)) + die(_("could not recurse into submodule '%s'"), path); +} + /* * Migrate the git directory of the submodule given by path from * having its git directory within the working tree to the git dir nested * in its superprojects git dir under modules/. */ void absorb_git_dir_into_superproject(const char *path, - unsigned flags) + const char *super_prefix) { 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); @@ -2352,36 +2462,14 @@ void absorb_git_dir_into_superproject(const char *path, char *real_common_git_dir = real_pathdup(get_git_common_dir(), 1); if (!starts_with(real_sub_git_dir, real_common_git_dir)) - relocate_single_git_dir_into_superproject(path); + relocate_single_git_dir_into_superproject(path, super_prefix); free(real_sub_git_dir); free(real_common_git_dir); } strbuf_release(&gitdir); - if (flags & ABSORB_GITDIR_RECURSE_SUBMODULES) { - struct child_process cp = CHILD_PROCESS_INIT; - struct strbuf sb = STRBUF_INIT; - - if (flags & ~ABSORB_GITDIR_RECURSE_SUBMODULES) - BUG("we don't know how to pass the flags down?"); - - strbuf_addstr(&sb, get_super_prefix_or_empty()); - strbuf_addstr(&sb, path); - strbuf_addch(&sb, '/'); - - cp.dir = path; - cp.git_cmd = 1; - cp.no_stdin = 1; - strvec_pushl(&cp.args, "--super-prefix", sb.buf, - "submodule--helper", - "absorbgitdirs", NULL); - prepare_submodule_repo_env(&cp.env); - if (run_command(&cp)) - die(_("could not recurse into submodule '%s'"), path); - - strbuf_release(&sb); - } + absorb_git_dir_into_superproject_recurse(path, super_prefix); } int get_superproject_working_tree(struct strbuf *buf) @@ -2479,6 +2567,9 @@ int submodule_to_gitdir(struct strbuf *buf, const char *submodule) 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, '/'); |
