Skip to content

Commit

Permalink
submodule--helper: initial clone learns retry logic
Browse files Browse the repository at this point in the history
Each submodule that is attempted to be cloned, will be retried once in
case of failure after all other submodules were cloned. This helps to
mitigate ephemeral server failures and increases chances of a reliable
clone of a repo with hundreds of submodules immensely.

Signed-off-by: Stefan Beller <sbeller@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
  • Loading branch information
stefanbeller authored and gitster committed Jun 13, 2016
1 parent abed000 commit 665b35e
Showing 1 changed file with 59 additions and 7 deletions.
66 changes: 59 additions & 7 deletions builtin/submodule--helper.c
Expand Up @@ -592,10 +592,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,
Expand Down Expand Up @@ -720,23 +724,47 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
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;
Expand All @@ -746,15 +774,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)
Expand Down

0 comments on commit 665b35e

Please sign in to comment.