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 =>
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
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 &&
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 &&
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.
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).
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
+}