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