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 | 117 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 submodule.h |   4 +++
 2 files changed, 121 insertions(+)

diff --git a/submodule.c b/submodule.c
index 0fa4613..2773151 100644
--- a/submodule.c
+++ b/submodule.c
@@ -308,6 +308,75 @@ static void print_submodule_summary(struct rev_info *rev, 
FILE *f,
        strbuf_release(&sb);
 }
 
+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 depopulate_submodule(const char *path)
 {
        int ret = 0;
@@ -1336,3 +1405,51 @@ void prepare_submodule_repo_env(struct argv_array *out)
        }
        argv_array_push(out, "GIT_DIR=.git");
 }
+
+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}
+
+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;
+}
+
+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 scheduled_submodules_update_type *ssu =
+                       &scheduled_submodules[i];
+
+               if (submodule_is_interesting(ssu->path, null_sha1))
+                       update_submodule(ssu->path, ssu->oid,
+                                        force, ssu->is_new);
+       }
+
+       scheduled_submodules_nr = 0;
+       return 0;
+}
diff --git a/submodule.h b/submodule.h
index 8518cf3..f01f87c 100644
--- a/submodule.h
+++ b/submodule.h
@@ -94,4 +94,8 @@ extern int parallel_submodules(void);
  */
 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.10.1.469.g00a8914

Reply via email to