Skip to content

Commit 88a2197

Browse files
jlehmanngitster
authored andcommitted
fetch/pull: recurse into submodules when necessary
To be able to access all commits of populated submodules referenced by the superproject it is sufficient to only then let "git fetch" recurse into a submodule when the new commits fetched in the superproject record new commits for it. Having these commits present is extremely useful when using the "--submodule" option to "git diff" (which is what "git gui" and "gitk" do since 1.6.6), as all submodule commits needed for creating a descriptive output can be accessed. Also merging submodule commits (added in 1.7.3) depends on the submodule commits in question being present to work. Last but not least this enables disconnected operation when using submodules, as all commits necessary for a successful "git submodule update -N" will have been fetched automatically. So we choose this mode as the default for fetch and pull. Before a new or changed ref from upstream is updated in update_local_ref() "git rev-list <new-sha1> --not --branches --remotes" is used to determine all newly fetched commits. These are then walked and diffed against their parent(s) to see if a submodule has been changed. If that is the case, its path is stored to be fetched after the superproject fetch is completed. Using the "--recurse-submodules" or the "--no-recurse-submodules" option disables the examination of the fetched refs because the result will be ignored anyway. There is currently no infrastructure for storing deleted and new submodules in the .git directory of the superproject. That's why fetch and pull for now only fetch submodules that are already checked out and are not renamed. In t7403 the "--no-recurse-submodules" argument had to be added to "git pull" to avoid failure because of the moved upstream submodule repo. Thanks-to: Jonathan Nieder <[email protected]> Thanks-to: Heiko Voigt <[email protected]> Signed-off-by: Jens Lehmann <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 046613c commit 88a2197

File tree

6 files changed

+243
-17
lines changed

6 files changed

+243
-17
lines changed

Documentation/fetch-options.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,14 @@ ifndef::git-pull[]
7373
Prepend <path> to paths printed in informative messages
7474
such as "Fetching submodule foo". This option is used
7575
internally when recursing over submodules.
76+
77+
--recurse-submodules-default=[yes|on-demand]::
78+
This option is used internally to temporarily provide a
79+
non-negative default value for the --recurse-submodules
80+
option. All other methods of configuring fetch's submodule
81+
recursion (such as settings in linkgit:gitmodules[5] and
82+
linkgit:git-config[1]) override this option, as does
83+
specifying --[no-]recurse-submodules directly.
7684
endif::git-pull[]
7785

7886
-u::

builtin/fetch.c

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,6 @@ enum {
2828
TAGS_SET = 2
2929
};
3030

31-
enum {
32-
RECURSE_SUBMODULES_OFF = 0,
33-
RECURSE_SUBMODULES_DEFAULT = 1,
34-
RECURSE_SUBMODULES_ON = 2
35-
};
36-
3731
static int all, append, dry_run, force, keep, multiple, prune, update_head_ok, verbosity;
3832
static int progress, recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
3933
static int tags = TAGS_DEFAULT;
@@ -42,6 +36,7 @@ static const char *upload_pack;
4236
static struct strbuf default_rla = STRBUF_INIT;
4337
static struct transport *transport;
4438
static const char *submodule_prefix = "";
39+
static const char *recurse_submodules_default;
4540

4641
static struct option builtin_fetch_options[] = {
4742
OPT__VERBOSITY(&verbosity),
@@ -73,6 +68,9 @@ static struct option builtin_fetch_options[] = {
7368
"deepen history of shallow clone"),
7469
{ OPTION_STRING, 0, "submodule-prefix", &submodule_prefix, "DIR",
7570
"prepend this to submodule path output", PARSE_OPT_HIDDEN },
71+
{ OPTION_STRING, 0, "recurse-submodules-default",
72+
&recurse_submodules_default, NULL,
73+
"default mode for recursion", PARSE_OPT_HIDDEN },
7674
OPT_END()
7775
};
7876

