Raw
1 #!/bin/sh
2
3 test_description="merge cases"
4
5 # The setup for all of them, pictorially, is:
6 #
7 # A
8 # o
9 # / \
10 # O o ?
11 # \ /
12 # o
13 # B
14 #
15 # To help make it easier to follow the flow of tests, they have been
16 # divided into sections and each test will start with a quick explanation
17 # of what commits O, A, and B contain.
18 #
19 # Notation:
20 # z/{b,c} means files z/b and z/c both exist
21 # x/d_1 means file x/d exists with content d1. (Purpose of the
22 # underscore notation is to differentiate different
23 # files that might be renamed into each other's paths.)
24
25 . ./test-lib.sh
26
27 ###########################################################################
28 # SECTION 1: Cases involving no renames (one side has subset of changes of
29 # the other side)
30 ###########################################################################
31
32 # Testcase 1a, Changes on A, subset of changes on B
33 # Commit O: b_1
34 # Commit A: b_2
35 # Commit B: b_3
36 # Expected: b_2
37
38 test_setup_1a () {
39 git init 1a_$1 &&
40 (
41 cd 1a_$1 &&
42
43 test_write_lines 1 2 3 4 5 6 7 8 9 10 >b &&
44 git add b &&
45 test_tick &&
46 git commit -m "O" &&
47
48 git branch O &&
49 git branch A &&
50 git branch B &&
51
52 git checkout A &&
53 test_write_lines 1 2 3 4 5 5.5 6 7 8 9 10 10.5 >b &&
54 git add b &&
55 test_tick &&
56 git commit -m "A" &&
57
58 git checkout B &&
59 test_write_lines 1 2 3 4 5 5.5 6 7 8 9 10 >b &&
60 git add b &&
61 test_tick &&
62 git commit -m "B"
63 )
64 }
65
66 test_expect_success '1a-L: Modify(A)/Modify(B), change on B subset of A' '
67 test_setup_1a L &&
68 (
69 cd 1a_L &&
70
71 git checkout A^0 &&
72
73 test-tool chmtime --get -3600 b >old-mtime &&
74
75 GIT_MERGE_VERBOSITY=3 git merge -s recursive B^0 >out 2>err &&
76
77 test_must_be_empty err &&
78
79 # Make sure b was NOT updated
80 test-tool chmtime --get b >new-mtime &&
81 test_cmp old-mtime new-mtime &&
82
83 git ls-files -s >index_files &&
84 test_line_count = 1 index_files &&
85
86 git rev-parse >actual HEAD:b &&
87 git rev-parse >expect A:b &&
88 test_cmp expect actual &&
89
90 git hash-object b >actual &&
91 git rev-parse A:b >expect &&
92 test_cmp expect actual
93 )
94 '
95
96 test_expect_success '1a-R: Modify(A)/Modify(B), change on B subset of A' '
97 test_setup_1a R &&
98 (
99 cd 1a_R &&
100
101 git checkout B^0 &&
102
103 test-tool chmtime --get -3600 b >old-mtime &&
104 GIT_MERGE_VERBOSITY=3 git merge -s recursive A^0 >out 2>err &&
105
106 # Make sure b WAS updated
107 test-tool chmtime --get b >new-mtime &&
108 test $(cat old-mtime) -lt $(cat new-mtime) &&
109
110 test_must_be_empty err &&
111
112 git ls-files -s >index_files &&
113 test_line_count = 1 index_files &&
114
115 git rev-parse >actual HEAD:b &&
116 git rev-parse >expect A:b &&
117 test_cmp expect actual &&
118
119 git hash-object b >actual &&
120 git rev-parse A:b >expect &&
121 test_cmp expect actual
122 )
123 '
124
125
126 ###########################################################################
127 # SECTION 2: Cases involving basic renames
128 ###########################################################################
129
130 # Testcase 2a, Changes on A, rename on B
131 # Commit O: b_1
132 # Commit A: b_2
133 # Commit B: c_1
134 # Expected: c_2
135
136 test_setup_2a () {
137 git init 2a_$1 &&
138 (
139 cd 2a_$1 &&
140
141 test_seq 1 10 >b &&
142 git add b &&
143 test_tick &&
144 git commit -m "O" &&
145
146 git branch O &&
147 git branch A &&
148 git branch B &&
149
150 git checkout A &&
151 test_seq 1 11 >b &&
152 git add b &&
153 test_tick &&
154 git commit -m "A" &&
155
156 git checkout B &&
157 git mv b c &&
158 test_tick &&
159 git commit -m "B"
160 )
161 }
162
163 test_expect_success '2a-L: Modify/rename, merge into modify side' '
164 test_setup_2a L &&
165 (
166 cd 2a_L &&
167
168 git checkout A^0 &&
169
170 test_path_is_missing c &&
171 GIT_MERGE_VERBOSITY=3 git merge -s recursive B^0 >out 2>err &&
172
173 test_path_is_file c &&
174
175 git ls-files -s >index_files &&
176 test_line_count = 1 index_files &&
177
178 git rev-parse >actual HEAD:c &&
179 git rev-parse >expect A:b &&
180 test_cmp expect actual &&
181
182 git hash-object c >actual &&
183 git rev-parse A:b >expect &&
184 test_cmp expect actual &&
185
186 test_must_fail git rev-parse HEAD:b &&
187 test_path_is_missing b
188 )
189 '
190
191 test_expect_success '2a-R: Modify/rename, merge into rename side' '
192 test_setup_2a R &&
193 (
194 cd 2a_R &&
195
196 git checkout B^0 &&
197
198 test-tool chmtime --get -3600 c >old-mtime &&
199 GIT_MERGE_VERBOSITY=3 git merge -s recursive A^0 >out 2>err &&
200
201 # Make sure c WAS updated
202 test-tool chmtime --get c >new-mtime &&
203 test $(cat old-mtime) -lt $(cat new-mtime) &&
204
205 test_must_be_empty err &&
206
207 git ls-files -s >index_files &&
208 test_line_count = 1 index_files &&
209
210 git rev-parse >actual HEAD:c &&
211 git rev-parse >expect A:b &&
212 test_cmp expect actual &&
213
214 git hash-object c >actual &&
215 git rev-parse A:b >expect &&
216 test_cmp expect actual &&
217
218 test_must_fail git rev-parse HEAD:b &&
219 test_path_is_missing b
220 )
221 '
222
223 # Testcase 2b, Changed and renamed on A, subset of changes on B
224 # Commit O: b_1
225 # Commit A: c_2
226 # Commit B: b_3
227 # Expected: c_2
228
229 test_setup_2b () {
230 git init 2b_$1 &&
231 (
232 cd 2b_$1 &&
233
234 test_write_lines 1 2 3 4 5 6 7 8 9 10 >b &&
235 git add b &&
236 test_tick &&
237 git commit -m "O" &&
238
239 git branch O &&
240 git branch A &&
241 git branch B &&
242
243 git checkout A &&
244 test_write_lines 1 2 3 4 5 5.5 6 7 8 9 10 10.5 >b &&
245 git add b &&
246 git mv b c &&
247 test_tick &&
248 git commit -m "A" &&
249
250 git checkout B &&
251 test_write_lines 1 2 3 4 5 5.5 6 7 8 9 10 >b &&
252 git add b &&
253 test_tick &&
254 git commit -m "B"
255 )
256 }
257
258 test_expect_success '2b-L: Rename+Mod(A)/Mod(B), B mods subset of A' '
259 test_setup_2b L &&
260 (
261 cd 2b_L &&
262
263 git checkout A^0 &&
264
265 test-tool chmtime --get -3600 c >old-mtime &&
266 GIT_MERGE_VERBOSITY=3 git merge -s recursive B^0 >out 2>err &&
267
268 test_must_be_empty err &&
269
270 # Make sure c WAS updated
271 test-tool chmtime --get c >new-mtime &&
272 test_cmp old-mtime new-mtime &&
273
274 git ls-files -s >index_files &&
275 test_line_count = 1 index_files &&
276
277 git rev-parse >actual HEAD:c &&
278 git rev-parse >expect A:c &&
279 test_cmp expect actual &&
280
281 git hash-object c >actual &&
282 git rev-parse A:c >expect &&
283 test_cmp expect actual &&
284
285 test_must_fail git rev-parse HEAD:b &&
286 test_path_is_missing b
287 )
288 '
289
290 test_expect_success '2b-R: Rename+Mod(A)/Mod(B), B mods subset of A' '
291 test_setup_2b R &&
292 (
293 cd 2b_R &&
294
295 git checkout B^0 &&
296
297 test_path_is_missing c &&
298 GIT_MERGE_VERBOSITY=3 git merge -s recursive A^0 >out 2>err &&
299
300 # Make sure c now present (and thus was updated)
301 test_path_is_file c &&
302
303 test_must_be_empty err &&
304
305 git ls-files -s >index_files &&
306 test_line_count = 1 index_files &&
307
308 git rev-parse >actual HEAD:c &&
309 git rev-parse >expect A:c &&
310 test_cmp expect actual &&
311
312 git hash-object c >actual &&
313 git rev-parse A:c >expect &&
314 test_cmp expect actual &&
315
316 test_must_fail git rev-parse HEAD:b &&
317 test_path_is_missing b
318 )
319 '
320
321 # Testcase 2c, Changes on A, rename on B
322 # Commit O: b_1
323 # Commit A: b_2, c_3
324 # Commit B: c_1
325 # Expected: rename/add conflict c_2 vs c_3
326 #
327 # NOTE: Since A modified b_1->b_2, and B renamed b_1->c_1, the threeway
328 # merge of those files should result in c_2. We then should have a
329 # rename/add conflict between c_2 and c_3. However, if we note in
330 # merge_content() that A had the right contents (b_2 has same
331 # contents as c_2, just at a different name), and that A had the
332 # right path present (c_3 existed) and thus decides that it can
333 # skip the update, then we're in trouble. This test verifies we do
334 # not make that particular mistake.
335
336 test_setup_2c () {
337 git init 2c &&
338 (
339 cd 2c &&
340
341 test_seq 1 10 >b &&
342 git add b &&
343 test_tick &&
344 git commit -m "O" &&
345
346 git branch O &&
347 git branch A &&
348 git branch B &&
349
350 git checkout A &&
351 test_seq 1 11 >b &&
352 echo whatever >c &&
353 git add b c &&
354 test_tick &&
355 git commit -m "A" &&
356
357 git checkout B &&
358 git mv b c &&
359 test_tick &&
360 git commit -m "B"
361 )
362 }
363
364 test_expect_success '2c: Modify b & add c VS rename b->c' '
365 test_setup_2c &&
366 (
367 cd 2c &&
368
369 git checkout A^0 &&
370
371 test-tool chmtime --get -3600 c >old-mtime &&
372 GIT_MERGE_VERBOSITY=3 &&
373 export GIT_MERGE_VERBOSITY &&
374 test_must_fail git merge -s recursive B^0 >out 2>err &&
375
376 test_grep "CONFLICT (.*/add):" out &&
377 test_must_be_empty err &&
378
379 git ls-files -s >index_files &&
380 test_line_count = 2 index_files &&
381
382 # Ensure b was removed
383 test_path_is_missing b &&
384
385 # Make sure c WAS updated...
386 test-tool chmtime --get c >new-mtime &&
387 test $(cat old-mtime) -lt $(cat new-mtime) &&
388
389 # ...and has correct index entries and working tree contents
390 git rev-parse >actual :2:c :3:c &&
391 git rev-parse >expect A:c A:b &&
392 test_cmp expect actual &&
393
394 git cat-file -p A:b >>merge-me &&
395 git cat-file -p A:c >>merged &&
396 >empty &&
397 test_must_fail git merge-file \
398 -L "HEAD" \
399 -L "" \
400 -L "B^0" \
401 merged empty merge-me &&
402 test_cmp merged c
403 )
404 '
405
406
407 ###########################################################################
408 # SECTION 3: Cases involving directory renames
409 #
410 # NOTE:
411 # Directory renames only apply when one side renames a directory, and the
412 # other side adds or renames a path into that directory. Applying the
413 # directory rename to that new path creates a new pathname that didn't
414 # exist on either side of history. Thus, it is impossible for the
415 # merge contents to already be at the right path, so all of these checks
416 # exist just to make sure that updates are not skipped.
417 ###########################################################################
418
419 # Testcase 3a, Change + rename into dir foo on A, dir rename foo->bar on B
420 # Commit O: bq_1, foo/whatever
421 # Commit A: foo/{bq_2, whatever}
422 # Commit B: bq_1, bar/whatever
423 # Expected: bar/{bq_2, whatever}
424
425 test_setup_3a () {
426 git init 3a_$1 &&
427 (
428 cd 3a_$1 &&
429
430 mkdir foo &&
431 test_seq 1 10 >bq &&
432 test_write_lines a b c d e f g h i j k >foo/whatever &&
433 git add bq foo/whatever &&
434 test_tick &&
435 git commit -m "O" &&
436
437 git branch O &&
438 git branch A &&
439 git branch B &&
440
441 git checkout A &&
442 test_seq 1 11 >bq &&
443 git add bq &&
444 git mv bq foo/ &&
445 test_tick &&
446 git commit -m "A" &&
447
448 git checkout B &&
449 git mv foo/ bar/ &&
450 test_tick &&
451 git commit -m "B"
452 )
453 }
454
455 test_expect_success '3a-L: bq_1->foo/bq_2 on A, foo/->bar/ on B' '
456 test_setup_3a L &&
457 (
458 cd 3a_L &&
459
460 git checkout A^0 &&
461
462 test_path_is_missing bar/bq &&
463 GIT_MERGE_VERBOSITY=3 git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
464
465 test_must_be_empty err &&
466
467 test_path_is_file bar/bq &&
468
469 git ls-files -s >index_files &&
470 test_line_count = 2 index_files &&
471
472 git rev-parse >actual HEAD:bar/bq HEAD:bar/whatever &&
473 git rev-parse >expect A:foo/bq A:foo/whatever &&
474 test_cmp expect actual &&
475
476 git hash-object bar/bq bar/whatever >actual &&
477 git rev-parse A:foo/bq A:foo/whatever >expect &&
478 test_cmp expect actual &&
479
480 test_must_fail git rev-parse HEAD:bq HEAD:foo/bq &&
481 test_path_is_missing bq &&
482 test_path_is_missing foo/bq &&
483 test_path_is_missing foo/whatever
484 )
485 '
486
487 test_expect_success '3a-R: bq_1->foo/bq_2 on A, foo/->bar/ on B' '
488 test_setup_3a R &&
489 (
490 cd 3a_R &&
491
492 git checkout B^0 &&
493
494 test_path_is_missing bar/bq &&
495 GIT_MERGE_VERBOSITY=3 git -c merge.directoryRenames=true merge -s recursive A^0 >out 2>err &&
496
497 test_must_be_empty err &&
498
499 test_path_is_file bar/bq &&
500
501 git ls-files -s >index_files &&
502 test_line_count = 2 index_files &&
503
504 git rev-parse >actual HEAD:bar/bq HEAD:bar/whatever &&
505 git rev-parse >expect A:foo/bq A:foo/whatever &&
506 test_cmp expect actual &&
507
508 git hash-object bar/bq bar/whatever >actual &&
509 git rev-parse A:foo/bq A:foo/whatever >expect &&
510 test_cmp expect actual &&
511
512 test_must_fail git rev-parse HEAD:bq HEAD:foo/bq &&
513 test_path_is_missing bq &&
514 test_path_is_missing foo/bq &&
515 test_path_is_missing foo/whatever
516 )
517 '
518
519 # Testcase 3b, rename into dir foo on A, dir rename foo->bar + change on B
520 # Commit O: bq_1, foo/whatever
521 # Commit A: foo/{bq_1, whatever}
522 # Commit B: bq_2, bar/whatever
523 # Expected: bar/{bq_2, whatever}
524
525 test_setup_3b () {
526 git init 3b_$1 &&
527 (
528 cd 3b_$1 &&
529
530 mkdir foo &&
531 test_seq 1 10 >bq &&
532 test_write_lines a b c d e f g h i j k >foo/whatever &&
533 git add bq foo/whatever &&
534 test_tick &&
535 git commit -m "O" &&
536
537 git branch O &&
538 git branch A &&
539 git branch B &&
540
541 git checkout A &&
542 git mv bq foo/ &&
543 test_tick &&
544 git commit -m "A" &&
545
546 git checkout B &&
547 test_seq 1 11 >bq &&
548 git add bq &&
549 git mv foo/ bar/ &&
550 test_tick &&
551 git commit -m "B"
552 )
553 }
554
555 test_expect_success '3b-L: bq_1->foo/bq_2 on A, foo/->bar/ on B' '
556 test_setup_3b L &&
557 (
558 cd 3b_L &&
559
560 git checkout A^0 &&
561
562 test_path_is_missing bar/bq &&
563 GIT_MERGE_VERBOSITY=3 git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
564
565 test_must_be_empty err &&
566
567 test_path_is_file bar/bq &&
568
569 git ls-files -s >index_files &&
570 test_line_count = 2 index_files &&
571
572 git rev-parse >actual HEAD:bar/bq HEAD:bar/whatever &&
573 git rev-parse >expect B:bq A:foo/whatever &&
574 test_cmp expect actual &&
575
576 git hash-object bar/bq bar/whatever >actual &&
577 git rev-parse B:bq A:foo/whatever >expect &&
578 test_cmp expect actual &&
579
580 test_must_fail git rev-parse HEAD:bq HEAD:foo/bq &&
581 test_path_is_missing bq &&
582 test_path_is_missing foo/bq &&
583 test_path_is_missing foo/whatever
584 )
585 '
586
587 test_expect_success '3b-R: bq_1->foo/bq_2 on A, foo/->bar/ on B' '
588 test_setup_3b R &&
589 (
590 cd 3b_R &&
591
592 git checkout B^0 &&
593
594 test_path_is_missing bar/bq &&
595 GIT_MERGE_VERBOSITY=3 git -c merge.directoryRenames=true merge -s recursive A^0 >out 2>err &&
596
597 test_must_be_empty err &&
598
599 test_path_is_file bar/bq &&
600
601 git ls-files -s >index_files &&
602 test_line_count = 2 index_files &&
603
604 git rev-parse >actual HEAD:bar/bq HEAD:bar/whatever &&
605 git rev-parse >expect B:bq A:foo/whatever &&
606 test_cmp expect actual &&
607
608 git hash-object bar/bq bar/whatever >actual &&
609 git rev-parse B:bq A:foo/whatever >expect &&
610 test_cmp expect actual &&
611
612 test_must_fail git rev-parse HEAD:bq HEAD:foo/bq &&
613 test_path_is_missing bq &&
614 test_path_is_missing foo/bq &&
615 test_path_is_missing foo/whatever
616 )
617 '
618
619 ###########################################################################
620 # SECTION 4: Cases involving dirty changes
621 ###########################################################################
622
623 # Testcase 4a, Changed on A, subset of changes on B, locally modified
624 # Commit O: b_1
625 # Commit A: b_2
626 # Commit B: b_3
627 # Working copy: b_4
628 # Expected: b_2 for merge, b_4 in working copy
629
630 test_setup_4a () {
631 git init 4a &&
632 (
633 cd 4a &&
634
635 test_write_lines 1 2 3 4 5 6 7 8 9 10 >b &&
636 git add b &&
637 test_tick &&
638 git commit -m "O" &&
639
640 git branch O &&
641 git branch A &&
642 git branch B &&
643
644 git checkout A &&
645 test_write_lines 1 2 3 4 5 5.5 6 7 8 9 10 10.5 >b &&
646 git add b &&
647 test_tick &&
648 git commit -m "A" &&
649
650 git checkout B &&
651 test_write_lines 1 2 3 4 5 5.5 6 7 8 9 10 >b &&
652 git add b &&
653 test_tick &&
654 git commit -m "B"
655 )
656 }
657
658 # NOTE: For as long as we continue using unpack_trees() without index_only
659 # set to true, it will error out on a case like this claiming that the locally
660 # modified file would be overwritten by the merge. Getting this testcase
661 # correct requires doing the merge in-memory first, then realizing that no
662 # updates to the file are necessary, and thus that we can just leave the path
663 # alone.
664 test_expect_success '4a: Change on A, change on B subset of A, dirty mods present' '
665 test_setup_4a &&
666 (
667 cd 4a &&
668
669 git checkout A^0 &&
670 echo "File rewritten" >b &&
671
672 test-tool chmtime --get -3600 b >old-mtime &&
673
674 GIT_MERGE_VERBOSITY=3 git merge -s recursive B^0 >out 2>err &&
675
676 test_must_be_empty err &&
677
678 # Make sure b was NOT updated
679 test-tool chmtime --get b >new-mtime &&
680 test_cmp old-mtime new-mtime &&
681
682 git ls-files -s >index_files &&
683 test_line_count = 1 index_files &&
684
685 git rev-parse >actual :0:b &&
686 git rev-parse >expect A:b &&
687 test_cmp expect actual &&
688
689 git hash-object b >actual &&
690 echo "File rewritten" | git hash-object --stdin >expect &&
691 test_cmp expect actual
692 )
693 '
694
695 # Testcase 4b, Changed+renamed on A, subset of changes on B, locally modified
696 # Commit O: b_1
697 # Commit A: c_2
698 # Commit B: b_3
699 # Working copy: c_4
700 # Expected: c_2
701
702 test_setup_4b () {
703 git init 4b &&
704 (
705 cd 4b &&
706
707 test_write_lines 1 2 3 4 5 6 7 8 9 10 >b &&
708 git add b &&
709 test_tick &&
710 git commit -m "O" &&
711
712 git branch O &&
713 git branch A &&
714 git branch B &&
715
716 git checkout A &&
717 test_write_lines 1 2 3 4 5 5.5 6 7 8 9 10 10.5 >b &&
718 git add b &&
719 git mv b c &&
720 test_tick &&
721 git commit -m "A" &&
722
723 git checkout B &&
724 test_write_lines 1 2 3 4 5 5.5 6 7 8 9 10 >b &&
725 git add b &&
726 test_tick &&
727 git commit -m "B"
728 )
729 }
730
731 test_expect_success '4b: Rename+Mod(A)/Mod(B), change on B subset of A, dirty mods present' '
732 test_setup_4b &&
733 (
734 cd 4b &&
735
736 git checkout A^0 &&
737 echo "File rewritten" >c &&
738
739 test-tool chmtime --get -3600 c >old-mtime &&
740
741 GIT_MERGE_VERBOSITY=3 git merge -s recursive B^0 >out 2>err &&
742
743 test_must_be_empty err &&
744
745 # Make sure c was NOT updated
746 test-tool chmtime --get c >new-mtime &&
747 test_cmp old-mtime new-mtime &&
748
749 git ls-files -s >index_files &&
750 test_line_count = 1 index_files &&
751
752 git rev-parse >actual :0:c &&
753 git rev-parse >expect A:c &&
754 test_cmp expect actual &&
755
756 git hash-object c >actual &&
757 echo "File rewritten" | git hash-object --stdin >expect &&
758 test_cmp expect actual &&
759
760 test_must_fail git rev-parse HEAD:b &&
761 test_path_is_missing b
762 )
763 '
764
765 test_done