Skip to content

Commit b4d658b

Browse files
committed
Merge branch 'hv/fetch-moved-submodules-on-demand'
"git fetch --recurse-submodules" now knows that submodules can be moved around in the superproject in addition to getting updated, and finds the ones that need to be fetched accordingly. * hv/fetch-moved-submodules-on-demand: submodule: simplify decision tree whether to or not to fetch implement fetching of moved submodules fetch: add test to make sure we stay backwards compatible
2 parents 5a74ce2 + 4b4aced commit b4d658b

File tree

3 files changed

+210
-70
lines changed

3 files changed

+210
-70
lines changed

submodule-config.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ struct submodule {
2222
int recommend_shallow;
2323
};
2424

25+
#define SUBMODULE_INIT { NULL, NULL, NULL, RECURSE_SUBMODULES_NONE, \
26+
NULL, NULL, SUBMODULE_UPDATE_STRATEGY_INIT, {0}, -1 };
27+
2528
struct submodule_cache;
2629
struct repository;
2730

submodule.c

Lines changed: 131 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
#include "parse-options.h"
2222

2323
static int config_update_recurse_submodules = RECURSE_SUBMODULES_OFF;
24-
static struct string_list changed_submodule_paths = STRING_LIST_INIT_DUP;
24+
static struct string_list changed_submodule_names = STRING_LIST_INIT_DUP;
2525
static int initialized_fetch_ref_tips;
2626
static struct oid_array ref_tips_before_fetch;
2727
static struct oid_array ref_tips_after_fetch;
@@ -674,11 +674,11 @@ const struct submodule *submodule_from_ce(const struct cache_entry *ce)
674674
}
675675

