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