Skip to content

Commit d54dea7

Browse files
dschogitster
authored andcommitted
fetch: let --jobs=<n> parallelize --multiple, too
So far, `--jobs=<n>` only parallelizes submodule fetches/clones, not `--multiple` fetches, which is unintuitive, given that the option's name does not say anything about submodules in particular. Let's change that. With this patch, also fetches from multiple remotes are parallelized. For backwards-compatibility (and to prepare for a use case where submodule and multiple-remote fetches may need different parallelization limits), the config setting `submodule.fetchJobs` still only controls the submodule part of `git fetch`, while the newly-introduced setting `fetch.parallel` controls both (but can be overridden for submodules with `submodule.fetchJobs`). Signed-off-by: Johannes Schindelin <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 67feca3 commit d54dea7

File tree

4 files changed

+137
-21
lines changed

4 files changed

+137
-21
lines changed

Documentation/config/fetch.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,13 @@ fetch.showForcedUpdates::
6868
Set to false to enable `--no-show-forced-updates` in
6969
linkgit:git-fetch[1] and linkgit:git-pull[1] commands.
7070
Defaults to true.
71+
72+
fetch.parallel::
73+
Specifies the maximal number of fetch operations to be run in parallel
74+
at a time (submodules, or remotes when the `--multiple` option of
75+
linkgit:git-fetch[1] is in effect).
76+
+
77+
A value of 0 will give some reasonable default. If unset, it defaults to 1.
78+
+
79+
For submodules, this setting can be overridden using the `submodule.fetchJobs`
80+
config setting.

Documentation/fetch-options.txt

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -160,10 +160,15 @@ ifndef::git-pull[]
160160

161161
-j::
162162
--jobs=<n>::
163-
Number of parallel children to be used for fetching submodules.
164-
Each will fetch from different submodules, such that fetching many
165-
submodules will be faster. By default submodules will be fetched
166-
one at a time.
163+
Number of parallel children to be used for all forms of fetching.
164+
+
165+
If the `--multiple` option was specified, the different remotes will be fetched
166+
in parallel. If multiple submodules are fetched, they will be fetched in
167+
parallel. To control them independently, use the config settings
168+
`fetch.parallel` and `submodule.fetchJobs` (see linkgit:git-config[1]).
169+
+
170+
Typically, parallel recursive and multi-remote fetches will be faster. By
171+
default fetches are performed sequentially, not in parallel.
167172

168173
--no-recurse-submodules::
169174
Disable recursive fetching of submodules (this has the same effect as

builtin/fetch.c

Lines changed: 107 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@ static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosit
5454
static int progress = -1;
5555
static int enable_auto_gc = 1;
5656
static int tags = TAGS_DEFAULT, unshallow, update_shallow, deepen;
57-
static int max_children = 1;
57+
static int max_jobs = -1, submodule_fetch_jobs_config = -1;
58+
static int fetch_parallel_config = 1;
5859
static enum transport_family family;
5960
static const char *depth;
6061
static const char *deepen_since;
@@ -96,13 +97,20 @@ static int git_fetch_config(const char *k, const char *v, void *cb)
9697
}
9798

9899
if (!strcmp(k, "submodule.fetchjobs")) {
99-
max_children = parse_submodule_fetchjobs(k, v);
100+
submodule_fetch_jobs_config = parse_submodule_fetchjobs(k, v);
100101
return 0;
101102
} else if (!strcmp(k, "fetch.recursesubmodules")) {
102103
recurse_submodules = parse_fetch_recurse_submodules_arg(k, v);
103104
return 0;
104105
}
105106

107+
if (!strcmp(k, "fetch.parallel")) {
108+
fetch_parallel_config = git_config_int(k, v);
109+
if (fetch_parallel_config < 0)
110+
die(_("fetch.parallel cannot be negative"));
111+
return 0;
112+
}
113+
106114
return git_default_config(k, v, cb);
107115
}
108116

@@ -134,7 +142,7 @@ static struct option builtin_fetch_options[] = {
134142
N_("fetch all tags and associated objects"), TAGS_SET),
135143
OPT_SET_INT('n', NULL, &tags,
136144
N_("do not fetch all tags (--no-tags)"), TAGS_UNSET),
137-
OPT_INTEGER('j', "jobs", &max_children,
145+
OPT_INTEGER('j', "jobs", &max_jobs,
138146
N_("number of submodules fetched in parallel")),
139147
OPT_BOOL('p', "prune", &prune,
140148
N_("prune remote-tracking branches no longer on remote")),
@@ -1456,7 +1464,62 @@ static void add_options_to_argv(struct argv_array *argv)
14561464

14571465
}
14581466

