diff options
Diffstat (limited to 'submodule.c')
-rw-r--r-- | submodule.c | 192 |
1 files changed, 158 insertions, 34 deletions
diff --git a/submodule.c b/submodule.c index 1cc04d24e..11ff11ed8 100644 --- a/submodule.c +++ b/submodule.c @@ -199,6 +199,56 @@ void gitmodules_config(void) } } +void gitmodules_config_sha1(const unsigned char *commit_sha1) +{ + struct strbuf rev = STRBUF_INIT; + unsigned char sha1[20]; + + if (gitmodule_sha1_from_commit(commit_sha1, sha1, &rev)) { + git_config_from_blob_sha1(submodule_config, rev.buf, + sha1, NULL); + } + strbuf_release(&rev); +} + +/* + * Determine if a submodule has been initialized at a given 'path' + */ +int is_submodule_initialized(const char *path) +{ + int ret = 0; + const struct submodule *module = NULL; + + module = submodule_from_path(null_sha1, path); + + if (module) { + char *key = xstrfmt("submodule.%s.url", module->name); + char *value = NULL; + + ret = !git_config_get_string(key, &value); + + free(value); + free(key); + } + + return ret; +} + +/* + * Determine if a submodule has been populated at a given 'path' + */ +int is_submodule_populated(const char *path) +{ + int ret = 0; + char *gitdir = xstrfmt("%s/.git", path); + + if (resolve_gitdir(gitdir)) + ret = 1; + + free(gitdir); + return ret; +} + int parse_submodule_update_strategy(const char *value, struct submodule_update_strategy *dst) { @@ -501,27 +551,67 @@ static int has_remote(const char *refname, const struct object_id *oid, return 1; } -static int submodule_needs_pushing(const char *path, const unsigned char sha1[20]) +static int append_sha1_to_argv(const unsigned char sha1[20], void *data) +{ + struct argv_array *argv = data; + argv_array_push(argv, sha1_to_hex(sha1)); + return 0; +} + +static int check_has_commit(const unsigned char sha1[20], void *data) +{ + int *has_commit = data; + + if (!lookup_commit_reference(sha1)) + *has_commit = 0; + + return 0; +} + +static int submodule_has_commits(const char *path, struct sha1_array *commits) { - if (add_submodule_odb(path) || !lookup_commit_reference(sha1)) + int has_commit = 1; + + if (add_submodule_odb(path)) + return 0; + + sha1_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) +{ + if (!submodule_has_commits(path, commits)) + /* + * NOTE: We do consider it safe to return "no" here. The + * correct answer would be "We do not know" instead of + * "No push needed", but it is quite hard to change + * the submodule pointer without having the submodule + * around. If a user did however change the submodules + * without having the submodule around, this indicates + * an expert who knows what they are doing or a + * maintainer integrating work from other people. In + * both cases it should be safe to skip this check. + */ return 0; if (for_each_remote_ref_submodule(path, has_remote, NULL) > 0) { struct child_process cp = CHILD_PROCESS_INIT; - const char *argv[] = {"rev-list", NULL, "--not", "--remotes", "-n", "1" , NULL}; struct strbuf buf = STRBUF_INIT; int needs_pushing = 0; - argv[1] = sha1_to_hex(sha1); - cp.argv = argv; + argv_array_push(&cp.args, "rev-list"); + sha1_array_for_each_unique(commits, append_sha1_to_argv, &cp.args); + argv_array_pushl(&cp.args, "--not", "--remotes", "-n", "1" , NULL); + 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 rev-list %s --not --remotes -n 1' command in submodule %s", - sha1_to_hex(sha1), path); + die("Could not run 'git rev-list <commits> --not --remotes -n 1' command in submodule %s", + path); if (strbuf_read(&buf, cp.out, 41)) needs_pushing = 1; finish_command(&cp); @@ -533,19 +623,34 @@ static int submodule_needs_pushing(const char *path, const unsigned char sha1[20 return 0; } +static struct sha1_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; + + /* NEEDSWORK: should we have sha1_array_init()? */ + item->util = xcalloc(1, sizeof(struct sha1_array)); + return (struct sha1_array *) item->util; +} + static void collect_submodules_from_diff(struct diff_queue_struct *q, struct diff_options *options, void *data) { int i; - struct string_list *needs_pushing = data; + struct string_list *submodules = data; for (i = 0; i < q->nr; i++) { struct diff_filepair *p = q->queue[i]; + struct sha1_array *commits; if (!S_ISGITLINK(p->two->mode)) continue; - if (submodule_needs_pushing(p->two->path, p->two->oid.hash)) - string_list_insert(needs_pushing, p->two->path); + commits = submodule_commits(submodules, p->two->path); + sha1_array_append(commits, p->two->oid.hash); } } @@ -561,46 +666,63 @@ static void find_unpushed_submodule_commits(struct commit *commit, diff_tree_combined_merge(commit, 1, &rev); } -int find_unpushed_submodules(unsigned char new_sha1[20], +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); + string_list_clear(submodules, 1); +} + +int find_unpushed_submodules(struct sha1_array *commits, const char *remotes_name, struct string_list *needs_pushing) { struct rev_info rev; struct commit *commit; - const char *argv[] = {NULL, NULL, "--not", "NULL", NULL}; - int argc = ARRAY_SIZE(argv) - 1; - char *sha1_copy; - - struct strbuf remotes_arg = STRBUF_INIT; + struct string_list submodules = STRING_LIST_INIT_DUP; + struct string_list_item *submodule; + struct argv_array argv = ARGV_ARRAY_INIT; - strbuf_addf(&remotes_arg, "--remotes=%s", remotes_name); init_revisions(&rev, NULL); - sha1_copy = xstrdup(sha1_to_hex(new_sha1)); - argv[1] = sha1_copy; - argv[3] = remotes_arg.buf; - setup_revisions(argc, argv, &rev, NULL); + + /* 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); + argv_array_push(&argv, "--not"); + argv_array_pushf(&argv, "--remotes=%s", remotes_name); + + setup_revisions(argv.argc, argv.argv, &rev, NULL); if (prepare_revision_walk(&rev)) die("revision walk setup failed"); while ((commit = get_revision(&rev)) != NULL) - find_unpushed_submodule_commits(commit, needs_pushing); + find_unpushed_submodule_commits(commit, &submodules); reset_revision_walk(); - free(sha1_copy); - strbuf_release(&remotes_arg); + argv_array_clear(&argv); + + for_each_string_list_item(submodule, &submodules) { + struct sha1_array *commits = (struct sha1_array *) submodule->util; + + if (submodule_needs_pushing(submodule->string, commits)) + string_list_insert(needs_pushing, submodule->string); + } + free_submodules_sha1s(&submodules); return needs_pushing->nr; } -static int push_submodule(const char *path) +static int push_submodule(const char *path, int dry_run) { if (add_submodule_odb(path)) return 1; if (for_each_remote_ref_submodule(path, has_remote, NULL) > 0) { struct child_process cp = CHILD_PROCESS_INIT; - const char *argv[] = {"push", NULL}; + argv_array_push(&cp.args, "push"); + if (dry_run) + argv_array_push(&cp.args, "--dry-run"); - cp.argv = argv; prepare_submodule_repo_env(&cp.env_array); cp.git_cmd = 1; cp.no_stdin = 1; @@ -613,18 +735,20 @@ static int push_submodule(const char *path) return 1; } -int push_unpushed_submodules(unsigned char new_sha1[20], const char *remotes_name) +int push_unpushed_submodules(struct sha1_array *commits, + const char *remotes_name, + int dry_run) { int i, ret = 1; struct string_list needs_pushing = STRING_LIST_INIT_DUP; - if (!find_unpushed_submodules(new_sha1, remotes_name, &needs_pushing)) + if (!find_unpushed_submodules(commits, remotes_name, &needs_pushing)) return 1; 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)) { + if (!push_submodule(path, dry_run)) { fprintf(stderr, "Unable to push submodule '%s'\n", path); ret = 0; } @@ -1278,7 +1402,7 @@ static void relocate_single_git_dir_into_superproject(const char *prefix, /* If it is an actual gitfile, it doesn't need migration. */ return; - real_old_git_dir = xstrdup(real_path(old_git_dir)); + real_old_git_dir = real_pathdup(old_git_dir); sub = submodule_from_path(null_sha1, path); if (!sub) @@ -1287,7 +1411,7 @@ static void relocate_single_git_dir_into_superproject(const char *prefix, new_git_dir = git_path("modules/%s", sub->name); if (safe_create_leading_directories_const(new_git_dir) < 0) die(_("could not create directory '%s'"), new_git_dir); - real_new_git_dir = xstrdup(real_path(new_git_dir)); + real_new_git_dir = real_pathdup(new_git_dir); if (!prefix) prefix = get_super_prefix(); @@ -1324,8 +1448,8 @@ void absorb_git_dir_into_superproject(const char *prefix, goto out; /* Is it already absorbed into the superprojects git dir? */ - real_sub_git_dir = xstrdup(real_path(sub_git_dir)); - real_common_git_dir = xstrdup(real_path(get_git_common_dir())); + real_sub_git_dir = real_pathdup(sub_git_dir); + real_common_git_dir = real_pathdup(get_git_common_dir()); if (!skip_prefix(real_sub_git_dir, real_common_git_dir, &v)) relocate_single_git_dir_into_superproject(prefix, path); |