diff options
Diffstat (limited to 'submodule.c')
-rw-r--r-- | submodule.c | 253 |
1 files changed, 180 insertions, 73 deletions
diff --git a/submodule.c b/submodule.c index 7c3c4b17fb..d3299e29c0 100644 --- a/submodule.c +++ b/submodule.c @@ -14,6 +14,7 @@ #include "blob.h" #include "thread-utils.h" #include "quote.h" +#include "remote.h" #include "worktree.h" static int config_fetch_recurse_submodules = RECURSE_SUBMODULES_ON_DEMAND; @@ -21,8 +22,8 @@ static int config_update_recurse_submodules = RECURSE_SUBMODULES_DEFAULT; static int parallel_jobs = 1; static struct string_list changed_submodule_paths = STRING_LIST_INIT_NODUP; static int initialized_fetch_ref_tips; -static struct sha1_array ref_tips_before_fetch; -static struct sha1_array ref_tips_after_fetch; +static struct oid_array ref_tips_before_fetch; +static struct oid_array ref_tips_after_fetch; /* * The following flag is set if the .gitmodules file is unmerged. We then @@ -622,35 +623,35 @@ static int has_remote(const char *refname, const struct object_id *oid, return 1; } -static int append_sha1_to_argv(const unsigned char sha1[20], void *data) +static int append_oid_to_argv(const struct object_id *oid, void *data) { struct argv_array *argv = data; - argv_array_push(argv, sha1_to_hex(sha1)); + argv_array_push(argv, oid_to_hex(oid)); return 0; } -static int check_has_commit(const unsigned char sha1[20], void *data) +static int check_has_commit(const struct object_id *oid, void *data) { int *has_commit = data; - if (!lookup_commit_reference(sha1)) + if (!lookup_commit_reference(oid->hash)) *has_commit = 0; return 0; } -static int submodule_has_commits(const char *path, struct sha1_array *commits) +static int submodule_has_commits(const char *path, struct oid_array *commits) { int has_commit = 1; if (add_submodule_odb(path)) return 0; - sha1_array_for_each_unique(commits, check_has_commit, &has_commit); + oid_array_for_each_unique(commits, check_has_commit, &has_commit); return has_commit; } -static int submodule_needs_pushing(const char *path, struct sha1_array *commits) +static int submodule_needs_pushing(const char *path, struct oid_array *commits) { if (!submodule_has_commits(path, commits)) /* @@ -672,7 +673,7 @@ static int submodule_needs_pushing(const char *path, struct sha1_array *commits) int needs_pushing = 0; argv_array_push(&cp.args, "rev-list"); - sha1_array_for_each_unique(commits, append_sha1_to_argv, &cp.args); + oid_array_for_each_unique(commits, append_oid_to_argv, &cp.args); argv_array_pushl(&cp.args, "--not", "--remotes", "-n", "1" , NULL); prepare_submodule_repo_env(&cp.env_array); @@ -694,18 +695,18 @@ static int submodule_needs_pushing(const char *path, struct sha1_array *commits) return 0; } -static struct sha1_array *submodule_commits(struct string_list *submodules, +static struct oid_array *submodule_commits(struct string_list *submodules, const char *path) { struct string_list_item *item; item = string_list_insert(submodules, path); if (item->util) - return (struct sha1_array *) item->util; + return (struct oid_array *) item->util; - /* NEEDSWORK: should we have sha1_array_init()? */ - item->util = xcalloc(1, sizeof(struct sha1_array)); - return (struct sha1_array *) item->util; + /* NEEDSWORK: should we have oid_array_init()? */ + item->util = xcalloc(1, sizeof(struct oid_array)); + return (struct oid_array *) item->util; } static void collect_submodules_from_diff(struct diff_queue_struct *q, @@ -717,11 +718,11 @@ static void collect_submodules_from_diff(struct diff_queue_struct *q, for (i = 0; i < q->nr; i++) { struct diff_filepair *p = q->queue[i]; - struct sha1_array *commits; + struct oid_array *commits; if (!S_ISGITLINK(p->two->mode)) continue; commits = submodule_commits(submodules, p->two->path); - sha1_array_append(commits, p->two->oid.hash); + oid_array_append(commits, &p->two->oid); } } @@ -741,11 +742,11 @@ static void free_submodules_sha1s(struct string_list *submodules) { struct string_list_item *item; for_each_string_list_item(item, submodules) - sha1_array_clear((struct sha1_array *) item->util); + oid_array_clear((struct oid_array *) item->util); string_list_clear(submodules, 1); } -int find_unpushed_submodules(struct sha1_array *commits, +int find_unpushed_submodules(struct oid_array *commits, const char *remotes_name, struct string_list *needs_pushing) { struct rev_info rev; @@ -758,7 +759,7 @@ int find_unpushed_submodules(struct sha1_array *commits, /* argv.argv[0] will be ignored by setup_revisions */ argv_array_push(&argv, "find_unpushed_submodules"); - sha1_array_for_each_unique(commits, append_sha1_to_argv, &argv); + oid_array_for_each_unique(commits, append_oid_to_argv, &argv); argv_array_push(&argv, "--not"); argv_array_pushf(&argv, "--remotes=%s", remotes_name); @@ -773,7 +774,7 @@ int find_unpushed_submodules(struct sha1_array *commits, argv_array_clear(&argv); for_each_string_list_item(submodule, &submodules) { - struct sha1_array *commits = (struct sha1_array *) submodule->util; + struct oid_array *commits = (struct oid_array *) submodule->util; if (submodule_needs_pushing(submodule->string, commits)) string_list_insert(needs_pushing, submodule->string); @@ -783,7 +784,11 @@ int find_unpushed_submodules(struct sha1_array *commits, return needs_pushing->nr; } -static int push_submodule(const char *path, int dry_run) +static int push_submodule(const char *path, + const struct remote *remote, + const char **refspec, int refspec_nr, + const struct string_list *push_options, + int dry_run) { if (add_submodule_odb(path)) return 1; @@ -794,6 +799,20 @@ static int push_submodule(const char *path, int dry_run) if (dry_run) argv_array_push(&cp.args, "--dry-run"); + if (push_options && push_options->nr) { + const struct string_list_item *item; + for_each_string_list_item(item, push_options) + argv_array_pushf(&cp.args, "--push-option=%s", + item->string); + } + + if (remote->origin != REMOTE_UNCONFIGURED) { + int i; + argv_array_push(&cp.args, remote->name); + for (i = 0; i < refspec_nr; i++) + argv_array_push(&cp.args, refspec[i]); + } + prepare_submodule_repo_env(&cp.env_array); cp.git_cmd = 1; cp.no_stdin = 1; @@ -806,20 +825,67 @@ static int push_submodule(const char *path, int dry_run) return 1; } -int push_unpushed_submodules(struct sha1_array *commits, - const char *remotes_name, +/* + * Perform a check in the submodule to see if the remote and refspec work. + * Die if the submodule can't be pushed. + */ +static void submodule_push_check(const char *path, const struct remote *remote, + const char **refspec, int refspec_nr) +{ + struct child_process cp = CHILD_PROCESS_INIT; + int i; + + argv_array_push(&cp.args, "submodule--helper"); + argv_array_push(&cp.args, "push-check"); + argv_array_push(&cp.args, remote->name); + + for (i = 0; i < refspec_nr; i++) + argv_array_push(&cp.args, refspec[i]); + + prepare_submodule_repo_env(&cp.env_array); + cp.git_cmd = 1; + cp.no_stdin = 1; + cp.no_stdout = 1; + cp.dir = path; + + /* + * Simply indicate if 'submodule--helper push-check' failed. + * More detailed error information will be provided by the + * child process. + */ + if (run_command(&cp)) + die("process for submodule '%s' failed", path); +} + +int push_unpushed_submodules(struct oid_array *commits, + const struct remote *remote, + const char **refspec, int refspec_nr, + const struct string_list *push_options, int dry_run) { int i, ret = 1; struct string_list needs_pushing = STRING_LIST_INIT_DUP; - if (!find_unpushed_submodules(commits, remotes_name, &needs_pushing)) + if (!find_unpushed_submodules(commits, remote->name, &needs_pushing)) return 1; + /* + * Verify that the remote and refspec can be propagated to all + * submodules. This check can be skipped if the remote and refspec + * won't be propagated due to the remote being unconfigured (e.g. a URL + * instead of a remote name). + */ + if (remote->origin != REMOTE_UNCONFIGURED) + for (i = 0; i < needs_pushing.nr; i++) + submodule_push_check(needs_pushing.items[i].string, + remote, refspec, refspec_nr); + + /* Actually push the submodules */ for (i = 0; i < needs_pushing.nr; i++) { const char *path = needs_pushing.items[i].string; fprintf(stderr, "Pushing submodule '%s'\n", path); - if (!push_submodule(path, dry_run)) { + if (!push_submodule(path, remote, refspec, refspec_nr, + push_options, dry_run)) { fprintf(stderr, "Unable to push submodule '%s'\n", path); ret = 0; } @@ -888,23 +954,23 @@ static void submodule_collect_changed_cb(struct diff_queue_struct *q, static int add_sha1_to_array(const char *ref, const struct object_id *oid, int flags, void *data) { - sha1_array_append(data, oid->hash); + oid_array_append(data, oid); return 0; } -void check_for_new_submodule_commits(unsigned char new_sha1[20]) +void check_for_new_submodule_commits(struct object_id *oid) { if (!initialized_fetch_ref_tips) { for_each_ref(add_sha1_to_array, &ref_tips_before_fetch); initialized_fetch_ref_tips = 1; } - sha1_array_append(&ref_tips_after_fetch, new_sha1); + oid_array_append(&ref_tips_after_fetch, oid); } -static int add_sha1_to_argv(const unsigned char sha1[20], void *data) +static int add_oid_to_argv(const struct object_id *oid, void *data) { - argv_array_push(data, sha1_to_hex(sha1)); + argv_array_push(data, oid_to_hex(oid)); return 0; } @@ -920,11 +986,11 @@ static void calculate_changed_submodule_paths(void) init_revisions(&rev, NULL); argv_array_push(&argv, "--"); /* argv[0] program name */ - sha1_array_for_each_unique(&ref_tips_after_fetch, - add_sha1_to_argv, &argv); + oid_array_for_each_unique(&ref_tips_after_fetch, + add_oid_to_argv, &argv); argv_array_push(&argv, "--not"); - sha1_array_for_each_unique(&ref_tips_before_fetch, - add_sha1_to_argv, &argv); + oid_array_for_each_unique(&ref_tips_before_fetch, + add_oid_to_argv, &argv); setup_revisions(argv.argc, argv.argv, &rev, NULL); if (prepare_revision_walk(&rev)) die("revision walk setup failed"); @@ -950,8 +1016,8 @@ static void calculate_changed_submodule_paths(void) } argv_array_clear(&argv); - sha1_array_clear(&ref_tips_before_fetch); - sha1_array_clear(&ref_tips_after_fetch); + oid_array_clear(&ref_tips_before_fetch); + oid_array_clear(&ref_tips_after_fetch); initialized_fetch_ref_tips = 0; } @@ -1112,67 +1178,78 @@ out: unsigned is_submodule_modified(const char *path, int ignore_untracked) { - ssize_t len; struct child_process cp = CHILD_PROCESS_INIT; - const char *argv[] = { - "status", - "--porcelain", - NULL, - NULL, - }; struct strbuf buf = STRBUF_INIT; + FILE *fp; unsigned dirty_submodule = 0; - const char *line, *next_line; const char *git_dir; + int ignore_cp_exit_code = 0; strbuf_addf(&buf, "%s/.git", path); git_dir = read_gitfile(buf.buf); if (!git_dir) git_dir = buf.buf; - if (!is_directory(git_dir)) { + if (!is_git_directory(git_dir)) { + if (is_directory(git_dir)) + die(_("'%s' not recognized as a git repository"), git_dir); strbuf_release(&buf); /* The submodule is not checked out, so it is not modified */ return 0; - } strbuf_reset(&buf); + argv_array_pushl(&cp.args, "status", "--porcelain=2", NULL); if (ignore_untracked) - argv[2] = "-uno"; + argv_array_push(&cp.args, "-uno"); - cp.argv = argv; prepare_submodule_repo_env(&cp.env_array); cp.git_cmd = 1; cp.no_stdin = 1; cp.out = -1; cp.dir = path; if (start_command(&cp)) - die("Could not run 'git status --porcelain' in submodule %s", path); + die("Could not run 'git status --porcelain=2' in submodule %s", path); - len = strbuf_read(&buf, cp.out, 1024); - line = buf.buf; - while (len > 2) { - if ((line[0] == '?') && (line[1] == '?')) { + fp = xfdopen(cp.out, "r"); + while (strbuf_getwholeline(&buf, fp, '\n') != EOF) { + /* regular untracked files */ + if (buf.buf[0] == '?') dirty_submodule |= DIRTY_SUBMODULE_UNTRACKED; - if (dirty_submodule & DIRTY_SUBMODULE_MODIFIED) - break; - } else { - dirty_submodule |= DIRTY_SUBMODULE_MODIFIED; - if (ignore_untracked || - (dirty_submodule & DIRTY_SUBMODULE_UNTRACKED)) - break; + + if (buf.buf[0] == 'u' || + buf.buf[0] == '1' || + buf.buf[0] == '2') { + /* T = line type, XY = status, SSSS = submodule state */ + if (buf.len < strlen("T XY SSSS")) + die("BUG: invalid status --porcelain=2 line %s", + buf.buf); + + if (buf.buf[5] == 'S' && buf.buf[8] == 'U') + /* nested untracked file */ + dirty_submodule |= DIRTY_SUBMODULE_UNTRACKED; + + if (buf.buf[0] == 'u' || + buf.buf[0] == '2' || + memcmp(buf.buf + 5, "S..U", 4)) + /* other change */ + dirty_submodule |= DIRTY_SUBMODULE_MODIFIED; } - next_line = strchr(line, '\n'); - if (!next_line) + + if ((dirty_submodule & DIRTY_SUBMODULE_MODIFIED) && + ((dirty_submodule & DIRTY_SUBMODULE_UNTRACKED) || + ignore_untracked)) { + /* + * We're not interested in any further information from + * the child any more, neither output nor its exit code. + */ + ignore_cp_exit_code = 1; break; - next_line++; - len -= (next_line - line); - line = next_line; + } } - close(cp.out); + fclose(fp); - if (finish_command(&cp)) - die("'git status --porcelain' failed in submodule %s", path); + if (finish_command(&cp) && !ignore_cp_exit_code) + die("'git status --porcelain=2' failed in submodule %s", path); strbuf_release(&buf); return dirty_submodule; @@ -1252,7 +1329,7 @@ int bad_to_remove_submodule(const char *path, unsigned flags) cp.dir = path; if (start_command(&cp)) { if (flags & SUBMODULE_REMOVAL_DIE_ON_ERROR) - die(_("could not start 'git status in submodule '%s'"), + die(_("could not start 'git status' in submodule '%s'"), path); ret = -1; goto out; @@ -1265,7 +1342,7 @@ int bad_to_remove_submodule(const char *path, unsigned flags) if (finish_command(&cp)) { if (flags & SUBMODULE_REMOVAL_DIE_ON_ERROR) - die(_("could not run 'git status in submodule '%s'"), + die(_("could not run 'git status' in submodule '%s'"), path); ret = -1; } @@ -1397,8 +1474,7 @@ int submodule_move_head(const char *path, cp1.no_stdin = 1; cp1.dir = path; - argv_array_pushl(&cp1.args, "update-ref", "HEAD", - new ? new : EMPTY_TREE_SHA1_HEX, NULL); + argv_array_pushl(&cp1.args, "update-ref", "HEAD", new, NULL); if (run_command(&cp1)) { ret = -1; @@ -1789,3 +1865,34 @@ const char *get_superproject_working_tree(void) return ret; } + +int submodule_to_gitdir(struct strbuf *buf, const char *submodule) +{ + const struct submodule *sub; + const char *git_dir; + int ret = 0; + + strbuf_reset(buf); + strbuf_addstr(buf, submodule); + strbuf_complete(buf, '/'); + strbuf_addstr(buf, ".git"); + + git_dir = read_gitfile(buf->buf); + if (git_dir) { + strbuf_reset(buf); + strbuf_addstr(buf, git_dir); + } + if (!is_git_directory(buf->buf)) { + gitmodules_config(); + sub = submodule_from_path(null_sha1, submodule); + if (!sub) { + ret = -1; + goto cleanup; + } + strbuf_reset(buf); + strbuf_git_path(buf, "%s/%s", "modules", sub->name); + } + +cleanup: + return ret; +} |