1459-
static int fetch_multiple(struct string_list *list)
1467+
/* Fetch multiple remotes in parallel */
1468+
1469+
struct parallel_fetch_state {
1470+
const char **argv;
1471+
struct string_list *remotes;
1472+
int next, result;
1473+
};
1474+
1475+
static int fetch_next_remote(struct child_process *cp, struct strbuf *out,
1476+
void *cb, void **task_cb)
1477+
{
1478+
struct parallel_fetch_state *state = cb;
1479+
char *remote;
1480+
1481+
if (state->next < 0 || state->next >= state->remotes->nr)
1482+
return 0;
1483+
1484+
remote = state->remotes->items[state->next++].string;
1485+
*task_cb = remote;
1486+
1487+
argv_array_pushv(&cp->args, state->argv);
1488+
argv_array_push(&cp->args, remote);
1489+
cp->git_cmd = 1;
1490+
1491+
if (verbosity >= 0)
1492+
printf(_("Fetching %s\n"), remote);
1493+
1494+
return 1;
1495+
}
1496+
1497+
static int fetch_failed_to_start(struct strbuf *out, void *cb, void *task_cb)
1498+
{
1499+
struct parallel_fetch_state *state = cb;
1500+
const char *remote = task_cb;
1501+
1502+
state->result = error(_("Could not fetch %s"), remote);
1503+
1504+
return 0;
1505+
}
1506+
1507+
static int fetch_finished(int result, struct strbuf *out,
1508+
void *cb, void *task_cb)
1509+
{
1510+
struct parallel_fetch_state *state = cb;
1511+
const char *remote = task_cb;
1512+
1513+
if (result) {
1514+
strbuf_addf(out, _("could not fetch '%s' (exit code: %d)\n"),
1515+
remote, result);
1516+
state->result = -1;
1517+
}
1518+
1519+
return 0;
1520+
}
1521+
1522+
static int fetch_multiple(struct string_list *list, int max_children)
14601523
{
14611524
int i, result = 0;
14621525
struct argv_array argv = ARGV_ARRAY_INIT;
@@ -1470,20 +1533,34 @@ static int fetch_multiple(struct string_list *list)
14701533
argv_array_pushl(&argv, "fetch", "--append", "--no-auto-gc", NULL);
14711534
add_options_to_argv(&argv);
14721535

1473-
for (i = 0; i < list->nr; i++) {
1474-
const char *name = list->items[i].string;
1475-
argv_array_push(&argv, name);
1476-
if (verbosity >= 0)
1477-
printf(_("Fetching %s\n"), name);
1478-
if (run_command_v_opt(argv.argv, RUN_GIT_CMD)) {
1479-
error(_("Could not fetch %s"), name);
1480-
result = 1;
1536+
if (max_children != 1 && list->nr != 1) {
1537+
struct parallel_fetch_state state = { argv.argv, list, 0, 0 };
1538+
1539+
argv_array_push(&argv, "--end-of-options");
1540+
result = run_processes_parallel_tr2(max_children,
1541+
&fetch_next_remote,
1542+
&fetch_failed_to_start,
1543+
&fetch_finished,
1544+
&state,
1545+
"fetch", "parallel/fetch");
1546+
1547+
if (!result)
1548+
result = state.result;
1549+
} else
1550+
for (i = 0; i < list->nr; i++) {
1551+
const char *name = list->items[i].string;
1552+
argv_array_push(&argv, name);
1553+
if (verbosity >= 0)
1554+
printf(_("Fetching %s\n"), name);
1555+
if (run_command_v_opt(argv.argv, RUN_GIT_CMD)) {
1556+
error(_("Could not fetch %s"), name);
1557+
result = 1;
1558+
}
1559+
argv_array_pop(&argv);
14811560
}
1482-
argv_array_pop(&argv);
1483-
}
14841561

14851562
argv_array_clear(&argv);
1486-
return result;
1563+
return !!result;
14871564
}
14881565

14891566
/*
@@ -1626,7 +1703,8 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
16261703
for (i = 1; i < argc; i++)
16271704
strbuf_addf(&default_rla, " %s", argv[i]);
16281705

1629-
fetch_config_from_gitmodules(&max_children, &recurse_submodules);
1706+
fetch_config_from_gitmodules(&submodule_fetch_jobs_config,
1707+
&recurse_submodules);
16301708
git_config(git_fetch_config, NULL);
16311709

16321710
argc = parse_options(argc, argv, prefix,
@@ -1692,15 +1770,27 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
16921770
fetch_one_setup_partial(remote);
16931771
result = fetch_one(remote, argc, argv, prune_tags_ok);
16941772
} else {
1773+
int max_children = max_jobs;
1774+
16951775
if (filter_options.choice)
16961776
die(_("--filter can only be used with the remote "
16971777
"configured in extensions.partialclone"));
1778+
1779+
if (max_children < 0)
1780+
max_children = fetch_parallel_config;
1781+
16981782
/* TODO should this also die if we have a previous partial-clone? */
1699-
result = fetch_multiple(&list);
1783+
result = fetch_multiple(&list, max_children);
17001784
}
17011785

17021786
if (!result && (recurse_submodules != RECURSE_SUBMODULES_OFF)) {
17031787
struct argv_array options = ARGV_ARRAY_INIT;
1788+
int max_children = max_jobs;
1789+
1790+
if (max_children < 0)
1791+
max_children = submodule_fetch_jobs_config;
1792+
if (max_children < 0)
1793+
max_children = fetch_parallel_config;
17041794

17051795
add_options_to_argv(&options);
17061796
result = fetch_populated_submodules(the_repository,

t/t5514-fetch-multiple.sh

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,4 +183,15 @@ test_expect_success 'git fetch --all --tags' '
183183
test_cmp expect test8/output
184184
'
185185

186+
test_expect_success 'parallel' '
187+
git remote add one ./bogus1 &&
188+
git remote add two ./bogus2 &&
189+
190+
test_must_fail env GIT_TRACE="$PWD/trace" \
191+
git fetch --jobs=2 --multiple one two 2>err &&
192+
grep "preparing to run up to 2 tasks" trace &&
193+
test_i18ngrep "could not fetch .one.*128" err &&
194+
test_i18ngrep "could not fetch .two.*128" err
195+
'
196+
186197
test_done

0 commit comments

Comments
 (0)