676676
static struct oid_array *submodule_commits(struct string_list *submodules,
677-
const char *path)
677+
const char *name)
678678
{
679679
struct string_list_item *item;
680680

681-
item = string_list_insert(submodules, path);
681+
item = string_list_insert(submodules, name);
682682
if (item->util)
683683
return (struct oid_array *) item->util;
684684

@@ -687,39 +687,67 @@ static struct oid_array *submodule_commits(struct string_list *submodules,
687687
return (struct oid_array *) item->util;
688688
}
689689

690+
struct collect_changed_submodules_cb_data {
691+
struct string_list *changed;
692+
const struct object_id *commit_oid;
693+
};
694+
695+
/*
696+
* this would normally be two functions: default_name_from_path() and
697+
* path_from_default_name(). Since the default name is the same as
698+
* the submodule path we can get away with just one function which only
699+
* checks whether there is a submodule in the working directory at that
700+
* location.
701+
*/
702+
static const char *default_name_or_path(const char *path_or_name)
703+
{
704+
int error_code;
705+
706+
if (!is_submodule_populated_gently(path_or_name, &error_code))
707+
return NULL;
708+
709+
return path_or_name;
710+
}
711+
690712
static void collect_changed_submodules_cb(struct diff_queue_struct *q,
691713
struct diff_options *options,
692714
void *data)
693715
{
716+
struct collect_changed_submodules_cb_data *me = data;
717+
struct string_list *changed = me->changed;
718+
const struct object_id *commit_oid = me->commit_oid;
694719
int i;
695-
struct string_list *changed = data;
696720

697721
for (i = 0; i < q->nr; i++) {
698722
struct diff_filepair *p = q->queue[i];
699723
struct oid_array *commits;
724+
const struct submodule *submodule;
725+
const char *name;
726+
700727
if (!S_ISGITLINK(p->two->mode))
701728
continue;
702729

703-
if (S_ISGITLINK(p->one->mode)) {
704-
/*
705-
* NEEDSWORK: We should honor the name configured in
706-
* the .gitmodules file of the commit we are examining
707-
* here to be able to correctly follow submodules
708-
* being moved around.
709-
*/
710-
commits = submodule_commits(changed, p->two->path);
711-
oid_array_append(commits, &p->two->oid);
712-
} else {
713-
/* Submodule is new or was moved here */
714-
/*
715-
* NEEDSWORK: When the .git directories of submodules
716-
* live inside the superprojects .git directory some
717-
* day we should fetch new submodules directly into
718-
* that location too when config or options request
719-
* that so they can be checked out from there.
720-
*/
721-
continue;
730+
submodule = submodule_from_path(commit_oid, p->two->path);
731+
if (submodule)
732+
name = submodule->name;
733+
else {
734+
name = default_name_or_path(p->two->path);
735+
/* make sure name does not collide with existing one */
736+
submodule = submodule_from_name(commit_oid, name);
737+
if (submodule) {
738+
warning("Submodule in commit %s at path: "
739+
"'%s' collides with a submodule named "
740+
"the same. Skipping it.",
741+
oid_to_hex(commit_oid), name);
742+
name = NULL;
743+
}
722744
}
745+
746+
if (!name)
747+
continue;
748+
749+
commits = submodule_commits(changed, name);
750+
oid_array_append(commits, &p->two->oid);
723751
}
724752
}
725753

@@ -742,11 +770,14 @@ static void collect_changed_submodules(struct string_list *changed,
742770

743771
while ((commit = get_revision(&rev))) {
744772
struct rev_info diff_rev;
773+
struct collect_changed_submodules_cb_data data;
774+
data.changed = changed;
775+
data.commit_oid = &commit->object.oid;
745776

746777
init_revisions(&diff_rev, NULL);
747778
diff_rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
748779
diff_rev.diffopt.format_callback = collect_changed_submodules_cb;
749-
diff_rev.diffopt.format_callback_data = changed;
780+
diff_rev.diffopt.format_callback_data = &data;
750781
diff_tree_combined_merge(commit, 1, &diff_rev);
751782
}
752783

@@ -894,7 +925,7 @@ int find_unpushed_submodules(struct oid_array *commits,
894925
const char *remotes_name, struct string_list *needs_pushing)
895926
{
896927
struct string_list submodules = STRING_LIST_INIT_DUP;
897-
struct string_list_item *submodule;
928+
struct string_list_item *name;
898929
struct argv_array argv = ARGV_ARRAY_INIT;
899930

900931
/* argv.argv[0] will be ignored by setup_revisions */
@@ -905,9 +936,19 @@ int find_unpushed_submodules(struct oid_array *commits,
905936

906937
collect_changed_submodules(&submodules, &argv);
907938

908-
for_each_string_list_item(submodule, &submodules) {
909-
struct oid_array *commits = submodule->util;
910-
const char *path = submodule->string;
939+
for_each_string_list_item(name, &submodules) {
940+
struct oid_array *commits = name->util;
941+
const struct submodule *submodule;
942+
const char *path = NULL;
943+
944+
submodule = submodule_from_name(&null_oid, name->string);
945+
if (submodule)
946+
path = submodule->path;
947+
else
948+
path = default_name_or_path(name->string);
949+
950+
if (!path)
951+
continue;
911952

912953
if (submodule_needs_pushing(path, commits))
913954
string_list_insert(needs_pushing, path);
@@ -1065,7 +1106,7 @@ static void calculate_changed_submodule_paths(void)
10651106
{
10661107
struct argv_array argv = ARGV_ARRAY_INIT;
10671108
struct string_list changed_submodules = STRING_LIST_INIT_DUP;
1068-
const struct string_list_item *item;
1109+
const struct string_list_item *name;
10691110

10701111
/* No need to check if there are no submodules configured */
10711112
if (!submodule_from_path(NULL, NULL))
@@ -1080,16 +1121,26 @@ static void calculate_changed_submodule_paths(void)
10801121

10811122
/*
10821123
* Collect all submodules (whether checked out or not) for which new
1083-
* commits have been recorded upstream in "changed_submodule_paths".
1124+
* commits have been recorded upstream in "changed_submodule_names".
10841125
*/
10851126
collect_changed_submodules(&changed_submodules, &argv);
10861127

1087-
for_each_string_list_item(item, &changed_submodules) {
1088-
struct oid_array *commits = item->util;
1089-
const char *path = item->string;
1128+
for_each_string_list_item(name, &changed_submodules) {
1129+
struct oid_array *commits = name->util;
1130+
const struct submodule *submodule;
1131+
const char *path = NULL;
1132+
1133+
submodule = submodule_from_name(&null_oid, name->string);
1134+
if (submodule)
1135+
path = submodule->path;
1136+
else
1137+
path = default_name_or_path(name->string);
1138+
1139+
if (!path)
1140+
continue;
10901141

10911142
if (!submodule_has_commits(path, commits))
1092-
string_list_append(&changed_submodule_paths, path);
1143+
string_list_append(&changed_submodule_names, name->string);
10931144
}
10941145

10951146
free_submodules_oids(&changed_submodules);
@@ -1136,6 +1187,31 @@ struct submodule_parallel_fetch {
11361187
};
11371188
#define SPF_INIT {0, ARGV_ARRAY_INIT, NULL, NULL, 0, 0, 0, 0}
11381189

1190+
static int get_fetch_recurse_config(const struct submodule *submodule,
1191+
struct submodule_parallel_fetch *spf)
1192+
{
1193+
if (spf->command_line_option != RECURSE_SUBMODULES_DEFAULT)
1194+
return spf->command_line_option;
1195+
1196+
if (submodule) {
1197+
char *key;
1198+
const char *value;
1199+
1200+
int fetch_recurse = submodule->fetch_recurse;
1201+
key = xstrfmt("submodule.%s.fetchRecurseSubmodules", submodule->name);
1202+
if (!repo_config_get_string_const(the_repository, key, &value)) {
1203+
fetch_recurse = parse_fetch_recurse_submodules_arg(key, value);
1204+
}
1205+
free(key);
1206+
1207+
if (fetch_recurse != RECURSE_SUBMODULES_NONE)
1208+
/* local config overrules everything except commandline */
1209+
return fetch_recurse;
1210+
}
1211+
1212+
return spf->default_option;
1213+
}
1214+
11391215
static int get_next_submodule(struct child_process *cp,
11401216
struct strbuf *err, void *data, void **task_cb)
11411217
{
@@ -1149,49 +1225,35 @@ static int get_next_submodule(struct child_process *cp,
11491225
const struct cache_entry *ce = active_cache[spf->count];
11501226
const char *git_dir, *default_argv;
11511227
const struct submodule *submodule;
1228+
struct submodule default_submodule = SUBMODULE_INIT;
11521229

11531230
if (!S_ISGITLINK(ce->ce_mode))
11541231
continue;
11551232

11561233
submodule = submodule_from_path(&null_oid, ce->name);
1157-
1158-
default_argv = "yes";
1159-
if (spf->command_line_option == RECURSE_SUBMODULES_DEFAULT) {
1160-
int fetch_recurse = RECURSE_SUBMODULES_NONE;
1161-
1162-
if (submodule) {
1163-
char *key;
1164-
const char *value;
1165-
1166-
fetch_recurse = submodule->fetch_recurse;
1167-
key = xstrfmt("submodule.%s.fetchRecurseSubmodules", submodule->name);
1168-
if (!repo_config_get_string_const(the_repository, key, &value)) {
1169-
fetch_recurse = parse_fetch_recurse_submodules_arg(key, value);
1170-
}
1171-
free(key);
1234+
if (!submodule) {
1235+
const char *name = default_name_or_path(ce->name);
1236+
if (name) {
1237+
default_submodule.path = default_submodule.name = name;
1238+
submodule = &default_submodule;
11721239
}
1240+
}
11731241

1174-
if (fetch_recurse != RECURSE_SUBMODULES_NONE) {
1175-
if (fetch_recurse == RECURSE_SUBMODULES_OFF)
1176-
continue;
1177-
if (fetch_recurse == RECURSE_SUBMODULES_ON_DEMAND) {
1178-
if (!unsorted_string_list_lookup(&changed_submodule_paths, ce->name))
1179-
continue;
1180-
default_argv = "on-demand";
1181-
}
1182-
} else {
1183-
if (spf->default_option == RECURSE_SUBMODULES_OFF)
1184-
continue;
1185-
if (spf->default_option == RECURSE_SUBMODULES_ON_DEMAND) {
1186-
if (!unsorted_string_list_lookup(&changed_submodule_paths, ce->name))
1187-
continue;
1188-
default_argv = "on-demand";
1189-
}
1190-
}
1191-
} else if (spf->command_line_option == RECURSE_SUBMODULES_ON_DEMAND) {
1192-
if (!unsorted_string_list_lookup(&changed_submodule_paths, ce->name))
1242+
switch (get_fetch_recurse_config(submodule, spf))
1243+
{
1244+
default:
1245+
case RECURSE_SUBMODULES_DEFAULT:
1246+
case RECURSE_SUBMODULES_ON_DEMAND:
1247+
if (!submodule || !unsorted_string_list_lookup(&changed_submodule_names,
1248+
submodule->name))
11931249
continue;
11941250
default_argv = "on-demand";
1251+
break;
1252+
case RECURSE_SUBMODULES_ON:
1253+
default_argv = "yes";
1254+
break;
1255+
case RECURSE_SUBMODULES_OFF:
1256+
continue;
11951257
}
11961258

11971259
strbuf_addf(&submodule_path, "%s/%s", spf->work_tree, ce->name);
@@ -1282,7 +1344,7 @@ int fetch_populated_submodules(const struct argv_array *options,
12821344

12831345
argv_array_clear(&spf.args);
12841346
out:
1285-
string_list_clear(&changed_submodule_paths, 1);
1347+
string_list_clear(&changed_submodule_names, 1);
12861348
return spf.result;
12871349
}
12881350

0 commit comments

Comments
 (0)