The walker of a tree is only expected to call `schedule_submodule_for_update` and once done, to run `update_submodules`. This avoids directory/file conflicts and later we can parallelize all submodule actions if needed. Signed-off-by: Stefan Beller <sbeller@xxxxxxxxxx> --- submodule.c | 132 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ submodule.h | 5 +++ 2 files changed, 137 insertions(+) diff --git a/submodule.c b/submodule.c index 7bb64d6c69..02c28ef56b 100644 --- a/submodule.c +++ b/submodule.c @@ -1348,3 +1348,135 @@ int parallel_submodules(void) { return parallel_jobs; } + +static struct scheduled_submodules_update_type { + const char *path; + const struct object_id *oid; + /* + * Do we need to perform a complete checkout or just incremental + * update? + */ + unsigned is_new:1; +} *scheduled_submodules; +#define SCHEDULED_SUBMODULES_INIT {NULL, NULL, 0} + +static int scheduled_submodules_nr, scheduled_submodules_alloc; + +void schedule_submodule_for_update(const struct cache_entry *ce, int is_new) +{ + struct scheduled_submodules_update_type *ssu; + ALLOC_GROW(scheduled_submodules, + scheduled_submodules_nr + 1, + scheduled_submodules_alloc); + ssu = &scheduled_submodules[scheduled_submodules_nr++]; + ssu->path = ce->name; + ssu->oid = &ce->oid; + ssu->is_new = !!is_new; +} + +static int update_submodule(const char *path, const struct object_id *oid, + int force, int is_new) +{ + const char *git_dir; + struct child_process cp = CHILD_PROCESS_INIT; + const struct submodule *sub = submodule_from_path(null_sha1, path); + + if (!sub || !sub->name) + return -1; + + git_dir = resolve_gitdir(git_common_path("modules/%s", sub->name)); + + if (!git_dir) + return -1; + + if (is_new) + connect_work_tree_and_git_dir(path, git_dir); + + /* update index via `read-tree --reset sha1` */ + argv_array_pushl(&cp.args, "read-tree", + force ? "--reset" : "-m", + "-u", sha1_to_hex(oid->hash), NULL); + prepare_submodule_repo_env(&cp.env_array); + cp.git_cmd = 1; + cp.no_stdin = 1; + cp.dir = path; + if (run_command(&cp)) { + warning(_("reading the index in submodule '%s' failed"), path); + child_process_clear(&cp); + return -1; + } + + /* write index to working dir */ + child_process_clear(&cp); + child_process_init(&cp); + argv_array_pushl(&cp.args, "checkout-index", "-a", NULL); + cp.git_cmd = 1; + cp.no_stdin = 1; + cp.dir = path; + if (force) + argv_array_push(&cp.args, "-f"); + + if (run_command(&cp)) { + warning(_("populating the working directory in submodule '%s' failed"), path); + child_process_clear(&cp); + return -1; + } + + /* get the HEAD right */ + child_process_clear(&cp); + child_process_init(&cp); + argv_array_pushl(&cp.args, "checkout", "--recurse-submodules", NULL); + cp.git_cmd = 1; + cp.no_stdin = 1; + cp.dir = path; + if (force) + argv_array_push(&cp.args, "-f"); + argv_array_push(&cp.args, sha1_to_hex(oid->hash)); + + if (run_command(&cp)) { + warning(_("setting the HEAD in submodule '%s' failed"), path); + child_process_clear(&cp); + return -1; + } + + child_process_clear(&cp); + return 0; +} + +int update_submodules(int force) +{ + int i; + gitmodules_config(); + + /* + * NEEDSWORK: As submodule updates can potentially take some + * time each and they do not overlap (i.e. no d/f conflicts; + * this can be parallelized using the run_commands.h API. + */ + for (i = 0; i < scheduled_submodules_nr; i++) { + struct submodule *sub; + struct scheduled_submodules_update_type *ssu = + &scheduled_submodules[i]; + + if (!submodule_is_interesting(ssu->path)) + continue; + + sub = submodule_from_path(null_sha1, ssu->path); + + switch (sub->update_strategy) { + case SM_UPDATE_UNSPECIFIED: /* fall thru */ + case SM_UPDATE_CHECKOUT: + update_submodule(ssu->path, ssu->oid, + force, ssu->is_new); + break; + case SM_UPDATE_REBASE: + case SM_UPDATE_MERGE: + case SM_UPDATE_NONE: + case SM_UPDATE_COMMAND: + warning("update strategy for submodule '%s' not supported", ssu->path); + } + } + + scheduled_submodules_nr = 0; + return 0; +} diff --git a/submodule.h b/submodule.h index d8bb1d4baf..74df8b93d5 100644 --- a/submodule.h +++ b/submodule.h @@ -90,4 +90,9 @@ extern int parallel_submodules(void); * retaining any config in the environment. */ extern void prepare_submodule_repo_env(struct argv_array *out); + +extern void schedule_submodule_for_update(const struct cache_entry *ce, + int new); +extern int update_submodules(int force); + #endif -- 2.11.0.rc2.28.g2673dad