From 365444a6a55391e192662964d523e2b0887557bd Mon Sep 17 00:00:00 2001 From: Stefan Beller Date: Tue, 14 Mar 2017 14:46:24 -0700 Subject: connect_work_tree_and_git_dir: safely create leading directories In a later patch we'll use connect_work_tree_and_git_dir when the directory for the gitlink file doesn't exist yet. This patch makes connect_work_tree_and_git_dir safe to use for both cases of either the git dir or the working dir missing. To do so, we need to call safe_create_leading_directories[_const] on both directories. However this has to happen before we construct the absolute paths as real_pathdup assumes the directories to be there already. So for both the config file in the git dir as well as the .git link file we need to a) construct the name b) call SCLD c) get the absolute path d) once a-c is done for both we can consume the absolute path to compute the relative path to each other and store those relative paths. The implementation provided here puts a) and b) for both cases first, and then performs c and d after. One of the two users of 'connect_work_tree_and_git_dir' already checked for the directory being there, so we can loose that check as connect_work_tree_and_git_dir handles this functionality now. Signed-off-by: Stefan Beller Signed-off-by: Junio C Hamano --- submodule.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) (limited to 'submodule.c') diff --git a/submodule.c b/submodule.c index 3b98766a6b..45e93a1d55 100644 --- a/submodule.c +++ b/submodule.c @@ -1445,8 +1445,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 +1467,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); - 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); -- cgit v1.3 From 15cdc6477634e0227e4211de464c6443a68172c9 Mon Sep 17 00:00:00 2001 From: Stefan Beller Date: Tue, 14 Mar 2017 14:46:31 -0700 Subject: make is_submodule_populated gently We need the gentle version in a later patch. As we have just one caller, migrate the caller. Signed-off-by: Stefan Beller Signed-off-by: Junio C Hamano --- builtin/grep.c | 2 +- submodule.c | 7 ++----- submodule.h | 8 +++++++- 3 files changed, 10 insertions(+), 7 deletions(-) (limited to 'submodule.c') diff --git a/builtin/grep.c b/builtin/grep.c index 2c727ef499..b17835aed6 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -616,7 +616,7 @@ static int grep_submodule(struct grep_opt *opt, const unsigned char *sha1, { if (!is_submodule_initialized(path)) return 0; - if (!is_submodule_populated(path)) { + if (!is_submodule_populated_gently(path, NULL)) { /* * If searching history, check for the presense of the * submodule's gitdir before skipping the submodule. diff --git a/submodule.c b/submodule.c index 45e93a1d55..04d185738f 100644 --- a/submodule.c +++ b/submodule.c @@ -234,15 +234,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); diff --git a/submodule.h b/submodule.h index 05ab674f06..0b915bd3ac 100644 --- a/submodule.h +++ b/submodule.h @@ -41,7 +41,13 @@ extern int submodule_config(const char *var, const char *value, void *cb); extern void gitmodules_config(void); extern void gitmodules_config_sha1(const unsigned char *commit_sha1); extern int is_submodule_initialized(const char *path); -extern int is_submodule_populated(const char *path); +/* + * Determine if a submodule has been populated at a given 'path' by checking if + * the /.git resolves to a valid git repository. + * If return_error_code is NULL, die on error. + * Otherwise the return error code is the same as of resolve_gitdir_gently. + */ +extern int is_submodule_populated_gently(const char *path, int *return_error_code); extern int parse_submodule_update_strategy(const char *value, struct submodule_update_strategy *dst); extern const char *submodule_strategy_to_string(const struct submodule_update_strategy *s); -- cgit v1.3 From cf055e465f6febedb8189407a901042814d6b5d8 Mon Sep 17 00:00:00 2001 From: Stefan Beller Date: Tue, 14 Mar 2017 14:46:33 -0700 Subject: update submodules: add a config option to determine if submodules are updated In later patches we introduce the options and flag for commands that modify the working directory, e.g. git-checkout. Have a central place to store such settings whether we want to update a submodule. Signed-off-by: Stefan Beller Signed-off-by: Junio C Hamano --- submodule.c | 6 ++++++ submodule.h | 1 + 2 files changed, 7 insertions(+) (limited to 'submodule.c') diff --git a/submodule.c b/submodule.c index 04d185738f..591f4a694e 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; @@ -542,6 +543,11 @@ 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; +} + static int has_remote(const char *refname, const struct object_id *oid, int flags, void *cb_data) { diff --git a/submodule.h b/submodule.h index 0b915bd3ac..b4e60c08d2 100644 --- a/submodule.h +++ b/submodule.h @@ -64,6 +64,7 @@ extern void show_submodule_inline_diff(FILE *f, const char *path, const char *del, const char *add, const char *reset, const struct diff_options *opt); extern void set_config_fetch_recurse_submodules(int value); +extern void set_config_update_recurse_submodules(int value); extern void check_for_new_submodule_commits(unsigned char new_sha1[20]); extern int fetch_populated_submodules(const struct argv_array *options, const char *prefix, int command_line_option, -- cgit v1.3 From 84f8925eeb97827e9d334b8b9ff7282177ab9105 Mon Sep 17 00:00:00 2001 From: Stefan Beller Date: Tue, 14 Mar 2017 14:46:34 -0700 Subject: submodules: introduce check to see whether to touch a submodule In later patches we introduce the --recurse-submodule flag for commands that modify the working directory, e.g. git-checkout. It is potentially expensive to check if a submodule needs an update, because a common theme to interact with submodules is to spawn a child process for each interaction. So let's introduce a function that checks if a submodule needs to be checked for an update before attempting the update. Signed-off-by: Stefan Beller Signed-off-by: Junio C Hamano --- submodule.c | 16 ++++++++++++++++ submodule.h | 7 +++++++ 2 files changed, 23 insertions(+) (limited to 'submodule.c') diff --git a/submodule.c b/submodule.c index 591f4a694e..8b2c0212be 100644 --- a/submodule.c +++ b/submodule.c @@ -548,6 +548,22 @@ 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) { diff --git a/submodule.h b/submodule.h index b4e60c08d2..6f3fe85c7c 100644 --- a/submodule.h +++ b/submodule.h @@ -65,6 +65,13 @@ extern void show_submodule_inline_diff(FILE *f, const char *path, const struct diff_options *opt); extern void set_config_fetch_recurse_submodules(int value); extern void set_config_update_recurse_submodules(int value); +/* Check if we want to update any submodule.*/ +extern int should_update_submodules(void); +/* + * Returns the submodule struct if the given ce entry is a submodule + * and it should be updated. Returns NULL otherwise. + */ +extern const struct submodule *submodule_from_ce(const struct cache_entry *ce); extern void check_for_new_submodule_commits(unsigned char new_sha1[20]); extern int fetch_populated_submodules(const struct argv_array *options, const char *prefix, int command_line_option, -- cgit v1.3 From 6cd5757c898d845747d9886c71cb5884b56e9702 Mon Sep 17 00:00:00 2001 From: Stefan Beller Date: Tue, 14 Mar 2017 14:46:35 -0700 Subject: update submodules: move up prepare_submodule_repo_env In a later patch we need to prepare the submodule environment with another git directory, so split up the function. Also move it up in the file such that we do not need to declare the function later before using it. Signed-off-by: Stefan Beller Signed-off-by: Junio C Hamano --- submodule.c | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) (limited to 'submodule.c') diff --git a/submodule.c b/submodule.c index 8b2c0212be..0b2596e88a 100644 --- a/submodule.c +++ b/submodule.c @@ -356,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 @@ -1390,18 +1407,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. -- cgit v1.3 From 202275b96b6c3ffc2c380cfb017e4f6a0dd6bddb Mon Sep 17 00:00:00 2001 From: Stefan Beller Date: Tue, 14 Mar 2017 14:46:36 -0700 Subject: submodule.c: get_super_prefix_or_empty In a later patch we need to use the super_prefix, and in case it is NULL we can just assume it is empty. Create a helper function for this. We already have some use cases for this helper function, convert them, too. Signed-off-by: Stefan Beller Signed-off-by: Junio C Hamano --- submodule.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'submodule.c') diff --git a/submodule.c b/submodule.c index 0b2596e88a..d8efbdb96e 100644 --- a/submodule.c +++ b/submodule.c @@ -1239,6 +1239,14 @@ 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 find_first_merges(struct object_array *result, const char *path, struct commit *a, struct commit *b) { @@ -1438,11 +1446,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); - 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); @@ -1513,8 +1518,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, '/'); -- cgit v1.3 From 6e3c1595c66e2b192a2a516447c5645814766339 Mon Sep 17 00:00:00 2001 From: Stefan Beller Date: Tue, 14 Mar 2017 14:46:37 -0700 Subject: update submodules: add submodule_move_head In later patches we introduce the options and flag for commands that modify the working directory, e.g. git-checkout. This piece of code will be used universally for all these working tree modifications as it * supports dry run to answer the question: "Is it safe to change the submodule to this new state?" e.g. is it overwriting untracked files or are there local changes that would be overwritten? * supports a force flag that can be used for resetting the tree. Signed-off-by: Stefan Beller Signed-off-by: Junio C Hamano --- submodule.c | 137 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ submodule.h | 7 ++++ 2 files changed, 144 insertions(+) (limited to 'submodule.c') diff --git a/submodule.c b/submodule.c index d8efbdb96e..6e8825b451 100644 --- a/submodule.c +++ b/submodule.c @@ -1247,6 +1247,143 @@ static const char *get_super_prefix_or_empty(void) 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) { diff --git a/submodule.h b/submodule.h index 6f3fe85c7c..4cdf6445f7 100644 --- a/submodule.h +++ b/submodule.h @@ -96,6 +96,13 @@ extern int push_unpushed_submodules(struct sha1_array *commits, extern void connect_work_tree_and_git_dir(const char *work_tree, const char *git_dir); extern int parallel_submodules(void); +#define SUBMODULE_MOVE_HEAD_DRY_RUN (1<<0) +#define SUBMODULE_MOVE_HEAD_FORCE (1<<1) +extern int submodule_move_head(const char *path, + const char *old, + const char *new, + unsigned flags); + /* * Prepare the "env_array" parameter of a "struct child_process" for executing * a submodule by clearing any repo-specific envirionment variables, but -- cgit v1.3