lib-submodule-update.sh: define tests for recursing into submodules

Currently lib-submodule-update.sh provides 2 functions test_submodule_switch and test_submodule_forced_switch that are used by a variety of tests to ensure that submodules behave as expected. The current expected behavior is that submodules are not touched at all (see 42639d2317a for the exact setup). In the future we want to teach all these commands to recurse into submodules. To do that, we'll add two testing functions to submodule-update-lib.sh: test_submodule_switch_recursing and test_submodule_forced_switch_recursing. These two functions behave in analogy to the already existing functions just with a different expectation on submodule behavior. The submodule in the working tree is expected to be updated to the recorded submodule version. The behavior is analogous to e.g. the behavior of files in a nested directory in the working tree, where a change to the working tree handles any arising directory/file conflicts just fine. Signed-off-by: Stefan Beller <sbeller@google.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Stefan Beller committed Mar 14, 2017 at 14:46 UTC 259f3ee29666eddf65fb0fe0e3fd14aa22f33cba
1 file changed +504 -2
t/lib-submodule-update.sh
+504 -2
@@ -4,6 +4,7 @@
4 # - New submodule (no_submodule => add_sub1)
5 # - Removed submodule (add_sub1 => remove_sub1)
6 # - Updated submodule (add_sub1 => modify_sub1)
7 +# - Updated submodule recursively (add_nested_sub => modify_sub1_recursively)
8 # - Submodule updated to invalid commit (add_sub1 => invalid_sub1)
9 # - Submodule updated from invalid commit (invalid_sub1 => valid_sub1)
10 # - Submodule replaced by tracked files in directory (add_sub1 =>
@@ -19,8 +20,8 @@
20 # / ^
21 # / remove_sub1
22 # /
22 -# add_sub1 /-------O
23 -# | / ^
23 +# add_sub1 /-------O---------O--------O modify_sub1_recursively
24 +# | / ^ add_nested_sub
25 # | / modify_sub1
26 # v/
27 # O------O-----------O---------O
@@ -48,6 +49,17 @@ create_lib_submodule_repo () {
49 git commit -m "Base inside first submodule" &&
50 git branch "no_submodule"
51 ) &&
52 + git init submodule_update_sub2 &&
53 + (
54 + cd submodule_update_sub2
55 + echo "expect" >>.gitignore &&
56 + echo "actual" >>.gitignore &&
57 + echo "x" >file1 &&
58 + echo "y" >file2 &&
59 + git add .gitignore file1 file2 &&
60 + git commit -m "nested submodule base" &&
61 + git branch "no_submodule"
62 + ) &&
63 git init submodule_update_repo &&
64 (
65 cd submodule_update_repo &&
@@ -84,6 +96,26 @@ create_lib_submodule_repo () {
96 git add sub1 &&
97 git commit -m "Modify sub1" &&
98
99 + git checkout -b add_nested_sub modify_sub1 &&
100 + git -C sub1 checkout -b "add_nested_sub" &&
101 + git -C sub1 submodule add --branch no_submodule ../submodule_update_sub2 sub2 &&
102 + git -C sub1 commit -a -m "add a nested submodule" &&
103 + git add sub1 &&
104 + git commit -a -m "update submodule, that updates a nested submodule" &&
105 + git checkout -b modify_sub1_recursively &&
106 + git -C sub1 checkout -b modify_sub1_recursively &&
107 + git -C sub1/sub2 checkout -b modify_sub1_recursively &&
108 + echo change >sub1/sub2/file3 &&
109 + git -C sub1/sub2 add file3 &&
110 + git -C sub1/sub2 commit -m "make a change in nested sub" &&
111 + git -C sub1 add sub2 &&
112 + git -C sub1 commit -m "update nested sub" &&
113 + git add sub1 &&
114 + git commit -m "update sub1, that updates nested sub" &&
115 + git -C sub1 push origin modify_sub1_recursively &&
116 + git -C sub1/sub2 push origin modify_sub1_recursively &&
117 + git -C sub1 submodule deinit -f --all &&
118 +
119 git checkout -b replace_sub1_with_directory add_sub1 &&
120 git submodule update &&
121 git -C sub1 checkout modifications &&
@@ -150,6 +182,15 @@ test_git_directory_is_unchanged () {
182 )
183 }
184
185 +test_git_directory_exists() {
186 + test -e ".git/modules/$1" &&
187 + if test -f sub1/.git
188 + then
189 + # does core.worktree point at the right place?
190 + test "$(git -C .git/modules/$1 config core.worktree)" = "../../../$1"
191 + fi
192 +}
193 +
194 # Helper function to be executed at the start of every test below, it sets up
195 # the submodule repo if it doesn't exist and configures the most problematic
196 # settings for diff.ignoreSubmodules.
@@ -180,6 +221,27 @@ reset_work_tree_to () {
221 )
222 }
223
224 +reset_work_tree_to_interested () {
225 + reset_work_tree_to $1 &&
226 + # make the submodule git dirs available
227 + if ! test -d submodule_update/.git/modules/sub1
228 + then
229 + mkdir -p submodule_update/.git/modules &&
230 + cp -r submodule_update_repo/.git/modules/sub1 submodule_update/.git/modules/sub1
231 + GIT_WORK_TREE=. git -C submodule_update/.git/modules/sub1 config --unset core.worktree
232 + fi &&
233 + if ! test -d submodule_update/.git/modules/sub1/modules/sub2
234 + then
235 + mkdir -p submodule_update/.git/modules/sub1/modules &&
236 + cp -r submodule_update_repo/.git/modules/sub1/modules/sub2 submodule_update/.git/modules/sub1/modules/sub2
237 + GIT_WORK_TREE=. git -C submodule_update/.git/modules/sub1/modules/sub2 config --unset core.worktree
238 + fi &&
239 + # indicate we are interested in the submodule:
240 + git -C submodule_update config submodule.sub1.url "bogus" &&
241 + # sub1 might not be checked out, so use the git dir
242 + git -C submodule_update/.git/modules/sub1 config submodule.sub2.url "bogus"
243 +}
244 +
245 # Test that the superproject contains the content according to commit "$1"
246 # (the work tree must match the index for everything but submodules but the
247 # index must exactly match the given commit including any submodule SHA-1s).
@@ -700,3 +762,443 @@ test_submodule_forced_switch () {
762 )
763 '
764 }
765 +
766 +# Test that submodule contents are correctly updated when switching
767 +# between commits that change a submodule.
768 +# Test that the following transitions are correctly handled:
769 +# (These tests are also above in the case where we expect no change
770 +# in the submodule)
771 +# - Updated submodule
772 +# - New submodule
773 +# - Removed submodule
774 +# - Directory containing tracked files replaced by submodule
775 +# - Submodule replaced by tracked files in directory
776 +# - Submodule replaced by tracked file with the same name
777 +# - tracked file replaced by submodule
778 +#
779 +# New test cases
780 +# - Removing a submodule with a git directory absorbs the submodules
781 +# git directory first into the superproject.
782 +
783 +test_submodule_switch_recursing () {
784 + command="$1"
785 + ######################### Appearing submodule #########################
786 + # Switching to a commit letting a submodule appear checks it out ...
787 + test_expect_success "$command: added submodule is checked out" '
788 + prolog &&
789 + reset_work_tree_to_interested no_submodule &&
790 + (
791 + cd submodule_update &&
792 + git branch -t add_sub1 origin/add_sub1 &&
793 + $command add_sub1 &&
794 + test_superproject_content origin/add_sub1 &&
795 + test_submodule_content sub1 origin/add_sub1
796 + )
797 + '
798 + # ... ignoring an empty existing directory ...
799 + test_expect_success "$command: added submodule is checked out in empty dir" '
800 + prolog &&
801 + reset_work_tree_to_interested no_submodule &&
802 + (
803 + cd submodule_update &&
804 + mkdir sub1 &&
805 + git branch -t add_sub1 origin/add_sub1 &&
806 + $command add_sub1 &&
807 + test_superproject_content origin/add_sub1 &&
808 + test_submodule_content sub1 origin/add_sub1
809 + )
810 + '
811 + # ... unless there is an untracked file in its place.
812 + test_expect_success "$command: added submodule doesn't remove untracked file with same name" '
813 + prolog &&
814 + reset_work_tree_to_interested no_submodule &&
815 + (
816 + cd submodule_update &&
817 + git branch -t add_sub1 origin/add_sub1 &&
818 + : >sub1 &&
819 + test_must_fail $command add_sub1 &&
820 + test_superproject_content origin/no_submodule &&
821 + test_must_be_empty sub1
822 + )
823 + '
824 + # ... but an ignored file is fine.
825 + test_expect_success "$command: added submodule removes an untracked ignored file" '
826 + test_when_finished "rm submodule_update/.git/info/exclude" &&
827 + prolog &&
828 + reset_work_tree_to_interested no_submodule &&
829 + (
830 + cd submodule_update &&
831 + git branch -t add_sub1 origin/add_sub1 &&
832 + : >sub1 &&
833 + echo sub1 >.git/info/exclude
834 + $command add_sub1 &&
835 + test_superproject_content origin/add_sub1 &&
836 + test_submodule_content sub1 origin/add_sub1
837 + )
838 + '
839 + # Replacing a tracked file with a submodule produces a checked out submodule
840 + test_expect_success "$command: replace tracked file with submodule checks out submodule" '
841 + prolog &&
842 + reset_work_tree_to_interested replace_sub1_with_file &&
843 + (
844 + cd submodule_update &&
845 + git branch -t replace_file_with_sub1 origin/replace_file_with_sub1 &&
846 + $command replace_file_with_sub1 &&
847 + test_superproject_content origin/replace_file_with_sub1 &&
848 + test_submodule_content sub1 origin/replace_file_with_sub1
849 + )
850 + '
851 + # ... as does removing a directory with tracked files with a submodule.
852 + test_expect_success "$command: replace directory with submodule" '
853 + prolog &&
854 + reset_work_tree_to_interested replace_sub1_with_directory &&
855 + (
856 + cd submodule_update &&
857 + git branch -t replace_directory_with_sub1 origin/replace_directory_with_sub1 &&
858 + $command replace_directory_with_sub1 &&
859 + test_superproject_content origin/replace_directory_with_sub1 &&
860 + test_submodule_content sub1 origin/replace_directory_with_sub1
861 + )
862 + '
863 +
864 + ######################## Disappearing submodule #######################
865 + # Removing a submodule removes its work tree ...
866 + test_expect_success "$command: removed submodule removes submodules working tree" '
867 + prolog &&
868 + reset_work_tree_to_interested add_sub1 &&
869 + (
870 + cd submodule_update &&
871 + git branch -t remove_sub1 origin/remove_sub1 &&
872 + $command remove_sub1 &&
873 + test_superproject_content origin/remove_sub1 &&
874 + ! test -e sub1
875 + )
876 + '
877 + # ... absorbing a .git directory along the way.
878 + test_expect_success "$command: removed submodule absorbs submodules .git directory" '
879 + prolog &&
880 + reset_work_tree_to_interested add_sub1 &&
881 + (
882 + cd submodule_update &&
883 + git branch -t remove_sub1 origin/remove_sub1 &&
884 + replace_gitfile_with_git_dir sub1 &&
885 + rm -rf .git/modules &&
886 + $command remove_sub1 &&
887 + test_superproject_content origin/remove_sub1 &&
888 + ! test -e sub1 &&
889 + test_git_directory_exists sub1
890 + )
891 + '
892 + # Replacing a submodule with files in a directory must succeeds
893 + # when the submodule is clean
894 + test_expect_success "$command: replace submodule with a directory" '
895 + prolog &&
896 + reset_work_tree_to_interested add_sub1 &&
897 + (
898 + cd submodule_update &&
899 + git branch -t replace_sub1_with_directory origin/replace_sub1_with_directory &&
900 + $command replace_sub1_with_directory &&
901 + test_superproject_content origin/replace_sub1_with_directory &&
902 + test_submodule_content sub1 origin/replace_sub1_with_directory
903 + )
904 + '
905 + # ... absorbing a .git directory.
906 + test_expect_success "$command: replace submodule containing a .git directory with a directory must absorb the git dir" '
907 + prolog &&
908 + reset_work_tree_to_interested add_sub1 &&
909 + (
910 + cd submodule_update &&
911 + git branch -t replace_sub1_with_directory origin/replace_sub1_with_directory &&
912 + replace_gitfile_with_git_dir sub1 &&
913 + rm -rf .git/modules &&
914 + $command replace_sub1_with_directory &&
915 + test_superproject_content origin/replace_sub1_with_directory &&
916 + test_git_directory_exists sub1
917 + )
918 + '
919 +
920 + # Replacing it with a file ...
921 + test_expect_success "$command: replace submodule with a file" '
922 + prolog &&
923 + reset_work_tree_to_interested add_sub1 &&
924 + (
925 + cd submodule_update &&
926 + git branch -t replace_sub1_with_file origin/replace_sub1_with_file &&
927 + $command replace_sub1_with_file &&
928 + test_superproject_content origin/replace_sub1_with_file &&
929 + test -f sub1
930 + )
931 + '
932 +
933 + # ... must check its local work tree for untracked files
934 + test_expect_success "$command: replace submodule with a file must fail with untracked files" '
935 + prolog &&
936 + reset_work_tree_to_interested add_sub1 &&
937 + (
938 + cd submodule_update &&
939 + git branch -t replace_sub1_with_file origin/replace_sub1_with_file &&
940 + : >sub1/untrackedfile &&
941 + test_must_fail $command replace_sub1_with_file &&
942 + test_superproject_content origin/add_sub1 &&
943 + test_submodule_content sub1 origin/add_sub1
944 + )
945 + '
946 +
947 + # ... and ignored files are ignored
948 + test_expect_success "$command: replace submodule with a file works ignores ignored files in submodule" '
949 + test_when_finished "rm submodule_update/.git/modules/sub1/info/exclude" &&
950 + prolog &&
951 + reset_work_tree_to_interested add_sub1 &&
952 + (
953 + cd submodule_update &&
954 + git branch -t replace_sub1_with_file origin/replace_sub1_with_file &&
955 + : >sub1/ignored &&
956 + $command replace_sub1_with_file &&
957 + test_superproject_content origin/replace_sub1_with_file &&
958 + test -f sub1
959 + )
960 + '
961 +
962 + ########################## Modified submodule #########################
963 + # Updating a submodule sha1 updates the submodule's work tree
964 + test_expect_success "$command: modified submodule updates submodule work tree" '
965 + prolog &&
966 + reset_work_tree_to_interested add_sub1 &&
967 + (
968 + cd submodule_update &&
969 + git branch -t modify_sub1 origin/modify_sub1 &&
970 + $command modify_sub1 &&
971 + test_superproject_content origin/modify_sub1 &&
972 + test_submodule_content sub1 origin/modify_sub1
973 + )
974 + '
975 +
976 + # Updating a submodule to an invalid sha1 doesn't update the
977 + # superproject nor the submodule's work tree.
978 + test_expect_success "$command: updating to a missing submodule commit fails" '
979 + prolog &&
980 + reset_work_tree_to_interested add_sub1 &&
981 + (
982 + cd submodule_update &&
983 + git branch -t invalid_sub1 origin/invalid_sub1 &&
984 + test_must_fail $command invalid_sub1 &&
985 + test_superproject_content origin/add_sub1 &&
986 + test_submodule_content sub1 origin/add_sub1
987 + )
988 + '
989 +
990 + test_expect_success "$command: modified submodule updates submodule recursively" '
991 + prolog &&
992 + reset_work_tree_to_interested add_nested_sub &&
993 + (
994 + cd submodule_update &&
995 + git branch -t modify_sub1_recursively origin/modify_sub1_recursively &&
996 + $command modify_sub1_recursively &&
997 + test_superproject_content origin/modify_sub1_recursively &&
998 + test_submodule_content sub1 origin/modify_sub1_recursively &&
999 + test_submodule_content -C sub1 sub2 origin/modify_sub1_recursively
1000 + )
1001 + '
1002 +}
1003 +
1004 +# Test that submodule contents are updated when switching between commits
1005 +# that change a submodule, but throwing away local changes in
1006 +# the superproject as well as the submodule is allowed.
1007 +test_submodule_forced_switch_recursing () {
1008 + command="$1"
1009 + ######################### Appearing submodule #########################
1010 + # Switching to a commit letting a submodule appear creates empty dir ...
1011 + test_expect_success "$command: added submodule is checked out" '
1012 + prolog &&
1013 + reset_work_tree_to_interested no_submodule &&
1014 + (
1015 + cd submodule_update &&
1016 + git branch -t add_sub1 origin/add_sub1 &&
1017 + $command add_sub1 &&
1018 + test_superproject_content origin/add_sub1 &&
1019 + test_submodule_content sub1 origin/add_sub1
1020 + )
1021 + '
1022 + # ... and doesn't care if it already exists ...
1023 + test_expect_success "$command: added submodule ignores empty directory" '
1024 + prolog &&
1025 + reset_work_tree_to_interested no_submodule &&
1026 + (
1027 + cd submodule_update &&
1028 + git branch -t add_sub1 origin/add_sub1 &&
1029 + mkdir sub1 &&
1030 + $command add_sub1 &&
1031 + test_superproject_content origin/add_sub1 &&
1032 + test_submodule_content sub1 origin/add_sub1
1033 + )
1034 + '
1035 + # ... not caring about an untracked file either
1036 + test_expect_success "$command: added submodule does remove untracked unignored file with same name when forced" '
1037 + prolog &&
1038 + reset_work_tree_to_interested no_submodule &&
1039 + (
1040 + cd submodule_update &&
1041 + git branch -t add_sub1 origin/add_sub1 &&
1042 + >sub1 &&
1043 + $command add_sub1 &&
1044 + test_superproject_content origin/add_sub1 &&
1045 + test_submodule_content sub1 origin/add_sub1
1046 + )
1047 + '
1048 + # Replacing a tracked file with a submodule checks out the submodule
1049 + test_expect_success "$command: replace tracked file with submodule populates the submodule" '
1050 + prolog &&
1051 + reset_work_tree_to_interested replace_sub1_with_file &&
1052 + (
1053 + cd submodule_update &&
1054 + git branch -t replace_file_with_sub1 origin/replace_file_with_sub1 &&
1055 + $command replace_file_with_sub1 &&
1056 + test_superproject_content origin/replace_file_with_sub1 &&
1057 + test_submodule_content sub1 origin/replace_file_with_sub1
1058 + )
1059 + '
1060 + # ... as does removing a directory with tracked files with a
1061 + # submodule.
1062 + test_expect_success "$command: replace directory with submodule" '
1063 + prolog &&
1064 + reset_work_tree_to_interested replace_sub1_with_directory &&
1065 + (
1066 + cd submodule_update &&
1067 + git branch -t replace_directory_with_sub1 origin/replace_directory_with_sub1 &&
1068 + $command replace_directory_with_sub1 &&
1069 + test_superproject_content origin/replace_directory_with_sub1 &&
1070 + test_submodule_content sub1 origin/replace_directory_with_sub1
1071 + )
1072 + '
1073 +
1074 + ######################## Disappearing submodule #######################
1075 + # Removing a submodule doesn't remove its work tree ...
1076 + test_expect_success "$command: removed submodule leaves submodule directory and its contents in place" '
1077 + prolog &&
1078 + reset_work_tree_to_interested add_sub1 &&
1079 + (
1080 + cd submodule_update &&
1081 + git branch -t remove_sub1 origin/remove_sub1 &&
1082 + $command remove_sub1 &&
1083 + test_superproject_content origin/remove_sub1 &&
1084 + ! test -e sub1
1085 + )
1086 + '
1087 + # ... especially when it contains a .git directory.
1088 + test_expect_success "$command: removed submodule leaves submodule containing a .git directory alone" '
1089 + prolog &&
1090 + reset_work_tree_to_interested add_sub1 &&
1091 + (
1092 + cd submodule_update &&
1093 + git branch -t remove_sub1 origin/remove_sub1 &&
1094 + replace_gitfile_with_git_dir sub1 &&
1095 + rm -rf .git/modules/sub1 &&
1096 + $command remove_sub1 &&
1097 + test_superproject_content origin/remove_sub1 &&
1098 + test_git_directory_exists sub1 &&
1099 + ! test -e sub1
1100 + )
1101 + '
1102 + # Replacing a submodule with files in a directory ...
1103 + test_expect_success "$command: replace submodule with a directory" '
1104 + prolog &&
1105 + reset_work_tree_to_interested add_sub1 &&
1106 + (
1107 + cd submodule_update &&
1108 + git branch -t replace_sub1_with_directory origin/replace_sub1_with_directory &&
1109 + $command replace_sub1_with_directory &&
1110 + test_superproject_content origin/replace_sub1_with_directory
1111 + )
1112 + '
1113 + # ... absorbing a .git directory.
1114 + test_expect_success "$command: replace submodule containing a .git directory with a directory must fail" '
1115 + prolog &&
1116 + reset_work_tree_to_interested add_sub1 &&
1117 + (
1118 + cd submodule_update &&
1119 + git branch -t replace_sub1_with_directory origin/replace_sub1_with_directory &&
1120 + replace_gitfile_with_git_dir sub1 &&
1121 + rm -rf .git/modules/sub1 &&
1122 + $command replace_sub1_with_directory &&
1123 + test_superproject_content origin/replace_sub1_with_directory &&
1124 + test_submodule_content sub1 origin/modify_sub1
1125 + test_git_directory_exists sub1
1126 + )
1127 + '
1128 + # Replacing it with a file
1129 + test_expect_success "$command: replace submodule with a file" '
1130 + prolog &&
1131 + reset_work_tree_to_interested add_sub1 &&
1132 + (
1133 + cd submodule_update &&
1134 + git branch -t replace_sub1_with_file origin/replace_sub1_with_file &&
1135 + $command replace_sub1_with_file &&
1136 + test_superproject_content origin/replace_sub1_with_file
1137 + )
1138 + '
1139 +
1140 + # ... even if the submodule contains ignored files
1141 + test_expect_success "$command: replace submodule with a file ignoring ignored files" '
1142 + prolog &&
1143 + reset_work_tree_to_interested add_sub1 &&
1144 + (
1145 + cd submodule_update &&
1146 + git branch -t replace_sub1_with_file origin/replace_sub1_with_file &&
1147 + : >sub1/expect &&
1148 + $command replace_sub1_with_file &&
1149 + test_superproject_content origin/replace_sub1_with_file
1150 + )
1151 + '
1152 +
1153 + # ... but stops for untracked files that would be lost
1154 + test_expect_success "$command: replace submodule with a file" '
1155 + prolog &&
1156 + reset_work_tree_to_interested add_sub1 &&
1157 + (
1158 + cd submodule_update &&
1159 + git branch -t replace_sub1_with_file origin/replace_sub1_with_file &&
1160 + : >sub1/untracked_file &&
1161 + test_must_fail $command replace_sub1_with_file &&
1162 + test_superproject_content origin/add_sub1 &&
1163 + test -f sub1/untracked_file
1164 + )
1165 + '
1166 +
1167 + ########################## Modified submodule #########################
1168 + # Updating a submodule sha1 updates the submodule's work tree
1169 + test_expect_success "$command: modified submodule updates submodule work tree" '
1170 + prolog &&
1171 + reset_work_tree_to_interested add_sub1 &&
1172 + (
1173 + cd submodule_update &&
1174 + git branch -t modify_sub1 origin/modify_sub1 &&
1175 + $command modify_sub1 &&
1176 + test_superproject_content origin/modify_sub1 &&
1177 + test_submodule_content sub1 origin/modify_sub1
1178 + )
1179 + '
1180 + # Updating a submodule to an invalid sha1 doesn't update the
1181 + # submodule's work tree, subsequent update will fail
1182 + test_expect_success "$command: modified submodule does not update submodule work tree to invalid commit" '
1183 + prolog &&
1184 + reset_work_tree_to_interested add_sub1 &&
1185 + (
1186 + cd submodule_update &&
1187 + git branch -t invalid_sub1 origin/invalid_sub1 &&
1188 + test_must_fail $command invalid_sub1 &&
1189 + test_superproject_content origin/add_sub1 &&
1190 + test_submodule_content sub1 origin/add_sub1
1191 + )
1192 + '
1193 + # Updating a submodule from an invalid sha1 updates
1194 + test_expect_success "$command: modified submodule does not update submodule work tree from invalid commit" '
1195 + prolog &&
1196 + reset_work_tree_to_interested invalid_sub1 &&
1197 + (
1198 + cd submodule_update &&
1199 + git branch -t valid_sub1 origin/valid_sub1 &&
1200 + test_must_fail $command valid_sub1 &&
1201 + test_superproject_content origin/invalid_sub1
1202 + )
1203 + '
1204 +}