directory rename detection: files/directories in the way of some renames
Reviewed-by: Stefan Beller <sbeller@google.com> Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Elijah Newren committed
Apr 19, 2018 at 10:57 UTC
c449947a79d551be3fbef807c662d701df1c6b4f
1 file changed
+330
t/t6043-merge-rename-directories.sh
+330
@@ -850,4 +850,334 @@ test_expect_success '4a-check: Directory split, with original directory still pr
850
# detection.) But, sadly, see testcase 8b.
851
###########################################################################
852
853
+
854
+###########################################################################
855
+# SECTION 5: Files/directories in the way of subset of to-be-renamed paths
856
+#
857
+# Implicitly renaming files due to a detected directory rename could run
858
+# into problems if there are files or directories in the way of the paths
859
+# we want to rename. Explore such cases in this section.
860
+###########################################################################
861
+
862
+# Testcase 5a, Merge directories, other side adds files to original and target
863
+# Commit O: z/{b,c}, y/d
864
+# Commit A: z/{b,c,e_1,f}, y/{d,e_2}
865
+# Commit B: y/{b,c,d}
866
+# Expected: z/e_1, y/{b,c,d,e_2,f} + CONFLICT warning
867
+# NOTE: While directory rename detection is active here causing z/f to
868
+# become y/f, we did not apply this for z/e_1 because that would
869
+# give us an add/add conflict for y/e_1 vs y/e_2. This problem with
870
+# this add/add, is that both versions of y/e are from the same side
871
+# of history, giving us no way to represent this conflict in the
872
+# index.
873
+
874
+test_expect_success '5a-setup: Merge directories, other side adds files to original and target' '
875
+ test_create_repo 5a &&
876
+ (
877
+ cd 5a &&
878
+
879
+ mkdir z &&
880
+ echo b >z/b &&
881
+ echo c >z/c &&
882
+ mkdir y &&
883
+ echo d >y/d &&
884
+ git add z y &&
885
+ test_tick &&
886
+ git commit -m "O" &&
887
+
888
+ git branch O &&
889
+ git branch A &&
890
+ git branch B &&
891
+
892
+ git checkout A &&
893
+ echo e1 >z/e &&
894
+ echo f >z/f &&
895
+ echo e2 >y/e &&
896
+ git add z/e z/f y/e &&
897
+ test_tick &&
898
+ git commit -m "A" &&
899
+
900
+ git checkout B &&
901
+ git mv z/b y/ &&
902
+ git mv z/c y/ &&
903
+ rmdir z &&
904
+ test_tick &&
905
+ git commit -m "B"
906
+ )
907
+'
908
+
909
+test_expect_failure '5a-check: Merge directories, other side adds files to original and target' '
910
+ (
911
+ cd 5a &&
912
+
913
+ git checkout A^0 &&
914
+
915
+ test_must_fail git merge -s recursive B^0 >out &&
916
+ test_i18ngrep "CONFLICT.*implicit dir rename" out &&
917
+
918
+ git ls-files -s >out &&
919
+ test_line_count = 6 out &&
920
+ git ls-files -u >out &&
921
+ test_line_count = 0 out &&
922
+ git ls-files -o >out &&
923
+ test_line_count = 1 out &&
924
+
925
+ git rev-parse >actual \
926
+ :0:y/b :0:y/c :0:y/d :0:y/e :0:z/e :0:y/f &&
927
+ git rev-parse >expect \
928
+ O:z/b O:z/c O:y/d A:y/e A:z/e A:z/f &&
929
+ test_cmp expect actual
930
+ )
931
+'
932
+
933
+# Testcase 5b, Rename/delete in order to get add/add/add conflict
934
+# (Related to testcase 8d; these may appear slightly inconsistent to users;
935
+# Also related to testcases 7d and 7e)
936
+# Commit O: z/{b,c,d_1}
937
+# Commit A: y/{b,c,d_2}
938
+# Commit B: z/{b,c,d_1,e}, y/d_3
939
+# Expected: y/{b,c,e}, CONFLICT(add/add: y/d_2 vs. y/d_3)
940
+# NOTE: If z/d_1 in commit B were to be involved in dir rename detection, as
941
+# we normaly would since z/ is being renamed to y/, then this would be
942
+# a rename/delete (z/d_1 -> y/d_1 vs. deleted) AND an add/add/add
943
+# conflict of y/d_1 vs. y/d_2 vs. y/d_3. Add/add/add is not
944
+# representable in the index, so the existence of y/d_3 needs to
945
+# cause us to bail on directory rename detection for that path, falling
946
+# back to git behavior without the directory rename detection.
947
+
948
+test_expect_success '5b-setup: Rename/delete in order to get add/add/add conflict' '
949
+ test_create_repo 5b &&
950
+ (
951
+ cd 5b &&
952
+
953
+ mkdir z &&
954
+ echo b >z/b &&
955
+ echo c >z/c &&
956
+ echo d1 >z/d &&
957
+ git add z &&
958
+ test_tick &&
959
+ git commit -m "O" &&
960
+
961
+ git branch O &&
962
+ git branch A &&
963
+ git branch B &&
964
+
965
+ git checkout A &&
966
+ git rm z/d &&
967
+ git mv z y &&
968
+ echo d2 >y/d &&
969
+ git add y/d &&
970
+ test_tick &&
971
+ git commit -m "A" &&
972
+
973
+ git checkout B &&
974
+ mkdir y &&
975
+ echo d3 >y/d &&
976
+ echo e >z/e &&
977
+ git add y/d z/e &&
978
+ test_tick &&
979
+ git commit -m "B"
980
+ )
981
+'
982
+
983
+test_expect_failure '5b-check: Rename/delete in order to get add/add/add conflict' '
984
+ (
985
+ cd 5b &&
986
+
987
+ git checkout A^0 &&
988
+
989
+ test_must_fail git merge -s recursive B^0 >out &&
990
+ test_i18ngrep "CONFLICT (add/add).* y/d" out &&
991
+
992
+ git ls-files -s >out &&
993
+ test_line_count = 5 out &&
994
+ git ls-files -u >out &&
995
+ test_line_count = 2 out &&
996
+ git ls-files -o >out &&
997
+ test_line_count = 1 out &&
998
+
999
+ git rev-parse >actual \
1000
+ :0:y/b :0:y/c :0:y/e :2:y/d :3:y/d &&
1001
+ git rev-parse >expect \
1002
+ O:z/b O:z/c B:z/e A:y/d B:y/d &&
1003
+ test_cmp expect actual &&
1004
+
1005
+ test_must_fail git rev-parse :1:y/d &&
1006
+ test_path_is_file y/d
1007
+ )
1008
+'
1009
+
1010
+# Testcase 5c, Transitive rename would cause rename/rename/rename/add/add/add
1011
+# (Directory rename detection would result in transitive rename vs.
1012
+# rename/rename(1to2) and turn it into a rename/rename(1to3). Further,
1013
+# rename paths conflict with separate adds on the other side)
1014
+# (Related to testcases 3b and 7c)
1015
+# Commit O: z/{b,c}, x/d_1
1016
+# Commit A: y/{b,c,d_2}, w/d_1
1017
+# Commit B: z/{b,c,d_1,e}, w/d_3, y/d_4
1018
+# Expected: A mess, but only a rename/rename(1to2)/add/add mess. Use the
1019
+# presence of y/d_4 in B to avoid doing transitive rename of
1020
+# x/d_1 -> z/d_1 -> y/d_1, so that the only paths we have at
1021
+# y/d are y/d_2 and y/d_4. We still do the move from z/e to y/e,
1022
+# though, because it doesn't have anything in the way.
1023
+
1024
+test_expect_success '5c-setup: Transitive rename would cause rename/rename/rename/add/add/add' '
1025
+ test_create_repo 5c &&
1026
+ (
1027
+ cd 5c &&
1028
+
1029
+ mkdir z &&
1030
+ echo b >z/b &&
1031
+ echo c >z/c &&
1032
+ mkdir x &&
1033
+ echo d1 >x/d &&
1034
+ git add z x &&
1035
+ test_tick &&
1036
+ git commit -m "O" &&
1037
+
1038
+ git branch O &&
1039
+ git branch A &&
1040
+ git branch B &&
1041
+
1042
+ git checkout A &&
1043
+ git mv z y &&
1044
+ echo d2 >y/d &&
1045
+ git add y/d &&
1046
+ git mv x w &&
1047
+ test_tick &&
1048
+ git commit -m "A" &&
1049
+
1050
+ git checkout B &&
1051
+ git mv x/d z/ &&
1052
+ mkdir w &&
1053
+ mkdir y &&
1054
+ echo d3 >w/d &&
1055
+ echo d4 >y/d &&
1056
+ echo e >z/e &&
1057
+ git add w/ y/ z/e &&
1058
+ test_tick &&
1059
+ git commit -m "B"
1060
+ )
1061
+'
1062
+
1063
+test_expect_failure '5c-check: Transitive rename would cause rename/rename/rename/add/add/add' '
1064
+ (
1065
+ cd 5c &&
1066
+
1067
+ git checkout A^0 &&
1068
+
1069
+ test_must_fail git merge -s recursive B^0 >out &&
1070
+ test_i18ngrep "CONFLICT (rename/rename).*x/d.*w/d.*z/d" out &&
1071
+ test_i18ngrep "CONFLICT (add/add).* y/d" out &&
1072
+
1073
+ git ls-files -s >out &&
1074
+ test_line_count = 9 out &&
1075
+ git ls-files -u >out &&
1076
+ test_line_count = 6 out &&
1077
+ git ls-files -o >out &&
1078
+ test_line_count = 3 out &&
1079
+
1080
+ git rev-parse >actual \
1081
+ :0:y/b :0:y/c :0:y/e &&
1082
+ git rev-parse >expect \
1083
+ O:z/b O:z/c B:z/e &&
1084
+ test_cmp expect actual &&
1085
+
1086
+ test_must_fail git rev-parse :1:y/d &&
1087
+ git rev-parse >actual \
1088
+ :2:w/d :3:w/d :1:x/d :2:y/d :3:y/d :3:z/d &&
1089
+ git rev-parse >expect \
1090
+ O:x/d B:w/d O:x/d A:y/d B:y/d O:x/d &&
1091
+ test_cmp expect actual &&
1092
+
1093
+ git hash-object >actual \
1094
+ w/d~HEAD w/d~B^0 z/d &&
1095
+ git rev-parse >expect \
1096
+ O:x/d B:w/d O:x/d &&
1097
+ test_cmp expect actual &&
1098
+ test_path_is_missing x/d &&
1099
+ test_path_is_file y/d &&
1100
+ grep -q "<<<<" y/d # conflict markers should be present
1101
+ )
1102
+'
1103
+
1104
+# Testcase 5d, Directory/file/file conflict due to directory rename
1105
+# Commit O: z/{b,c}
1106
+# Commit A: y/{b,c,d_1}
1107
+# Commit B: z/{b,c,d_2,f}, y/d/e
1108
+# Expected: y/{b,c,d/e,f}, z/d_2, CONFLICT(file/directory), y/d_1~HEAD
1109
+# Note: The fact that y/d/ exists in B makes us bail on directory rename
1110
+# detection for z/d_2, but that doesn't prevent us from applying the
1111
+# directory rename detection for z/f -> y/f.
1112
+
1113
+test_expect_success '5d-setup: Directory/file/file conflict due to directory rename' '
1114
+ test_create_repo 5d &&
1115
+ (
1116
+ cd 5d &&
1117
+
1118
+ mkdir z &&
1119
+ echo b >z/b &&
1120
+ echo c >z/c &&
1121
+ git add z &&
1122
+ test_tick &&
1123
+ git commit -m "O" &&
1124
+
1125
+ git branch O &&
1126
+ git branch A &&
1127
+ git branch B &&
1128
+
1129
+ git checkout A &&
1130
+ git mv z y &&
1131
+ echo d1 >y/d &&
1132
+ git add y/d &&
1133
+ test_tick &&
1134
+ git commit -m "A" &&
1135
+
1136
+ git checkout B &&
1137
+ mkdir -p y/d &&
1138
+ echo e >y/d/e &&
1139
+ echo d2 >z/d &&
1140
+ echo f >z/f &&
1141
+ git add y/d/e z/d z/f &&
1142
+ test_tick &&
1143
+ git commit -m "B"
1144
+ )
1145
+'
1146
+
1147
+test_expect_failure '5d-check: Directory/file/file conflict due to directory rename' '
1148
+ (
1149
+ cd 5d &&
1150
+
1151
+ git checkout A^0 &&
1152
+
1153
+ test_must_fail git merge -s recursive B^0 >out &&
1154
+ test_i18ngrep "CONFLICT (file/directory).*y/d" out &&
1155
+
1156
+ git ls-files -s >out &&
1157
+ test_line_count = 6 out &&
1158
+ git ls-files -u >out &&
1159
+ test_line_count = 1 out &&
1160
+ git ls-files -o >out &&
1161
+ test_line_count = 2 out &&
1162
+
1163
+ git rev-parse >actual \
1164
+ :0:y/b :0:y/c :0:z/d :0:y/f :2:y/d :0:y/d/e &&
1165
+ git rev-parse >expect \
1166
+ O:z/b O:z/c B:z/d B:z/f A:y/d B:y/d/e &&
1167
+ test_cmp expect actual &&
1168
+
1169
+ git hash-object y/d~HEAD >actual &&
1170
+ git rev-parse A:y/d >expect &&
1171
+ test_cmp expect actual
1172
+ )
1173
+'
1174
+
1175
+###########################################################################
1176
+# Rules suggested by section 5:
1177
+#
1178
+# If a subset of to-be-renamed files have a file or directory in the way,
1179
+# "turn off" the directory rename for those specific sub-paths, falling
1180
+# back to old handling. But, sadly, see testcases 8a and 8b.
1181
+###########################################################################
1182
+
1183
test_done