]>
Commit | Line | Data |
---|---|---|
c94736a2 JH |
1 | #!/bin/sh |
2 | ||
695576fd | 3 | test_description='recursive merge corner cases involving criss-cross merges' |
c94736a2 JH |
4 | |
5 | . ./test-lib.sh | |
6 | ||
827f2b7d EN |
7 | get_clean_checkout () { |
8 | git reset --hard && | |
9 | git clean -fdqx && | |
10 | git checkout "$1" | |
11 | } | |
12 | ||
c94736a2 JH |
13 | # |
14 | # L1 L2 | |
15 | # o---o | |
16 | # / \ / \ | |
17 | # o X ? | |
18 | # \ / \ / | |
19 | # o---o | |
20 | # R1 R2 | |
21 | # | |
22 | ||
c976260d | 23 | test_expect_success 'setup basic criss-cross + rename with no modifications' ' |
a48fcd83 | 24 | ten="0 1 2 3 4 5 6 7 8 9" && |
c94736a2 JH |
25 | for i in $ten |
26 | do | |
27 | echo line $i in a sample file | |
28 | done >one && | |
29 | for i in $ten | |
30 | do | |
31 | echo line $i in another sample file | |
32 | done >two && | |
33 | git add one two && | |
34 | test_tick && git commit -m initial && | |
35 | ||
36 | git branch L1 && | |
37 | git checkout -b R1 && | |
38 | git mv one three && | |
39 | test_tick && git commit -m R1 && | |
40 | ||
41 | git checkout L1 && | |
42 | git mv two three && | |
43 | test_tick && git commit -m L1 && | |
44 | ||
45 | git checkout L1^0 && | |
46 | test_tick && git merge -s ours R1 && | |
47 | git tag L2 && | |
48 | ||
49 | git checkout R1^0 && | |
50 | test_tick && git merge -s ours L1 && | |
51 | git tag R2 | |
52 | ' | |
53 | ||
c976260d | 54 | test_expect_success 'merge simple rename+criss-cross with no modifications' ' |
c94736a2 JH |
55 | git reset --hard && |
56 | git checkout L2^0 && | |
57 | ||
c976260d EN |
58 | test_must_fail git merge -s recursive R2^0 && |
59 | ||
434b8525 EN |
60 | test 2 = $(git ls-files -s | wc -l) && |
61 | test 2 = $(git ls-files -u | wc -l) && | |
62 | test 2 = $(git ls-files -o | wc -l) && | |
c976260d | 63 | |
c976260d EN |
64 | test $(git rev-parse :2:three) = $(git rev-parse L2:three) && |
65 | test $(git rev-parse :3:three) = $(git rev-parse R2:three) && | |
66 | ||
434b8525 EN |
67 | test $(git rev-parse L2:three) = $(git hash-object three~HEAD) && |
68 | test $(git rev-parse R2:three) = $(git hash-object three~R2^0) | |
c94736a2 JH |
69 | ' |
70 | ||
583942df EN |
71 | # |
72 | # Same as before, but modify L1 slightly: | |
73 | # | |
74 | # L1m L2 | |
75 | # o---o | |
76 | # / \ / \ | |
77 | # o X ? | |
78 | # \ / \ / | |
79 | # o---o | |
80 | # R1 R2 | |
81 | # | |
82 | ||
83 | test_expect_success 'setup criss-cross + rename merges with basic modification' ' | |
84 | git rm -rf . && | |
85 | git clean -fdqx && | |
86 | rm -rf .git && | |
87 | git init && | |
88 | ||
89 | ten="0 1 2 3 4 5 6 7 8 9" | |
90 | for i in $ten | |
91 | do | |
92 | echo line $i in a sample file | |
93 | done >one && | |
94 | for i in $ten | |
95 | do | |
96 | echo line $i in another sample file | |
97 | done >two && | |
98 | git add one two && | |
99 | test_tick && git commit -m initial && | |
100 | ||
101 | git branch L1 && | |
102 | git checkout -b R1 && | |
103 | git mv one three && | |
104 | echo more >>two && | |
105 | git add two && | |
106 | test_tick && git commit -m R1 && | |
107 | ||
108 | git checkout L1 && | |
109 | git mv two three && | |
110 | test_tick && git commit -m L1 && | |
111 | ||
112 | git checkout L1^0 && | |
113 | test_tick && git merge -s ours R1 && | |
114 | git tag L2 && | |
115 | ||
116 | git checkout R1^0 && | |
117 | test_tick && git merge -s ours L1 && | |
118 | git tag R2 | |
119 | ' | |
120 | ||
2a669c34 | 121 | test_expect_success 'merge criss-cross + rename merges with basic modification' ' |
583942df EN |
122 | git reset --hard && |
123 | git checkout L2^0 && | |
124 | ||
125 | test_must_fail git merge -s recursive R2^0 && | |
126 | ||
434b8525 EN |
127 | test 2 = $(git ls-files -s | wc -l) && |
128 | test 2 = $(git ls-files -u | wc -l) && | |
129 | test 2 = $(git ls-files -o | wc -l) && | |
583942df | 130 | |
583942df EN |
131 | test $(git rev-parse :2:three) = $(git rev-parse L2:three) && |
132 | test $(git rev-parse :3:three) = $(git rev-parse R2:three) && | |
133 | ||
434b8525 EN |
134 | test $(git rev-parse L2:three) = $(git hash-object three~HEAD) && |
135 | test $(git rev-parse R2:three) = $(git hash-object three~R2^0) | |
583942df EN |
136 | ' |
137 | ||
f63622c0 EN |
138 | # |
139 | # For the next test, we start with three commits in two lines of development | |
140 | # which setup a rename/add conflict: | |
141 | # Commit A: File 'a' exists | |
142 | # Commit B: Rename 'a' -> 'new_a' | |
143 | # Commit C: Modify 'a', create different 'new_a' | |
144 | # Later, two different people merge and resolve differently: | |
145 | # Commit D: Merge B & C, ignoring separately created 'new_a' | |
146 | # Commit E: Merge B & C making use of some piece of secondary 'new_a' | |
147 | # Finally, someone goes to merge D & E. Does git detect the conflict? | |
148 | # | |
149 | # B D | |
150 | # o---o | |
151 | # / \ / \ | |
152 | # A o X ? F | |
153 | # \ / \ / | |
154 | # o---o | |
155 | # C E | |
156 | # | |
157 | ||
158 | test_expect_success 'setup differently handled merges of rename/add conflict' ' | |
159 | git rm -rf . && | |
160 | git clean -fdqx && | |
161 | rm -rf .git && | |
162 | git init && | |
163 | ||
164 | printf "0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n" >a && | |
165 | git add a && | |
166 | test_tick && git commit -m A && | |
167 | ||
168 | git branch B && | |
169 | git checkout -b C && | |
170 | echo 10 >>a && | |
171 | echo "other content" >>new_a && | |
172 | git add a new_a && | |
173 | test_tick && git commit -m C && | |
174 | ||
175 | git checkout B && | |
176 | git mv a new_a && | |
177 | test_tick && git commit -m B && | |
178 | ||
179 | git checkout B^0 && | |
180 | test_must_fail git merge C && | |
181 | git clean -f && | |
182 | test_tick && git commit -m D && | |
183 | git tag D && | |
184 | ||
185 | git checkout C^0 && | |
186 | test_must_fail git merge B && | |
187 | rm new_a~HEAD new_a && | |
188 | printf "Incorrectly merged content" >>new_a && | |
189 | git add -u && | |
190 | test_tick && git commit -m E && | |
191 | git tag E | |
192 | ' | |
193 | ||
2a669c34 | 194 | test_expect_success 'git detects differently handled merges conflict' ' |
f63622c0 EN |
195 | git reset --hard && |
196 | git checkout D^0 && | |
197 | ||
198 | git merge -s recursive E^0 && { | |
199 | echo "BAD: should have conflicted" | |
200 | test "Incorrectly merged content" = "$(cat new_a)" && | |
201 | echo "BAD: Silently accepted wrong content" | |
202 | return 1 | |
203 | } | |
204 | ||
205 | test 3 = $(git ls-files -s | wc -l) && | |
206 | test 3 = $(git ls-files -u | wc -l) && | |
207 | test 0 = $(git ls-files -o | wc -l) && | |
208 | ||
209 | test $(git rev-parse :2:new_a) = $(git rev-parse D:new_a) && | |
210 | test $(git rev-parse :3:new_a) = $(git rev-parse E:new_a) && | |
211 | ||
212 | git cat-file -p B:new_a >>merged && | |
213 | git cat-file -p C:new_a >>merge-me && | |
214 | >empty && | |
215 | test_must_fail git merge-file \ | |
216 | -L "Temporary merge branch 2" \ | |
217 | -L "" \ | |
218 | -L "Temporary merge branch 1" \ | |
219 | merged empty merge-me && | |
220 | test $(git rev-parse :1:new_a) = $(git hash-object merged) | |
c94736a2 JH |
221 | ' |
222 | ||
fe7e9c23 EN |
223 | # |
224 | # criss-cross + modify/delete: | |
225 | # | |
226 | # B D | |
227 | # o---o | |
228 | # / \ / \ | |
229 | # A o X ? F | |
230 | # \ / \ / | |
231 | # o---o | |
232 | # C E | |
233 | # | |
234 | # Commit A: file with contents 'A\n' | |
235 | # Commit B: file with contents 'B\n' | |
236 | # Commit C: file not present | |
237 | # Commit D: file with contents 'B\n' | |
238 | # Commit E: file not present | |
239 | # | |
240 | # Merging commits D & E should result in modify/delete conflict. | |
241 | ||
242 | test_expect_success 'setup criss-cross + modify/delete resolved differently' ' | |
243 | git rm -rf . && | |
244 | git clean -fdqx && | |
245 | rm -rf .git && | |
246 | git init && | |
247 | ||
248 | echo A >file && | |
249 | git add file && | |
250 | test_tick && | |
251 | git commit -m A && | |
252 | ||
253 | git branch B && | |
254 | git checkout -b C && | |
255 | git rm file && | |
256 | test_tick && | |
257 | git commit -m C && | |
258 | ||
259 | git checkout B && | |
260 | echo B >file && | |
261 | git add file && | |
262 | test_tick && | |
263 | git commit -m B && | |
264 | ||
265 | git checkout B^0 && | |
266 | test_must_fail git merge C && | |
267 | echo B >file && | |
268 | git add file && | |
269 | test_tick && | |
270 | git commit -m D && | |
271 | git tag D && | |
272 | ||
273 | git checkout C^0 && | |
274 | test_must_fail git merge B && | |
275 | git rm file && | |
276 | test_tick && | |
277 | git commit -m E && | |
278 | git tag E | |
279 | ' | |
280 | ||
ec61d149 | 281 | test_expect_success 'git detects conflict merging criss-cross+modify/delete' ' |
fe7e9c23 EN |
282 | git checkout D^0 && |
283 | ||
284 | test_must_fail git merge -s recursive E^0 && | |
285 | ||
286 | test 2 -eq $(git ls-files -s | wc -l) && | |
287 | test 2 -eq $(git ls-files -u | wc -l) && | |
288 | ||
289 | test $(git rev-parse :1:file) = $(git rev-parse master:file) && | |
290 | test $(git rev-parse :2:file) = $(git rev-parse B:file) | |
291 | ' | |
292 | ||
ec61d149 | 293 | test_expect_success 'git detects conflict merging criss-cross+modify/delete, reverse direction' ' |
fe7e9c23 EN |
294 | git reset --hard && |
295 | git checkout E^0 && | |
296 | ||
297 | test_must_fail git merge -s recursive D^0 && | |
298 | ||
299 | test 2 -eq $(git ls-files -s | wc -l) && | |
300 | test 2 -eq $(git ls-files -u | wc -l) && | |
301 | ||
302 | test $(git rev-parse :1:file) = $(git rev-parse master:file) && | |
303 | test $(git rev-parse :3:file) = $(git rev-parse B:file) | |
304 | ' | |
305 | ||
96b079e5 EN |
306 | # |
307 | # criss-cross + modify/modify with very contrived file contents: | |
308 | # | |
309 | # B D | |
310 | # o---o | |
311 | # / \ / \ | |
312 | # A o X ? F | |
313 | # \ / \ / | |
314 | # o---o | |
315 | # C E | |
316 | # | |
317 | # Commit A: file with contents 'A\n' | |
318 | # Commit B: file with contents 'B\n' | |
319 | # Commit C: file with contents 'C\n' | |
320 | # Commit D: file with contents 'D\n' | |
321 | # Commit E: file with contents: | |
322 | # <<<<<<< Temporary merge branch 1 | |
323 | # C | |
324 | # ======= | |
325 | # B | |
326 | # >>>>>>> Temporary merge branch 2 | |
327 | # | |
328 | # Now, when we merge commits D & E, does git detect the conflict? | |
329 | ||
330 | test_expect_success 'setup differently handled merges of content conflict' ' | |
331 | git clean -fdqx && | |
332 | rm -rf .git && | |
333 | git init && | |
334 | ||
335 | echo A >file && | |
336 | git add file && | |
337 | test_tick && | |
338 | git commit -m A && | |
339 | ||
340 | git branch B && | |
341 | git checkout -b C && | |
342 | echo C >file && | |
343 | git add file && | |
344 | test_tick && | |
345 | git commit -m C && | |
346 | ||
347 | git checkout B && | |
348 | echo B >file && | |
349 | git add file && | |
350 | test_tick && | |
351 | git commit -m B && | |
352 | ||
353 | git checkout B^0 && | |
354 | test_must_fail git merge C && | |
355 | echo D >file && | |
356 | git add file && | |
357 | test_tick && | |
358 | git commit -m D && | |
359 | git tag D && | |
360 | ||
361 | git checkout C^0 && | |
362 | test_must_fail git merge B && | |
363 | cat <<EOF >file && | |
364 | <<<<<<< Temporary merge branch 1 | |
365 | C | |
366 | ======= | |
367 | B | |
368 | >>>>>>> Temporary merge branch 2 | |
369 | EOF | |
370 | git add file && | |
371 | test_tick && | |
372 | git commit -m E && | |
373 | git tag E | |
374 | ' | |
375 | ||
376 | test_expect_failure 'git detects conflict w/ criss-cross+contrived resolution' ' | |
377 | git checkout D^0 && | |
378 | ||
379 | test_must_fail git merge -s recursive E^0 && | |
380 | ||
381 | test 3 -eq $(git ls-files -s | wc -l) && | |
382 | test 3 -eq $(git ls-files -u | wc -l) && | |
383 | test 0 -eq $(git ls-files -o | wc -l) && | |
384 | ||
385 | test $(git rev-parse :2:file) = $(git rev-parse D:file) && | |
386 | test $(git rev-parse :3:file) = $(git rev-parse E:file) | |
387 | ' | |
388 | ||
827f2b7d EN |
389 | # |
390 | # criss-cross + d/f conflict via add/add: | |
391 | # Commit A: Neither file 'a' nor directory 'a/' exist. | |
392 | # Commit B: Introduce 'a' | |
393 | # Commit C: Introduce 'a/file' | |
394 | # Commit D: Merge B & C, keeping 'a' and deleting 'a/' | |
395 | # | |
396 | # Two different later cases: | |
397 | # Commit E1: Merge B & C, deleting 'a' but keeping 'a/file' | |
398 | # Commit E2: Merge B & C, deleting 'a' but keeping a slightly modified 'a/file' | |
399 | # | |
400 | # B D | |
401 | # o---o | |
402 | # / \ / \ | |
403 | # A o X ? F | |
404 | # \ / \ / | |
405 | # o---o | |
406 | # C E1 or E2 | |
407 | # | |
408 | # Merging D & E1 requires we first create a virtual merge base X from | |
409 | # merging A & B in memory. Now, if X could keep both 'a' and 'a/file' in | |
410 | # the index, then the merge of D & E1 could be resolved cleanly with both | |
411 | # 'a' and 'a/file' removed. Since git does not currently allow creating | |
412 | # such a tree, the best we can do is have X contain both 'a~<unique>' and | |
413 | # 'a/file' resulting in the merge of D and E1 having a rename/delete | |
414 | # conflict for 'a'. (Although this merge appears to be unsolvable with git | |
415 | # currently, git could do a lot better than it currently does with these | |
416 | # d/f conflicts, which is the purpose of this test.) | |
417 | # | |
418 | # Merge of D & E2 has similar issues for path 'a', but should always result | |
419 | # in a modify/delete conflict for path 'a/file'. | |
420 | # | |
421 | # We run each merge in both directions, to check for directional issues | |
422 | # with D/F conflict handling. | |
423 | # | |
424 | ||
425 | test_expect_success 'setup differently handled merges of directory/file conflict' ' | |
426 | git rm -rf . && | |
427 | git clean -fdqx && | |
428 | rm -rf .git && | |
429 | git init && | |
430 | ||
431 | >ignore-me && | |
432 | git add ignore-me && | |
433 | test_tick && | |
434 | git commit -m A && | |
435 | git tag A && | |
436 | ||
437 | git branch B && | |
438 | git checkout -b C && | |
439 | mkdir a && | |
440 | echo 10 >a/file && | |
441 | git add a/file && | |
442 | test_tick && | |
443 | git commit -m C && | |
444 | ||
445 | git checkout B && | |
446 | echo 5 >a && | |
447 | git add a && | |
448 | test_tick && | |
449 | git commit -m B && | |
450 | ||
451 | git checkout B^0 && | |
452 | test_must_fail git merge C && | |
453 | git clean -f && | |
454 | rm -rf a/ && | |
455 | echo 5 >a && | |
456 | git add a && | |
457 | test_tick && | |
458 | git commit -m D && | |
459 | git tag D && | |
460 | ||
461 | git checkout C^0 && | |
462 | test_must_fail git merge B && | |
463 | git clean -f && | |
464 | git rm --cached a && | |
465 | echo 10 >a/file && | |
466 | git add a/file && | |
467 | test_tick && | |
468 | git commit -m E1 && | |
469 | git tag E1 && | |
470 | ||
471 | git checkout C^0 && | |
472 | test_must_fail git merge B && | |
473 | git clean -f && | |
474 | git rm --cached a && | |
475 | printf "10\n11\n" >a/file && | |
476 | git add a/file && | |
477 | test_tick && | |
478 | git commit -m E2 && | |
479 | git tag E2 | |
480 | ' | |
481 | ||
ed0148a5 | 482 | test_expect_success 'merge of D & E1 fails but has appropriate contents' ' |
827f2b7d EN |
483 | get_clean_checkout D^0 && |
484 | ||
485 | test_must_fail git merge -s recursive E1^0 && | |
486 | ||
487 | test 2 -eq $(git ls-files -s | wc -l) && | |
488 | test 1 -eq $(git ls-files -u | wc -l) && | |
489 | test 0 -eq $(git ls-files -o | wc -l) && | |
490 | ||
491 | test $(git rev-parse :0:ignore-me) = $(git rev-parse A:ignore-me) && | |
492 | test $(git rev-parse :2:a) = $(git rev-parse B:a) | |
493 | ' | |
494 | ||
7b1c610f | 495 | test_expect_success 'merge of E1 & D fails but has appropriate contents' ' |
827f2b7d EN |
496 | get_clean_checkout E1^0 && |
497 | ||
498 | test_must_fail git merge -s recursive D^0 && | |
499 | ||
500 | test 2 -eq $(git ls-files -s | wc -l) && | |
501 | test 1 -eq $(git ls-files -u | wc -l) && | |
502 | test 0 -eq $(git ls-files -o | wc -l) && | |
503 | ||
504 | test $(git rev-parse :0:ignore-me) = $(git rev-parse A:ignore-me) && | |
505 | test $(git rev-parse :3:a) = $(git rev-parse B:a) | |
506 | ' | |
507 | ||
508 | test_expect_success 'merge of D & E2 fails but has appropriate contents' ' | |
509 | get_clean_checkout D^0 && | |
510 | ||
511 | test_must_fail git merge -s recursive E2^0 && | |
512 | ||
513 | test 4 -eq $(git ls-files -s | wc -l) && | |
514 | test 3 -eq $(git ls-files -u | wc -l) && | |
515 | test 1 -eq $(git ls-files -o | wc -l) && | |
516 | ||
517 | test $(git rev-parse :2:a) = $(git rev-parse B:a) && | |
518 | test $(git rev-parse :3:a/file) = $(git rev-parse E2:a/file) && | |
519 | test $(git rev-parse :1:a/file) = $(git rev-parse C:a/file) && | |
520 | test $(git rev-parse :0:ignore-me) = $(git rev-parse A:ignore-me) && | |
521 | ||
522 | test -f a~HEAD | |
523 | ' | |
524 | ||
7b1c610f | 525 | test_expect_success 'merge of E2 & D fails but has appropriate contents' ' |
827f2b7d EN |
526 | get_clean_checkout E2^0 && |
527 | ||
528 | test_must_fail git merge -s recursive D^0 && | |
529 | ||
530 | test 4 -eq $(git ls-files -s | wc -l) && | |
531 | test 3 -eq $(git ls-files -u | wc -l) && | |
532 | test 1 -eq $(git ls-files -o | wc -l) && | |
533 | ||
534 | test $(git rev-parse :3:a) = $(git rev-parse B:a) && | |
535 | test $(git rev-parse :2:a/file) = $(git rev-parse E2:a/file) && | |
536 | test $(git rev-parse :1:a/file) = $(git rev-parse C:a/file) | |
537 | test $(git rev-parse :0:ignore-me) = $(git rev-parse A:ignore-me) && | |
538 | ||
539 | test -f a~D^0 | |
540 | ' | |
541 | ||
a0d33116 EN |
542 | # |
543 | # criss-cross with rename/rename(1to2)/modify followed by | |
544 | # rename/rename(2to1)/modify: | |
545 | # | |
546 | # B D | |
547 | # o---o | |
548 | # / \ / \ | |
549 | # A o X ? F | |
550 | # \ / \ / | |
551 | # o---o | |
552 | # C E | |
553 | # | |
554 | # Commit A: new file: a | |
555 | # Commit B: rename a->b, modifying by adding a line | |
556 | # Commit C: rename a->c | |
557 | # Commit D: merge B&C, resolving conflict by keeping contents in newname | |
558 | # Commit E: merge B&C, resolving conflict similar to D but adding another line | |
559 | # | |
560 | # There is a conflict merging B & C, but one of filename not of file | |
561 | # content. Whoever created D and E chose specific resolutions for that | |
562 | # conflict resolution. Now, since: (1) there is no content conflict | |
563 | # merging B & C, (2) D does not modify that merged content further, and (3) | |
564 | # both D & E resolve the name conflict in the same way, the modification to | |
565 | # newname in E should not cause any conflicts when it is merged with D. | |
566 | # (Note that this can be accomplished by having the virtual merge base have | |
567 | # the merged contents of b and c stored in a file named a, which seems like | |
568 | # the most logical choice anyway.) | |
569 | # | |
570 | # Comment from Junio: I do not necessarily agree with the choice "a", but | |
571 | # it feels sound to say "B and C do not agree what the final pathname | |
572 | # should be, but we know this content was derived from the common A:a so we | |
573 | # use one path whose name is arbitrary in the virtual merge base X between | |
574 | # D and E" and then further let the rename detection to notice that that | |
575 | # arbitrary path gets renamed between X-D to "newname" and X-E also to | |
576 | # "newname" to resolve it as both sides renaming it to the same new | |
577 | # name. It is akin to what we do at the content level, i.e. "B and C do not | |
578 | # agree what the final contents should be, so we leave the conflict marker | |
579 | # but that may cancel out at the final merge stage". | |
580 | ||
581 | test_expect_success 'setup rename/rename(1to2)/modify followed by what looks like rename/rename(2to1)/modify' ' | |
582 | git reset --hard && | |
583 | git rm -rf . && | |
584 | git clean -fdqx && | |
585 | rm -rf .git && | |
586 | git init && | |
587 | ||
588 | printf "1\n2\n3\n4\n5\n6\n" >a && | |
589 | git add a && | |
590 | git commit -m A && | |
591 | git tag A && | |
592 | ||
593 | git checkout -b B A && | |
594 | git mv a b && | |
595 | echo 7 >>b && | |
596 | git add -u && | |
597 | git commit -m B && | |
598 | ||
599 | git checkout -b C A && | |
600 | git mv a c && | |
601 | git commit -m C && | |
602 | ||
603 | git checkout -q B^0 && | |
604 | git merge --no-commit -s ours C^0 && | |
605 | git mv b newname && | |
606 | git commit -m "Merge commit C^0 into HEAD" && | |
607 | git tag D && | |
608 | ||
609 | git checkout -q C^0 && | |
610 | git merge --no-commit -s ours B^0 && | |
611 | git mv c newname && | |
612 | printf "7\n8\n" >>newname && | |
613 | git add -u && | |
614 | git commit -m "Merge commit B^0 into HEAD" && | |
615 | git tag E | |
616 | ' | |
617 | ||
c52ff85d | 618 | test_expect_success 'handle rename/rename(1to2)/modify followed by what looks like rename/rename(2to1)/modify' ' |
a0d33116 EN |
619 | git checkout D^0 && |
620 | ||
621 | git merge -s recursive E^0 && | |
622 | ||
623 | test 1 -eq $(git ls-files -s | wc -l) && | |
624 | test 0 -eq $(git ls-files -u | wc -l) && | |
625 | test 0 -eq $(git ls-files -o | wc -l) && | |
626 | ||
627 | test $(git rev-parse HEAD:newname) = $(git rev-parse E:newname) | |
628 | ' | |
629 | ||
0b35deb3 EN |
630 | # |
631 | # criss-cross with rename/rename(1to2)/add-source + resolvable modify/modify: | |
632 | # | |
633 | # B D | |
634 | # o---o | |
635 | # / \ / \ | |
636 | # A o X ? F | |
637 | # \ / \ / | |
638 | # o---o | |
639 | # C E | |
640 | # | |
641 | # Commit A: new file: a | |
642 | # Commit B: rename a->b | |
643 | # Commit C: rename a->c, add different a | |
644 | # Commit D: merge B&C, keeping b&c and (new) a modified at beginning | |
645 | # Commit E: merge B&C, keeping b&c and (new) a modified at end | |
646 | # | |
647 | # Merging commits D & E should result in no conflict; doing so correctly | |
648 | # requires getting the virtual merge base (from merging B&C) right, handling | |
649 | # renaming carefully (both in the virtual merge base and later), and getting | |
650 | # content merge handled. | |
651 | ||
652 | test_expect_success 'setup criss-cross + rename/rename/add + modify/modify' ' | |
653 | git rm -rf . && | |
654 | git clean -fdqx && | |
655 | rm -rf .git && | |
656 | git init && | |
657 | ||
658 | printf "lots\nof\nwords\nand\ncontent\n" >a && | |
659 | git add a && | |
660 | git commit -m A && | |
661 | git tag A && | |
662 | ||
663 | git checkout -b B A && | |
664 | git mv a b && | |
665 | git commit -m B && | |
666 | ||
667 | git checkout -b C A && | |
668 | git mv a c && | |
669 | printf "2\n3\n4\n5\n6\n7\n" >a && | |
670 | git add a && | |
671 | git commit -m C && | |
672 | ||
673 | git checkout B^0 && | |
674 | git merge --no-commit -s ours C^0 && | |
675 | git checkout C -- a c && | |
676 | mv a old_a && | |
677 | echo 1 >a && | |
678 | cat old_a >>a && | |
679 | rm old_a && | |
680 | git add -u && | |
681 | git commit -m "Merge commit C^0 into HEAD" && | |
682 | git tag D && | |
683 | ||
684 | git checkout C^0 && | |
685 | git merge --no-commit -s ours B^0 && | |
686 | git checkout B -- b && | |
687 | echo 8 >>a && | |
688 | git add -u && | |
689 | git commit -m "Merge commit B^0 into HEAD" && | |
690 | git tag E | |
691 | ' | |
692 | ||
693 | test_expect_failure 'detect rename/rename/add-source for virtual merge-base' ' | |
694 | git checkout D^0 && | |
695 | ||
696 | git merge -s recursive E^0 && | |
697 | ||
698 | test 3 -eq $(git ls-files -s | wc -l) && | |
699 | test 0 -eq $(git ls-files -u | wc -l) && | |
700 | test 0 -eq $(git ls-files -o | wc -l) && | |
701 | ||
702 | test $(git rev-parse HEAD:b) = $(git rev-parse A:a) && | |
703 | test $(git rev-parse HEAD:c) = $(git rev-parse A:a) && | |
704 | test "$(cat a)" = "$(printf "1\n2\n3\n4\n5\n6\n7\n8\n")" | |
705 | ' | |
706 | ||
b630b814 EN |
707 | # |
708 | # criss-cross with rename/rename(1to2)/add-dest + simple modify: | |
709 | # | |
710 | # B D | |
711 | # o---o | |
712 | # / \ / \ | |
713 | # A o X ? F | |
714 | # \ / \ / | |
715 | # o---o | |
716 | # C E | |
717 | # | |
718 | # Commit A: new file: a | |
719 | # Commit B: rename a->b, add c | |
720 | # Commit C: rename a->c | |
721 | # Commit D: merge B&C, keeping A:a and B:c | |
722 | # Commit E: merge B&C, keeping A:a and slightly modified c from B | |
723 | # | |
724 | # Merging commits D & E should result in no conflict. The virtual merge | |
725 | # base of B & C needs to not delete B:c for that to work, though... | |
726 | ||
727 | test_expect_success 'setup criss-cross+rename/rename/add-dest + simple modify' ' | |
728 | git rm -rf . && | |
729 | git clean -fdqx && | |
730 | rm -rf .git && | |
731 | git init && | |
732 | ||
733 | >a && | |
734 | git add a && | |
735 | git commit -m A && | |
736 | git tag A && | |
737 | ||
738 | git checkout -b B A && | |
739 | git mv a b && | |
740 | printf "1\n2\n3\n4\n5\n6\n7\n" >c && | |
741 | git add c && | |
742 | git commit -m B && | |
743 | ||
744 | git checkout -b C A && | |
745 | git mv a c && | |
746 | git commit -m C && | |
747 | ||
748 | git checkout B^0 && | |
749 | git merge --no-commit -s ours C^0 && | |
750 | git mv b a && | |
751 | git commit -m "D is like B but renames b back to a" && | |
752 | git tag D && | |
753 | ||
754 | git checkout B^0 && | |
755 | git merge --no-commit -s ours C^0 && | |
756 | git mv b a && | |
757 | echo 8 >>c && | |
758 | git add c && | |
759 | git commit -m "E like D but has mod in c" && | |
760 | git tag E | |
761 | ' | |
762 | ||
6d63070c | 763 | test_expect_success 'virtual merge base handles rename/rename(1to2)/add-dest' ' |
b630b814 EN |
764 | git checkout D^0 && |
765 | ||
766 | git merge -s recursive E^0 && | |
767 | ||
768 | test 2 -eq $(git ls-files -s | wc -l) && | |
769 | test 0 -eq $(git ls-files -u | wc -l) && | |
770 | test 0 -eq $(git ls-files -o | wc -l) && | |
771 | ||
772 | test $(git rev-parse HEAD:a) = $(git rev-parse A:a) && | |
773 | test $(git rev-parse HEAD:c) = $(git rev-parse E:c) | |
774 | ' | |
775 | ||
c94736a2 | 776 | test_done |