Skip to content

Commit c68f837

Browse files
hvoigtgitster
authored andcommitted
implement fetching of moved submodules
We store the changed submodules paths to calculate which submodule needs fetching. This does not work for moved submodules since their paths do not stay the same in case of a moved submodules. In case of new submodules we do not have a path in the current checkout, since they just appeared in this fetch. It is more general to collect the submodule names for changes instead of their paths to include the above cases. If we do not have a configuration for a gitlink we rely on constructing a default name from the path if a git repository can be found at its path. We skip non-configured gitlinks whose default name collides with a configured one. With the change described above we implement 'on-demand' fetching of changes in moved submodules. Signed-off-by: Heiko Voigt <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 01ce122 commit c68f837

File tree

3 files changed

+138
-38
lines changed

3 files changed

+138
-38
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: 100 additions & 38 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);
@@ -1149,11 +1200,19 @@ static int get_next_submodule(struct child_process *cp,
11491200
const struct cache_entry *ce = active_cache[spf->count];
11501201
const char *git_dir, *default_argv;
11511202
const struct submodule *submodule;
1203+
struct submodule default_submodule = SUBMODULE_INIT;
11521204

11531205
if (!S_ISGITLINK(ce->ce_mode))
11541206
continue;
11551207

11561208
submodule = submodule_from_path(&null_oid, ce->name);
1209+
if (!submodule) {
1210+
const char *name = default_name_or_path(ce->name);
1211+
if (name) {
1212+
default_submodule.path = default_submodule.name = name;
1213+
submodule = &default_submodule;
1214+
}
1215+
}
11571216

11581217
default_argv = "yes";
11591218
if (spf->command_line_option == RECURSE_SUBMODULES_DEFAULT) {
@@ -1175,21 +1234,24 @@ static int get_next_submodule(struct child_process *cp,
11751234
if (fetch_recurse == RECURSE_SUBMODULES_OFF)
11761235
continue;
11771236
if (fetch_recurse == RECURSE_SUBMODULES_ON_DEMAND) {
1178-
if (!unsorted_string_list_lookup(&changed_submodule_paths, ce->name))
1237+
if (!unsorted_string_list_lookup(&changed_submodule_names,
1238+
submodule->name))
11791239
continue;
11801240
default_argv = "on-demand";
11811241
}
11821242
} else {
11831243
if (spf->default_option == RECURSE_SUBMODULES_OFF)
11841244
continue;
11851245
if (spf->default_option == RECURSE_SUBMODULES_ON_DEMAND) {
1186-
if (!unsorted_string_list_lookup(&changed_submodule_paths, ce->name))
1246+
if (!unsorted_string_list_lookup(&changed_submodule_names,
1247+
submodule->name))
11871248
continue;
11881249
default_argv = "on-demand";
11891250
}
11901251
}
11911252
} else if (spf->command_line_option == RECURSE_SUBMODULES_ON_DEMAND) {
1192-
if (!unsorted_string_list_lookup(&changed_submodule_paths, ce->name))
1253+
if (!unsorted_string_list_lookup(&changed_submodule_names,
1254+
submodule->name))
11931255
continue;
11941256
default_argv = "on-demand";
11951257
}
@@ -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

t/t5526-fetch-submodules.sh

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -570,4 +570,39 @@ test_expect_success 'fetching submodule into a broken repository' '
570570
test_must_fail git -C dst fetch --recurse-submodules
571571
'
572572

573+
test_expect_success "fetch new commits when submodule got renamed" '
574+
git clone . downstream_rename &&
575+
(
576+
cd downstream_rename &&
577+
git submodule update --init &&
578+
# NEEDSWORK: we omitted --recursive for the submodule update here since
579+
# that does not work. See test 7001 for mv "moving nested submodules"
580+
# for details. Once that is fixed we should add the --recursive option
581+
# here.
582+
git checkout -b rename &&
583+
git mv submodule submodule_renamed &&
584+
(
585+
cd submodule_renamed &&
586+
git checkout -b rename_sub &&
587+
echo a >a &&
588+
git add a &&
589+
git commit -ma &&
590+
git push origin rename_sub &&
591+
git rev-parse HEAD >../../expect
592+
) &&
593+
git add submodule_renamed &&
594+
git commit -m "update renamed submodule" &&
595+
git push origin rename
596+
) &&
597+
(
598+
cd downstream &&
599+
git fetch --recurse-submodules=on-demand &&
600+
(
601+
cd submodule &&
602+
git rev-parse origin/rename_sub >../../actual
603+
)
604+
) &&
605+
test_cmp expect actual
606+
'
607+
573608
test_done

0 commit comments

Comments
 (0)