Merge branch 'sb/submodule-clone-retry'
authorJunio C Hamano <gitster@pobox.com>
Mon, 11 Jul 2016 17:31:04 +0000 (10:31 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 11 Jul 2016 17:31:04 +0000 (10:31 -0700)
"git submodule update" that drives many "git clone" could
eventually hit flaky servers/network conditions on one of the
submodules; the command learned to retry the attempt.

* sb/submodule-clone-retry:
  submodule update: continue when a clone fails
  submodule--helper: initial clone learns retry logic

builtin/submodule--helper.c
git-submodule.sh

index c7deb55..b22352b 100644 (file)
@@ -590,10 +590,14 @@ struct submodule_update_clone {
 
        /* If we want to stop as fast as possible and return an error */
        unsigned quickstop : 1;
+
+       /* failed clones to be retried again */
+       const struct cache_entry **failed_clones;
+       int failed_clones_nr, failed_clones_alloc;
 };
 #define SUBMODULE_UPDATE_CLONE_INIT {0, MODULE_LIST_INIT, 0, \
        SUBMODULE_UPDATE_STRATEGY_INIT, 0, -1, NULL, NULL, NULL, NULL, \
-       STRING_LIST_INIT_DUP, 0}
+       STRING_LIST_INIT_DUP, 0, NULL, 0, 0}
 
 
 static void next_submodule_warn_missing(struct submodule_update_clone *suc,
@@ -718,23 +722,47 @@ cleanup:
 static int update_clone_get_next_task(struct child_process *child,
                                      struct strbuf *err,
                                      void *suc_cb,
-                                     void **void_task_cb)
+                                     void **idx_task_cb)
 {
        struct submodule_update_clone *suc = suc_cb;
+       const struct cache_entry *ce;
+       int index;
 
        for (; suc->current < suc->list.nr; suc->current++) {
-               const struct cache_entry *ce = suc->list.entries[suc->current];
+               ce = suc->list.entries[suc->current];
                if (prepare_to_clone_next_submodule(ce, child, suc, err)) {
+                       int *p = xmalloc(sizeof(*p));
+                       *p = suc->current;
+                       *idx_task_cb = p;
                        suc->current++;
                        return 1;
                }
        }
+
+       /*
+        * The loop above tried cloning each submodule once, now try the
+        * stragglers again, which we can imagine as an extension of the
+        * entry list.
+        */
+       index = suc->current - suc->list.nr;
+       if (index < suc->failed_clones_nr) {
+               int *p;
+               ce = suc->failed_clones[index];
+               if (!prepare_to_clone_next_submodule(ce, child, suc, err))
+                       die("BUG: ce was a submodule before?");
+               p = xmalloc(sizeof(*p));
+               *p = suc->current;
+               *idx_task_cb = p;
+               suc->current ++;
+               return 1;
+       }
+
        return 0;
 }
 
 static int update_clone_start_failure(struct strbuf *err,
                                      void *suc_cb,
-                                     void *void_task_cb)
+                                     void *idx_task_cb)
 {
        struct submodule_update_clone *suc = suc_cb;
        suc->quickstop = 1;
@@ -744,15 +772,39 @@ static int update_clone_start_failure(struct strbuf *err,
 static int update_clone_task_finished(int result,
                                      struct strbuf *err,
                                      void *suc_cb,
-                                     void *void_task_cb)
+                                     void *idx_task_cb)
 {
+       const struct cache_entry *ce;
        struct submodule_update_clone *suc = suc_cb;
 
+       int *idxP = *(int**)idx_task_cb;
+       int idx = *idxP;
+       free(idxP);
+
        if (!result)
                return 0;
 
-       suc->quickstop = 1;
-       return 1;
+       if (idx < suc->list.nr) {
+               ce  = suc->list.entries[idx];
+               strbuf_addf(err, _("Failed to clone '%s'. Retry scheduled"),
+                           ce->name);
+               strbuf_addch(err, '\n');
+               ALLOC_GROW(suc->failed_clones,
+                          suc->failed_clones_nr + 1,
+                          suc->failed_clones_alloc);
+               suc->failed_clones[suc->failed_clones_nr++] = ce;
+               return 0;
+       } else {
+               idx = suc->current - suc->list.nr;
+               ce  = suc->failed_clones[idx];
+               strbuf_addf(err, _("Failed to clone '%s' a second time, aborting"),
+                           ce->name);
+               strbuf_addch(err, '\n');
+               suc->quickstop = 1;
+               return 1;
+       }
+
+       return 0;
 }
 
 static int update_clone(int argc, const char **argv, const char *prefix)
index b39ac10..7c62b53 100755 (executable)
@@ -705,7 +705,7 @@ cmd_update()
                        if test $res -gt 0
                        then
                                die_msg="$(eval_gettext "Failed to recurse into submodule path '\$displaypath'")"
-                               if test $res -eq 1
+                               if test $res -ne 2
                                then
                                        err="${err};$die_msg"
                                        continue