@@ -284,6 +282,9 @@ static int update_local_ref(struct ref *ref,
284282
else {
285283
msg = "storing head";
286284
what = "[new branch]";
285+
if ((recurse_submodules != RECURSE_SUBMODULES_OFF) &&
286+
(recurse_submodules != RECURSE_SUBMODULES_ON))
287+
check_for_new_submodule_commits(ref->new_sha1);
287288
}
288289

289290
r = s_update_ref(msg, ref, 0);
@@ -299,6 +300,9 @@ static int update_local_ref(struct ref *ref,
299300
strcpy(quickref, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV));
300301
strcat(quickref, "..");
301302
strcat(quickref, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV));
303+
if ((recurse_submodules != RECURSE_SUBMODULES_OFF) &&
304+
(recurse_submodules != RECURSE_SUBMODULES_ON))
305+
check_for_new_submodule_commits(ref->new_sha1);
302306
r = s_update_ref("fast-forward", ref, 1);
303307
sprintf(display, "%c %-*s %-*s -> %s%s", r ? '!' : ' ',
304308
TRANSPORT_SUMMARY_WIDTH, quickref, REFCOL_WIDTH, remote,
@@ -310,6 +314,9 @@ static int update_local_ref(struct ref *ref,
310314
strcpy(quickref, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV));
311315
strcat(quickref, "...");
312316
strcat(quickref, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV));
317+
if ((recurse_submodules != RECURSE_SUBMODULES_OFF) &&
318+
(recurse_submodules != RECURSE_SUBMODULES_ON))
319+
check_for_new_submodule_commits(ref->new_sha1);
313320
r = s_update_ref("forced-update", ref, 1);
314321
sprintf(display, "%c %-*s %-*s -> %s (%s)", r ? '!' : '+',
315322
TRANSPORT_SUMMARY_WIDTH, quickref, REFCOL_WIDTH, remote,
@@ -949,9 +956,10 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
949956
if (!result && (recurse_submodules != RECURSE_SUBMODULES_OFF)) {
950957
const char *options[10];
951958
int num_options = 0;
952-
/* Set recursion as default when we already are recursing */
953-
if (submodule_prefix[0])
954-
set_config_fetch_recurse_submodules(1);
959+
if (recurse_submodules_default) {
960+
int arg = parse_fetch_recurse_submodules_arg("--recurse-submodules-default", recurse_submodules_default);
961+
set_config_fetch_recurse_submodules(arg);
962+
}
955963
gitmodules_config();
956964
git_config(submodule_config, NULL);
957965
add_options_to_argv(&num_options, options);

submodule.c

Lines changed: 99 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
struct string_list config_name_for_path;
1313
struct string_list config_fetch_recurse_submodules_for_name;
1414
struct string_list config_ignore_for_name;
15-
static int config_fetch_recurse_submodules;
15+
static int config_fetch_recurse_submodules = RECURSE_SUBMODULES_ON_DEMAND;
16+
struct string_list changed_submodule_paths;
1617

1718
static int add_submodule_odb(const char *path)
1819
{
@@ -152,6 +153,20 @@ void handle_ignore_submodules_arg(struct diff_options *diffopt,
152153
die("bad --ignore-submodules argument: %s", arg);
153154
}
154155

156+
int parse_fetch_recurse_submodules_arg(const char *opt, const char *arg)
157+
{
158+
switch (git_config_maybe_bool(opt, arg)) {
159+
case 1:
160+
return RECURSE_SUBMODULES_ON;
161+
case 0:
162+
return RECURSE_SUBMODULES_OFF;
163+
default:
164+
if (!strcmp(arg, "on-demand"))
165+
return RECURSE_SUBMODULES_ON_DEMAND;
166+
die("bad %s argument: %s", opt, arg);
167+
}
168+
}
169+
155170
void show_submodule_summary(FILE *f, const char *path,
156171
unsigned char one[20], unsigned char two[20],
157172
unsigned dirty_submodule,
@@ -248,27 +263,95 @@ void set_config_fetch_recurse_submodules(int value)
248263
config_fetch_recurse_submodules = value;
249264
}
250265

266+
static void submodule_collect_changed_cb(struct diff_queue_struct *q,
267+
struct diff_options *options,
268+
void *data)
269+
{
270+
int i;
271+
for (i = 0; i < q->nr; i++) {
272+
struct diff_filepair *p = q->queue[i];
273+
if (!S_ISGITLINK(p->two->mode))
274+
continue;
275+
276+
if (S_ISGITLINK(p->one->mode)) {
277+
/* NEEDSWORK: We should honor the name configured in
278+
* the .gitmodules file of the commit we are examining
279+
* here to be able to correctly follow submodules
280+
* being moved around. */
281+
struct string_list_item *path;
282+
path = unsorted_string_list_lookup(&changed_submodule_paths, p->two->path);
283+
if (!path)
284+
string_list_append(&changed_submodule_paths, xstrdup(p->two->path));
285+
} else {
286+
/* Submodule is new or was moved here */
287+
/* NEEDSWORK: When the .git directories of submodules
288+
* live inside the superprojects .git directory some
289+
* day we should fetch new submodules directly into
290+
* that location too when config or options request
291+
* that so they can be checked out from there. */
292+
continue;
293+
}
294+
}
295+
}
296+
297+
void check_for_new_submodule_commits(unsigned char new_sha1[20])
298+
{
299+
struct rev_info rev;
300+
struct commit *commit;
301+
const char *argv[] = {NULL, NULL, "--not", "--all", NULL};
302+
int argc = ARRAY_SIZE(argv) - 1;
303+
304+
init_revisions(&rev, NULL);
305+
argv[1] = xstrdup(sha1_to_hex(new_sha1));
306+
setup_revisions(argc, argv, &rev, NULL);
307+
if (prepare_revision_walk(&rev))
308+
die("revision walk setup failed");
309+
310+
/*
311+
* Collect all submodules (whether checked out or not) for which new
312+
* commits have been recorded upstream in "changed_submodule_paths".
313+
*/
314+
while ((commit = get_revision(&rev))) {
315+
struct commit_list *parent = commit->parents;
316+
while (parent) {
317+
struct diff_options diff_opts;
318+
diff_setup(&diff_opts);
319+
diff_opts.output_format |= DIFF_FORMAT_CALLBACK;
320+
diff_opts.format_callback = submodule_collect_changed_cb;
321+
if (diff_setup_done(&diff_opts) < 0)
322+
die("diff_setup_done failed");
323+
diff_tree_sha1(parent->item->object.sha1, commit->object.sha1, "", &diff_opts);
324+
diffcore_std(&diff_opts);
325+
diff_flush(&diff_opts);
326+
parent = parent->next;
327+
}
328+
}
329+
free((char *)argv[1]);
330+
}
331+
251332
int fetch_populated_submodules(int num_options, const char **options,
252333
const char *prefix, int ignore_config,
253334
int quiet)
254335
{
255-
int i, result = 0, argc = 0;
336+
int i, result = 0, argc = 0, default_argc;
256337
struct child_process cp;
257338
const char **argv;
258339
struct string_list_item *name_for_path;
259340
const char *work_tree = get_git_work_tree();
260341
if (!work_tree)
261-
return 0;
342+
goto out;
262343

263344
if (!the_index.initialized)
264345
if (read_cache() < 0)
265346
die("index file corrupt");
266347

267-
/* 4: "fetch" (options) "--submodule-prefix" prefix NULL */
268-
argv = xcalloc(num_options + 4, sizeof(const char *));
348+
/* 6: "fetch" (options) --recurse-submodules-default default "--submodule-prefix" prefix NULL */
349+
argv = xcalloc(num_options + 6, sizeof(const char *));
269350
argv[argc++] = "fetch";
270351
for (i = 0; i < num_options; i++)
271352
argv[argc++] = options[i];
353+
argv[argc++] = "--recurse-submodules-default";
354+
default_argc = argc++;
272355
argv[argc++] = "--submodule-prefix";
273356

274357
memset(&cp, 0, sizeof(cp));
@@ -282,7 +365,7 @@ int fetch_populated_submodules(int num_options, const char **options,
282365
struct strbuf submodule_git_dir = STRBUF_INIT;
283366
struct strbuf submodule_prefix = STRBUF_INIT;
284367
struct cache_entry *ce = active_cache[i];
285-
const char *git_dir, *name;
368+
const char *git_dir, *name, *default_argv;
286369

287370
if (!S_ISGITLINK(ce->ce_mode))
288371
continue;
@@ -292,15 +375,21 @@ int fetch_populated_submodules(int num_options, const char **options,
292375
if (name_for_path)
293376
name = name_for_path->util;
294377

378+
default_argv = "yes";
295379
if (!ignore_config) {
296380
struct string_list_item *fetch_recurse_submodules_option;
297381
fetch_recurse_submodules_option = unsorted_string_list_lookup(&config_fetch_recurse_submodules_for_name, name);
298382
if (fetch_recurse_submodules_option) {
299383
if (!fetch_recurse_submodules_option->util)
300384
continue;
301385
} else {
302-
if (!config_fetch_recurse_submodules)
386+
if (config_fetch_recurse_submodules == RECURSE_SUBMODULES_OFF)
303387
continue;
388+
if (config_fetch_recurse_submodules == RECURSE_SUBMODULES_ON_DEMAND) {
389+
if (!unsorted_string_list_lookup(&changed_submodule_paths, ce->name))
390+
continue;
391+
default_argv = "on-demand";
392+
}
304393
}
305394
}
306395

@@ -314,6 +403,7 @@ int fetch_populated_submodules(int num_options, const char **options,
314403
if (!quiet)
315404
printf("Fetching submodule %s%s\n", prefix, ce->name);
316405
cp.dir = submodule_path.buf;
406+
argv[default_argc] = default_argv;
317407
argv[argc] = submodule_prefix.buf;
318408
if (run_command(&cp))
319409
result = 1;
@@ -323,6 +413,8 @@ int fetch_populated_submodules(int num_options, const char **options,
323413
strbuf_release(&submodule_prefix);
324414
}
325415
free(argv);
416+
out:
417+
string_list_clear(&changed_submodule_paths, 1);
326418
return result;
327419
}
328420

submodule.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,26 @@
33

44
struct diff_options;
55

6+
enum {
7+
RECURSE_SUBMODULES_ON_DEMAND = -1,
8+
RECURSE_SUBMODULES_OFF = 0,
9+
RECURSE_SUBMODULES_DEFAULT = 1,
10+
RECURSE_SUBMODULES_ON = 2
11+
};
12+
613
void set_diffopt_flags_from_submodule_config(struct diff_options *diffopt,
714
const char *path);
815
int submodule_config(const char *var, const char *value, void *cb);
916
void gitmodules_config();
1017
int parse_submodule_config_option(const char *var, const char *value);
1118
void handle_ignore_submodules_arg(struct diff_options *diffopt, const char *);
19+
int parse_fetch_recurse_submodules_arg(const char *opt, const char *arg);
1220
void show_submodule_summary(FILE *f, const char *path,
1321
unsigned char one[20], unsigned char two[20],
1422
unsigned dirty_submodule,
1523
const char *del, const char *add, const char *reset);
1624
void set_config_fetch_recurse_submodules(int value);
25+
void check_for_new_submodule_commits(unsigned char new_sha1[20]);
1726
int fetch_populated_submodules(int num_options, const char **options,
1827
const char *prefix, int ignore_config,
1928
int quiet);

0 commit comments

Comments
 (0)