diff options
author | Junio C Hamano <gitster@pobox.com> | 2017-03-28 14:05:58 -0700 |
---|---|---|
committer | Junio C Hamano <gitster@pobox.com> | 2017-03-28 14:05:58 -0700 |
commit | e394fa01d65a5e182639e226f5b46eca999cd8d7 (patch) | |
tree | b202d6e4807998e130fbfd4ac315ac868e16c104 /submodule.c | |
parent | Merge branch 'bw/grep-recurse-submodules' (diff) | |
parent | builtin/read-tree: add --recurse-submodules switch (diff) | |
download | tgif-e394fa01d65a5e182639e226f5b46eca999cd8d7.tar.xz |
Merge branch 'sb/checkout-recurse-submodules'
"git checkout" is taught the "--recurse-submodules" option.
* sb/checkout-recurse-submodules:
builtin/read-tree: add --recurse-submodules switch
builtin/checkout: add --recurse-submodules switch
entry.c: create submodules when interesting
unpack-trees: check if we can perform the operation for submodules
unpack-trees: pass old oid to verify_clean_submodule
update submodules: add submodule_move_head
submodule.c: get_super_prefix_or_empty
update submodules: move up prepare_submodule_repo_env
submodules: introduce check to see whether to touch a submodule
update submodules: add a config option to determine if submodules are updated
update submodules: add submodule config parsing
make is_submodule_populated gently
lib-submodule-update.sh: define tests for recursing into submodules
lib-submodule-update.sh: replace sha1 by hash
lib-submodule-update: teach test_submodule_content the -C <dir> flag
lib-submodule-update.sh: do not use ./. as submodule remote
lib-submodule-update.sh: reorder create_lib_submodule_repo
submodule--helper.c: remove duplicate code
connect_work_tree_and_git_dir: safely create leading directories
Diffstat (limited to 'submodule.c')
-rw-r--r-- | submodule.c | 222 |
1 files changed, 190 insertions, 32 deletions
diff --git a/submodule.c b/submodule.c index 3200b7bb2b..040f4c2287 100644 --- a/submodule.c +++ b/submodule.c @@ -17,6 +17,7 @@ #include "worktree.h" static int config_fetch_recurse_submodules = RECURSE_SUBMODULES_ON_DEMAND; +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; @@ -234,15 +235,12 @@ int is_submodule_initialized(const char *path) return ret; } -/* - * Determine if a submodule has been populated at a given 'path' - */ -int is_submodule_populated(const char *path) +int is_submodule_populated_gently(const char *path, int *return_error_code) { int ret = 0; char *gitdir = xstrfmt("%s/.git", path); - if (resolve_gitdir(gitdir)) + if (resolve_gitdir_gently(gitdir, return_error_code)) ret = 1; free(gitdir); @@ -358,6 +356,23 @@ static void print_submodule_summary(struct rev_info *rev, FILE *f, strbuf_release(&sb); } +static void prepare_submodule_repo_env_no_git_dir(struct argv_array *out) +{ + const char * const *var; + + for (var = local_repo_env; *var; var++) { + if (strcmp(*var, CONFIG_DATA_ENVIRONMENT)) + argv_array_push(out, *var); + } +} + +void prepare_submodule_repo_env(struct argv_array *out) +{ + prepare_submodule_repo_env_no_git_dir(out); + argv_array_pushf(out, "%s=%s", GIT_DIR_ENVIRONMENT, + DEFAULT_GIT_DIR_ENVIRONMENT); +} + /* Helper function to display the submodule header line prior to the full * summary output. If it can locate the submodule objects directory it will * attempt to lookup both the left and right commits and put them into the @@ -545,6 +560,27 @@ void set_config_fetch_recurse_submodules(int value) config_fetch_recurse_submodules = value; } +void set_config_update_recurse_submodules(int value) +{ + config_update_recurse_submodules = value; +} + +int should_update_submodules(void) +{ + return config_update_recurse_submodules == RECURSE_SUBMODULES_ON; +} + +const struct submodule *submodule_from_ce(const struct cache_entry *ce) +{ + if (!S_ISGITLINK(ce->ce_mode)) + return NULL; + + if (!should_update_submodules()) + return NULL; + + return submodule_from_path(null_sha1, ce->name); +} + static int has_remote(const char *refname, const struct object_id *oid, int flags, void *cb_data) { @@ -1203,6 +1239,151 @@ out: return ret; } +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; + + prepare_submodule_repo_env_no_git_dir(&cp.env_array); + + cp.git_cmd = 1; + argv_array_pushl(&cp.args, "diff-index", "--quiet", + "--cached", "HEAD", NULL); + cp.no_stdin = 1; + cp.no_stdout = 1; + cp.dir = sub->path; + if (start_command(&cp)) + die("could not recurse into submodule '%s'", sub->path); + + return finish_command(&cp); +} + +static void submodule_reset_index(const char *path) +{ + struct child_process cp = CHILD_PROCESS_INIT; + prepare_submodule_repo_env_no_git_dir(&cp.env_array); + + cp.git_cmd = 1; + cp.no_stdin = 1; + cp.dir = path; + + argv_array_pushf(&cp.args, "--super-prefix=%s%s/", + get_super_prefix_or_empty(), path); + argv_array_pushl(&cp.args, "read-tree", "-u", "--reset", NULL); + + argv_array_push(&cp.args, EMPTY_TREE_SHA1_HEX); + + if (run_command(&cp)) + die("could not reset submodule index"); +} + +/** + * Moves a submodule at a given path from a given head to another new head. + * 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, + const char *new, + unsigned flags) +{ + int ret = 0; + struct child_process cp = CHILD_PROCESS_INIT; + const struct submodule *sub; + + sub = submodule_from_path(null_sha1, path); + + if (!sub) + die("BUG: could not get submodule information for '%s'", path); + + if (old && !(flags & SUBMODULE_MOVE_HEAD_FORCE)) { + /* Check if the submodule has a dirty index. */ + if (submodule_has_dirty_index(sub)) + return error(_("submodule '%s' has dirty index"), path); + } + + if (!(flags & SUBMODULE_MOVE_HEAD_DRY_RUN)) { + if (old) { + if (!submodule_uses_gitfile(path)) + absorb_git_dir_into_superproject("", path, + ABSORB_GITDIR_RECURSE_SUBMODULES); + } else { + struct strbuf sb = STRBUF_INIT; + strbuf_addf(&sb, "%s/modules/%s", + get_git_common_dir(), sub->name); + connect_work_tree_and_git_dir(path, sb.buf); + strbuf_release(&sb); + + /* make sure the index is clean as well */ + submodule_reset_index(path); + } + } + + prepare_submodule_repo_env_no_git_dir(&cp.env_array); + + cp.git_cmd = 1; + cp.no_stdin = 1; + cp.dir = path; + + argv_array_pushf(&cp.args, "--super-prefix=%s%s/", + get_super_prefix_or_empty(), path); + argv_array_pushl(&cp.args, "read-tree", NULL); + + if (flags & SUBMODULE_MOVE_HEAD_DRY_RUN) + argv_array_push(&cp.args, "-n"); + else + argv_array_push(&cp.args, "-u"); + + if (flags & SUBMODULE_MOVE_HEAD_FORCE) + argv_array_push(&cp.args, "--reset"); + else + argv_array_push(&cp.args, "-m"); + + argv_array_push(&cp.args, old ? old : EMPTY_TREE_SHA1_HEX); + argv_array_push(&cp.args, new ? new : EMPTY_TREE_SHA1_HEX); + + if (run_command(&cp)) { + ret = -1; + goto out; + } + + if (!(flags & SUBMODULE_MOVE_HEAD_DRY_RUN)) { + if (new) { + struct child_process cp1 = CHILD_PROCESS_INIT; + /* also set the HEAD accordingly */ + cp1.git_cmd = 1; + cp1.no_stdin = 1; + cp1.dir = path; + + argv_array_pushl(&cp1.args, "update-ref", "HEAD", + new ? new : EMPTY_TREE_SHA1_HEX, NULL); + + if (run_command(&cp1)) { + ret = -1; + goto out; + } + } else { + struct strbuf sb = STRBUF_INIT; + + strbuf_addf(&sb, "%s/.git", path); + unlink_or_warn(sb.buf); + strbuf_release(&sb); + + if (is_empty_dir(path)) + rmdir_or_warn(path); + } + } +out: + return ret; +} + static int find_first_merges(struct object_array *result, const char *path, struct commit *a, struct commit *b) { @@ -1371,18 +1552,6 @@ int parallel_submodules(void) return parallel_jobs; } -void prepare_submodule_repo_env(struct argv_array *out) -{ - const char * const *var; - - for (var = local_repo_env; *var; var++) { - if (strcmp(*var, CONFIG_DATA_ENVIRONMENT)) - argv_array_push(out, *var); - } - argv_array_pushf(out, "%s=%s", GIT_DIR_ENVIRONMENT, - DEFAULT_GIT_DIR_ENVIRONMENT); -} - /* * Embeds a single submodules git directory into the superprojects git dir, * non recursively. @@ -1414,11 +1583,8 @@ static void relocate_single_git_dir_into_superproject(const char *prefix, die(_("could not create directory '%s'"), new_git_dir); real_new_git_dir = real_pathdup(new_git_dir, 1); - if (!prefix) - prefix = get_super_prefix(); - fprintf(stderr, _("Migrating git directory of '%s%s' from\n'%s' to\n'%s'\n"), - prefix ? prefix : "", path, + get_super_prefix_or_empty(), path, real_old_git_dir, real_new_git_dir); relocate_gitdir(path, real_old_git_dir, real_new_git_dir); @@ -1445,8 +1611,6 @@ void absorb_git_dir_into_superproject(const char *prefix, /* Not populated? */ if (!sub_git_dir) { - char *real_new_git_dir; - const char *new_git_dir; const struct submodule *sub; if (err_code == READ_GITFILE_ERR_STAT_FAILED) { @@ -1469,13 +1633,8 @@ void absorb_git_dir_into_superproject(const char *prefix, sub = submodule_from_path(null_sha1, path); if (!sub) die(_("could not lookup name for submodule '%s'"), path); - 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 = real_pathdup(new_git_dir, 1); - connect_work_tree_and_git_dir(path, real_new_git_dir); - - free(real_new_git_dir); + connect_work_tree_and_git_dir(path, + git_path("modules/%s", sub->name)); } else { /* Is it already absorbed into the superprojects git dir? */ char *real_sub_git_dir = real_pathdup(sub_git_dir, 1); @@ -1496,8 +1655,7 @@ void absorb_git_dir_into_superproject(const char *prefix, if (flags & ~ABSORB_GITDIR_RECURSE_SUBMODULES) die("BUG: we don't know how to pass the flags down?"); - if (get_super_prefix()) - strbuf_addstr(&sb, get_super_prefix()); + strbuf_addstr(&sb, get_super_prefix_or_empty()); strbuf_addstr(&sb, path); strbuf_addch(&sb, '/'); |