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 <sbel...@google.com>
---
 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

Reply via email to