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 <hvoigt@hvoigt.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Heiko Voigt committed Oct 16, 2017 at 15:58 UTC c68f8375760dc6b77f9c68772230a9c6cfd52aa7
3 files changed +138 -38
submodule-config.h
+3
@@ -22,6 +22,9 @@ struct submodule {
22 int recommend_shallow;
23 };
24
25 +#define SUBMODULE_INIT { NULL, NULL, NULL, RECURSE_SUBMODULES_NONE, \
26 + NULL, NULL, SUBMODULE_UPDATE_STRATEGY_INIT, {0}, -1 };
27 +
28 struct submodule_cache;
29 struct repository;
30
submodule.c
+100 -38
@@ -21,7 +21,7 @@
21 #include "parse-options.h"
22
23 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;
25 static int initialized_fetch_ref_tips;
26 static struct oid_array ref_tips_before_fetch;
27 static struct oid_array ref_tips_after_fetch;
@@ -674,11 +674,11 @@ const struct submodule *submodule_from_ce(const struct cache_entry *ce)
674 }
675
676 static struct oid_array *submodule_commits(struct string_list *submodules,
677 - const char *path)
677 + const char *name)
678 {
679 struct string_list_item *item;
680
681 - item = string_list_insert(submodules, path);
681 + item = string_list_insert(submodules, name);
682 if (item->util)
683 return (struct oid_array *) item->util;
684
@@ -687,39 +687,67 @@ static struct oid_array *submodule_commits(struct string_list *submodules,
687 return (struct oid_array *) item->util;
688 }
689
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 +
712 static void collect_changed_submodules_cb(struct diff_queue_struct *q,
713 struct diff_options *options,
714 void *data)
715 {
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;
719 int i;
695 - struct string_list *changed = data;
720
721 for (i = 0; i < q->nr; i++) {
722 struct diff_filepair *p = q->queue[i];
723 struct oid_array *commits;
724 + const struct submodule *submodule;
725 + const char *name;
726 +
727 if (!S_ISGITLINK(p->two->mode))
728 continue;
729
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 + }
744 }
745 +
746 + if (!name)
747 + continue;
748 +
749 + commits = submodule_commits(changed, name);
750 + oid_array_append(commits, &p->two->oid);
751 }
752 }
753
@@ -742,11 +770,14 @@ static void collect_changed_submodules(struct string_list *changed,
770
771 while ((commit = get_revision(&rev))) {
772 struct rev_info diff_rev;
773 + struct collect_changed_submodules_cb_data data;
774 + data.changed = changed;
775 + data.commit_oid = &commit->object.oid;
776
777 init_revisions(&diff_rev, NULL);
778 diff_rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
779 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;
781 diff_tree_combined_merge(commit, 1, &diff_rev);
782 }
783
@@ -894,7 +925,7 @@ int find_unpushed_submodules(struct oid_array *commits,
925 const char *remotes_name, struct string_list *needs_pushing)
926 {
927 struct string_list submodules = STRING_LIST_INIT_DUP;
897 - struct string_list_item *submodule;
928 + struct string_list_item *name;
929 struct argv_array argv = ARGV_ARRAY_INIT;
930
931 /* argv.argv[0] will be ignored by setup_revisions */
@@ -905,9 +936,19 @@ int find_unpushed_submodules(struct oid_array *commits,
936
937 collect_changed_submodules(&submodules, &argv);
938
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;
952
953 if (submodule_needs_pushing(path, commits))
954 string_list_insert(needs_pushing, path);
@@ -1065,7 +1106,7 @@ static void calculate_changed_submodule_paths(void)
1106 {
1107 struct argv_array argv = ARGV_ARRAY_INIT;
1108 struct string_list changed_submodules = STRING_LIST_INIT_DUP;
1068 - const struct string_list_item *item;
1109 + const struct string_list_item *name;
1110
1111 /* No need to check if there are no submodules configured */
1112 if (!submodule_from_path(NULL, NULL))
@@ -1080,16 +1121,26 @@ static void calculate_changed_submodule_paths(void)
1121
1122 /*
1123 * 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".
1125 */
1126 collect_changed_submodules(&changed_submodules, &argv);
1127
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;
1141
1142 if (!submodule_has_commits(path, commits))
1092 - string_list_append(&changed_submodule_paths, path);
1143 + string_list_append(&changed_submodule_names, name->string);
1144 }
1145
1146 free_submodules_oids(&changed_submodules);
@@ -1149,11 +1200,19 @@ static int get_next_submodule(struct child_process *cp,
1200 const struct cache_entry *ce = active_cache[spf->count];
1201 const char *git_dir, *default_argv;
1202 const struct submodule *submodule;
1203 + struct submodule default_submodule = SUBMODULE_INIT;
1204
1205 if (!S_ISGITLINK(ce->ce_mode))
1206 continue;
1207
1208 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 + }
1216
1217 default_argv = "yes";
1218 if (spf->command_line_option == RECURSE_SUBMODULES_DEFAULT) {
@@ -1175,7 +1234,8 @@ static int get_next_submodule(struct child_process *cp,
1234 if (fetch_recurse == RECURSE_SUBMODULES_OFF)
1235 continue;
1236 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))
1239 continue;
1240 default_argv = "on-demand";
1241 }
@@ -1183,13 +1243,15 @@ static int get_next_submodule(struct child_process *cp,
1243 if (spf->default_option == RECURSE_SUBMODULES_OFF)
1244 continue;
1245 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))
1248 continue;
1249 default_argv = "on-demand";
1250 }
1251 }
1252 } 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))
1255 continue;
1256 default_argv = "on-demand";
1257 }
@@ -1282,7 +1344,7 @@ int fetch_populated_submodules(const struct argv_array *options,
1344
1345 argv_array_clear(&spf.args);
1346 out:
1285 - string_list_clear(&changed_submodule_paths, 1);
1347 + string_list_clear(&changed_submodule_names, 1);
1348 return spf.result;
1349 }
1350
t/t5526-fetch-submodules.sh
+35
@@ -570,4 +570,39 @@ test_expect_success 'fetching submodule into a broken repository' '
570 test_must_fail git -C dst fetch --recurse-submodules
571 '
572
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 +
608 test_done