]> git.ipfire.org Git - thirdparty/git.git/blame - t/t6423-merge-rename-directories.sh
t6416: correct expectation for rename/rename(1to2) + directory/file
[thirdparty/git.git] / t / t6423-merge-rename-directories.sh
CommitLineData
04550ab5
EN
1#!/bin/sh
2
3test_description="recursive merge with directory renames"
4# includes checking of many corner cases, with a similar methodology to:
5# t6042: corner cases with renames but not criss-cross merges
6# t6036: corner cases with both renames and criss-cross merges
7#
8# The setup for all of them, pictorially, is:
9#
10# A
11# o
12# / \
13# O o ?
14# \ /
15# o
16# B
17#
18# To help make it easier to follow the flow of tests, they have been
19# divided into sections and each test will start with a quick explanation
20# of what commits O, A, and B contain.
21#
22# Notation:
23# z/{b,c} means files z/b and z/c both exist
24# x/d_1 means file x/d exists with content d1. (Purpose of the
25# underscore notation is to differentiate different
26# files that might be renamed into each other's paths.)
27
28. ./test-lib.sh
f06481f1 29. "$TEST_DIRECTORY"/lib-merge.sh
04550ab5
EN
30
31
32###########################################################################
33# SECTION 1: Basic cases we should be able to handle
34###########################################################################
35
36# Testcase 1a, Basic directory rename.
37# Commit O: z/{b,c}
38# Commit A: y/{b,c}
39# Commit B: z/{b,c,d,e/f}
40# Expected: y/{b,c,d,e/f}
41
da1e295e 42test_setup_1a () {
04550ab5
EN
43 test_create_repo 1a &&
44 (
45 cd 1a &&
46
47 mkdir z &&
48 echo b >z/b &&
49 echo c >z/c &&
50 git add z &&
51 test_tick &&
52 git commit -m "O" &&
53
54 git branch O &&
55 git branch A &&
56 git branch B &&
57
58 git checkout A &&
59 git mv z y &&
60 test_tick &&
61 git commit -m "A" &&
62
63 git checkout B &&
64 echo d >z/d &&
65 mkdir z/e &&
66 echo f >z/e/f &&
67 git add z/d z/e/f &&
68 test_tick &&
69 git commit -m "B"
70 )
da1e295e 71}
04550ab5 72
da1e295e
EN
73test_expect_success '1a: Simple directory rename detection' '
74 test_setup_1a &&
04550ab5
EN
75 (
76 cd 1a &&
77
78 git checkout A^0 &&
79
8c8e5bd6 80 git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
04550ab5
EN
81
82 git ls-files -s >out &&
83 test_line_count = 4 out &&
84
85 git rev-parse >actual \
86 HEAD:y/b HEAD:y/c HEAD:y/d HEAD:y/e/f &&
87 git rev-parse >expect \
88 O:z/b O:z/c B:z/d B:z/e/f &&
89 test_cmp expect actual &&
90
91 git hash-object y/d >actual &&
92 git rev-parse B:z/d >expect &&
93 test_cmp expect actual &&
94
95 test_must_fail git rev-parse HEAD:z/d &&
96 test_must_fail git rev-parse HEAD:z/e/f &&
97 test_path_is_missing z/d &&
98 test_path_is_missing z/e/f
99 )
100'
101
102# Testcase 1b, Merge a directory with another
103# Commit O: z/{b,c}, y/d
104# Commit A: z/{b,c,e}, y/d
105# Commit B: y/{b,c,d}
106# Expected: y/{b,c,d,e}
107
da1e295e 108test_setup_1b () {
04550ab5
EN
109 test_create_repo 1b &&
110 (
111 cd 1b &&
112
113 mkdir z &&
114 echo b >z/b &&
115 echo c >z/c &&
116 mkdir y &&
117 echo d >y/d &&
118 git add z y &&
119 test_tick &&
120 git commit -m "O" &&
121
122 git branch O &&
123 git branch A &&
124 git branch B &&
125
126 git checkout A &&
127 echo e >z/e &&
128 git add z/e &&
129 test_tick &&
130 git commit -m "A" &&
131
132 git checkout B &&
133 git mv z/b y &&
134 git mv z/c y &&
135 rmdir z &&
136 test_tick &&
137 git commit -m "B"
138 )
da1e295e 139}
04550ab5 140
da1e295e
EN
141test_expect_success '1b: Merge a directory with another' '
142 test_setup_1b &&
04550ab5
EN
143 (
144 cd 1b &&
145
146 git checkout A^0 &&
147
8c8e5bd6 148 git -c merge.directoryRenames=true merge -s recursive B^0 &&
04550ab5
EN
149
150 git ls-files -s >out &&
151 test_line_count = 4 out &&
152
153 git rev-parse >actual \
154 HEAD:y/b HEAD:y/c HEAD:y/d HEAD:y/e &&
155 git rev-parse >expect \
156 O:z/b O:z/c O:y/d A:z/e &&
157 test_cmp expect actual &&
158 test_must_fail git rev-parse HEAD:z/e
159 )
160'
161
162# Testcase 1c, Transitive renaming
163# (Related to testcases 3a and 6d -- when should a transitive rename apply?)
164# (Related to testcases 9c and 9d -- can transitivity repeat?)
bc71c4ee 165# (Related to testcase 12b -- joint-transitivity?)
04550ab5
EN
166# Commit O: z/{b,c}, x/d
167# Commit A: y/{b,c}, x/d
168# Commit B: z/{b,c,d}
169# Expected: y/{b,c,d} (because x/d -> z/d -> y/d)
170
da1e295e 171test_setup_1c () {
04550ab5
EN
172 test_create_repo 1c &&
173 (
174 cd 1c &&
175
176 mkdir z &&
177 echo b >z/b &&
178 echo c >z/c &&
179 mkdir x &&
180 echo d >x/d &&
181 git add z x &&
182 test_tick &&
183 git commit -m "O" &&
184
185 git branch O &&
186 git branch A &&
187 git branch B &&
188
189 git checkout A &&
190 git mv z y &&
191 test_tick &&
192 git commit -m "A" &&
193
194 git checkout B &&
195 git mv x/d z/d &&
196 test_tick &&
197 git commit -m "B"
198 )
da1e295e 199}
04550ab5 200
da1e295e
EN
201test_expect_success '1c: Transitive renaming' '
202 test_setup_1c &&
04550ab5
EN
203 (
204 cd 1c &&
205
206 git checkout A^0 &&
207
8c8e5bd6 208 git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
04550ab5
EN
209
210 git ls-files -s >out &&
211 test_line_count = 3 out &&
212
213 git rev-parse >actual \
214 HEAD:y/b HEAD:y/c HEAD:y/d &&
215 git rev-parse >expect \
216 O:z/b O:z/c O:x/d &&
217 test_cmp expect actual &&
218 test_must_fail git rev-parse HEAD:x/d &&
219 test_must_fail git rev-parse HEAD:z/d &&
220 test_path_is_missing z/d
221 )
222'
223
224# Testcase 1d, Directory renames (merging two directories into one new one)
225# cause a rename/rename(2to1) conflict
226# (Related to testcases 1c and 7b)
227# Commit O. z/{b,c}, y/{d,e}
228# Commit A. x/{b,c}, y/{d,e,m,wham_1}
229# Commit B. z/{b,c,n,wham_2}, x/{d,e}
230# Expected: x/{b,c,d,e,m,n}, CONFLICT:(y/wham_1 & z/wham_2 -> x/wham)
231# Note: y/m & z/n should definitely move into x. By the same token, both
232# y/wham_1 & z/wham_2 should too...giving us a conflict.
233
da1e295e 234test_setup_1d () {
04550ab5
EN
235 test_create_repo 1d &&
236 (
237 cd 1d &&
238
239 mkdir z &&
240 echo b >z/b &&
241 echo c >z/c &&
242 mkdir y &&
243 echo d >y/d &&
244 echo e >y/e &&
245 git add z y &&
246 test_tick &&
247 git commit -m "O" &&
248
249 git branch O &&
250 git branch A &&
251 git branch B &&
252
253 git checkout A &&
254 git mv z x &&
255 echo m >y/m &&
256 echo wham1 >y/wham &&
257 git add y &&
258 test_tick &&
259 git commit -m "A" &&
260
261 git checkout B &&
262 git mv y x &&
263 echo n >z/n &&
264 echo wham2 >z/wham &&
265 git add z &&
266 test_tick &&
267 git commit -m "B"
268 )
da1e295e 269}
04550ab5 270
da1e295e
EN
271test_expect_success '1d: Directory renames cause a rename/rename(2to1) conflict' '
272 test_setup_1d &&
04550ab5
EN
273 (
274 cd 1d &&
275
276 git checkout A^0 &&
277
8c8e5bd6 278 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
e8eb99d4 279 test_i18ngrep "CONFLICT (\(.*\)/\1)" out &&
04550ab5
EN
280
281 git ls-files -s >out &&
282 test_line_count = 8 out &&
283 git ls-files -u >out &&
284 test_line_count = 2 out &&
285 git ls-files -o >out &&
bbafc9c4 286 test_line_count = 1 out &&
04550ab5
EN
287
288 git rev-parse >actual \
289 :0:x/b :0:x/c :0:x/d :0:x/e :0:x/m :0:x/n &&
290 git rev-parse >expect \
291 O:z/b O:z/c O:y/d O:y/e A:y/m B:z/n &&
292 test_cmp expect actual &&
293
294 test_must_fail git rev-parse :0:x/wham &&
295 git rev-parse >actual \
296 :2:x/wham :3:x/wham &&
297 git rev-parse >expect \
298 A:y/wham B:z/wham &&
299 test_cmp expect actual &&
300
bbafc9c4
EN
301 # Test that the two-way merge in x/wham is as expected
302 git cat-file -p :2:x/wham >expect &&
303 git cat-file -p :3:x/wham >other &&
304 >empty &&
305 test_must_fail git merge-file \
306 -L "HEAD" \
307 -L "" \
308 -L "B^0" \
309 expect empty other &&
310 test_cmp expect x/wham
04550ab5
EN
311 )
312'
313
314# Testcase 1e, Renamed directory, with all filenames being renamed too
792e1371 315# (Related to testcases 9f & 9g)
04550ab5
EN
316# Commit O: z/{oldb,oldc}
317# Commit A: y/{newb,newc}
318# Commit B: z/{oldb,oldc,d}
319# Expected: y/{newb,newc,d}
320
da1e295e 321test_setup_1e () {
04550ab5
EN
322 test_create_repo 1e &&
323 (
324 cd 1e &&
325
326 mkdir z &&
327 echo b >z/oldb &&
328 echo c >z/oldc &&
329 git add z &&
330 test_tick &&
331 git commit -m "O" &&
332
333 git branch O &&
334 git branch A &&
335 git branch B &&
336
337 git checkout A &&
338 mkdir y &&
339 git mv z/oldb y/newb &&
340 git mv z/oldc y/newc &&
341 test_tick &&
342 git commit -m "A" &&
343
344 git checkout B &&
345 echo d >z/d &&
346 git add z/d &&
347 test_tick &&
348 git commit -m "B"
349 )
da1e295e 350}
04550ab5 351
da1e295e
EN
352test_expect_success '1e: Renamed directory, with all files being renamed too' '
353 test_setup_1e &&
04550ab5
EN
354 (
355 cd 1e &&
356
357 git checkout A^0 &&
358
8c8e5bd6 359 git -c merge.directoryRenames=true merge -s recursive B^0 &&
04550ab5
EN
360
361 git ls-files -s >out &&
362 test_line_count = 3 out &&
363
364 git rev-parse >actual \
365 HEAD:y/newb HEAD:y/newc HEAD:y/d &&
366 git rev-parse >expect \
367 O:z/oldb O:z/oldc B:z/d &&
368 test_cmp expect actual &&
369 test_must_fail git rev-parse HEAD:z/d
370 )
371'
372
373# Testcase 1f, Split a directory into two other directories
374# (Related to testcases 3a, all of section 2, and all of section 4)
375# Commit O: z/{b,c,d,e,f}
376# Commit A: z/{b,c,d,e,f,g}
377# Commit B: y/{b,c}, x/{d,e,f}
378# Expected: y/{b,c}, x/{d,e,f,g}
379
da1e295e 380test_setup_1f () {
04550ab5
EN
381 test_create_repo 1f &&
382 (
383 cd 1f &&
384
385 mkdir z &&
386 echo b >z/b &&
387 echo c >z/c &&
388 echo d >z/d &&
389 echo e >z/e &&
390 echo f >z/f &&
391 git add z &&
392 test_tick &&
393 git commit -m "O" &&
394
395 git branch O &&
396 git branch A &&
397 git branch B &&
398
399 git checkout A &&
400 echo g >z/g &&
401 git add z/g &&
402 test_tick &&
403 git commit -m "A" &&
404
405 git checkout B &&
406 mkdir y &&
407 mkdir x &&
408 git mv z/b y/ &&
409 git mv z/c y/ &&
410 git mv z/d x/ &&
411 git mv z/e x/ &&
412 git mv z/f x/ &&
413 rmdir z &&
414 test_tick &&
415 git commit -m "B"
416 )
da1e295e 417}
04550ab5 418
da1e295e
EN
419test_expect_success '1f: Split a directory into two other directories' '
420 test_setup_1f &&
04550ab5
EN
421 (
422 cd 1f &&
423
424 git checkout A^0 &&
425
8c8e5bd6 426 git -c merge.directoryRenames=true merge -s recursive B^0 &&
04550ab5
EN
427
428 git ls-files -s >out &&
429 test_line_count = 6 out &&
430
431 git rev-parse >actual \
432 HEAD:y/b HEAD:y/c HEAD:x/d HEAD:x/e HEAD:x/f HEAD:x/g &&
433 git rev-parse >expect \
434 O:z/b O:z/c O:z/d O:z/e O:z/f A:z/g &&
435 test_cmp expect actual &&
436 test_path_is_missing z/g &&
437 test_must_fail git rev-parse HEAD:z/g
438 )
439'
440
441###########################################################################
442# Rules suggested by testcases in section 1:
443#
444# We should still detect the directory rename even if it wasn't just
445# the directory renamed, but the files within it. (see 1b)
446#
447# If renames split a directory into two or more others, the directory
448# with the most renames, "wins" (see 1c). However, see the testcases
449# in section 2, plus testcases 3a and 4a.
450###########################################################################
451
509555d8
EN
452
453###########################################################################
454# SECTION 2: Split into multiple directories, with equal number of paths
455#
456# Explore the splitting-a-directory rules a bit; what happens in the
457# edge cases?
458#
459# Note that there is a closely related case of a directory not being
460# split on either side of history, but being renamed differently on
461# each side. See testcase 8e for that.
462###########################################################################
463
464# Testcase 2a, Directory split into two on one side, with equal numbers of paths
465# Commit O: z/{b,c}
466# Commit A: y/b, w/c
467# Commit B: z/{b,c,d}
468# Expected: y/b, w/c, z/d, with warning about z/ -> (y/ vs. w/) conflict
da1e295e 469test_setup_2a () {
509555d8
EN
470 test_create_repo 2a &&
471 (
472 cd 2a &&
473
474 mkdir z &&
475 echo b >z/b &&
476 echo c >z/c &&
477 git add z &&
478 test_tick &&
479 git commit -m "O" &&
480
481 git branch O &&
482 git branch A &&
483 git branch B &&
484
485 git checkout A &&
486 mkdir y &&
487 mkdir w &&
488 git mv z/b y/ &&
489 git mv z/c w/ &&
490 test_tick &&
491 git commit -m "A" &&
492
493 git checkout B &&
494 echo d >z/d &&
495 git add z/d &&
496 test_tick &&
497 git commit -m "B"
498 )
da1e295e 499}
509555d8 500
da1e295e
EN
501test_expect_success '2a: Directory split into two on one side, with equal numbers of paths' '
502 test_setup_2a &&
509555d8
EN
503 (
504 cd 2a &&
505
506 git checkout A^0 &&
507
8c8e5bd6 508 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
509555d8
EN
509 test_i18ngrep "CONFLICT.*directory rename split" out &&
510
511 git ls-files -s >out &&
512 test_line_count = 3 out &&
513 git ls-files -u >out &&
514 test_line_count = 0 out &&
515 git ls-files -o >out &&
516 test_line_count = 1 out &&
517
518 git rev-parse >actual \
519 :0:y/b :0:w/c :0:z/d &&
520 git rev-parse >expect \
521 O:z/b O:z/c B:z/d &&
522 test_cmp expect actual
523 )
524'
525
526# Testcase 2b, Directory split into two on one side, with equal numbers of paths
527# Commit O: z/{b,c}
528# Commit A: y/b, w/c
529# Commit B: z/{b,c}, x/d
530# Expected: y/b, w/c, x/d; No warning about z/ -> (y/ vs. w/) conflict
da1e295e 531test_setup_2b () {
509555d8
EN
532 test_create_repo 2b &&
533 (
534 cd 2b &&
535
536 mkdir z &&
537 echo b >z/b &&
538 echo c >z/c &&
539 git add z &&
540 test_tick &&
541 git commit -m "O" &&
542
543 git branch O &&
544 git branch A &&
545 git branch B &&
546
547 git checkout A &&
548 mkdir y &&
549 mkdir w &&
550 git mv z/b y/ &&
551 git mv z/c w/ &&
552 test_tick &&
553 git commit -m "A" &&
554
555 git checkout B &&
556 mkdir x &&
557 echo d >x/d &&
558 git add x/d &&
559 test_tick &&
560 git commit -m "B"
561 )
da1e295e 562}
509555d8 563
da1e295e
EN
564test_expect_success '2b: Directory split into two on one side, with equal numbers of paths' '
565 test_setup_2b &&
509555d8
EN
566 (
567 cd 2b &&
568
569 git checkout A^0 &&
570
8c8e5bd6 571 git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
509555d8
EN
572
573 git ls-files -s >out &&
574 test_line_count = 3 out &&
575 git ls-files -u >out &&
576 test_line_count = 0 out &&
577 git ls-files -o >out &&
578 test_line_count = 1 out &&
579
580 git rev-parse >actual \
581 :0:y/b :0:w/c :0:x/d &&
582 git rev-parse >expect \
583 O:z/b O:z/c B:x/d &&
584 test_cmp expect actual &&
585 test_i18ngrep ! "CONFLICT.*directory rename split" out
586 )
587'
588
589###########################################################################
590# Rules suggested by section 2:
591#
592# None; the rule was already covered in section 1. These testcases are
593# here just to make sure the conflict resolution and necessary warning
594# messages are handled correctly.
595###########################################################################
596
21b53733
EN
597
598###########################################################################
599# SECTION 3: Path in question is the source path for some rename already
600#
601# Combining cases from Section 1 and trying to handle them could lead to
602# directory renaming detection being over-applied. So, this section
603# provides some good testcases to check that the implementation doesn't go
604# too far.
605###########################################################################
606
607# Testcase 3a, Avoid implicit rename if involved as source on other side
792e1371 608# (Related to testcases 1c, 1f, and 9h)
21b53733
EN
609# Commit O: z/{b,c,d}
610# Commit A: z/{b,c,d} (no change)
611# Commit B: y/{b,c}, x/d
612# Expected: y/{b,c}, x/d
da1e295e 613test_setup_3a () {
21b53733
EN
614 test_create_repo 3a &&
615 (
616 cd 3a &&
617
618 mkdir z &&
619 echo b >z/b &&
620 echo c >z/c &&
621 echo d >z/d &&
622 git add z &&
623 test_tick &&
624 git commit -m "O" &&
625
626 git branch O &&
627 git branch A &&
628 git branch B &&
629
630 git checkout A &&
631 test_tick &&
632 git commit --allow-empty -m "A" &&
633
634 git checkout B &&
635 mkdir y &&
636 mkdir x &&
637 git mv z/b y/ &&
638 git mv z/c y/ &&
639 git mv z/d x/ &&
640 rmdir z &&
641 test_tick &&
642 git commit -m "B"
643 )
da1e295e 644}
21b53733 645
da1e295e
EN
646test_expect_success '3a: Avoid implicit rename if involved as source on other side' '
647 test_setup_3a &&
21b53733
EN
648 (
649 cd 3a &&
650
651 git checkout A^0 &&
652
8c8e5bd6 653 git -c merge.directoryRenames=true merge -s recursive B^0 &&
21b53733
EN
654
655 git ls-files -s >out &&
656 test_line_count = 3 out &&
657
658 git rev-parse >actual \
659 HEAD:y/b HEAD:y/c HEAD:x/d &&
660 git rev-parse >expect \
661 O:z/b O:z/c O:z/d &&
662 test_cmp expect actual
663 )
664'
665
666# Testcase 3b, Avoid implicit rename if involved as source on other side
667# (Related to testcases 5c and 7c, also kind of 1e and 1f)
668# Commit O: z/{b,c,d}
669# Commit A: y/{b,c}, x/d
670# Commit B: z/{b,c}, w/d
671# Expected: y/{b,c}, CONFLICT:(z/d -> x/d vs. w/d)
672# NOTE: We're particularly checking that since z/d is already involved as
673# a source in a file rename on the same side of history, that we don't
674# get it involved in directory rename detection. If it were, we might
675# end up with CONFLICT:(z/d -> y/d vs. x/d vs. w/d), i.e. a
676# rename/rename/rename(1to3) conflict, which is just weird.
da1e295e 677test_setup_3b () {
21b53733
EN
678 test_create_repo 3b &&
679 (
680 cd 3b &&
681
682 mkdir z &&
683 echo b >z/b &&
684 echo c >z/c &&
685 echo d >z/d &&
686 git add z &&
687 test_tick &&
688 git commit -m "O" &&
689
690 git branch O &&
691 git branch A &&
692 git branch B &&
693
694 git checkout A &&
695 mkdir y &&
696 mkdir x &&
697 git mv z/b y/ &&
698 git mv z/c y/ &&
699 git mv z/d x/ &&
700 rmdir z &&
701 test_tick &&
702 git commit -m "A" &&
703
704 git checkout B &&
705 mkdir w &&
706 git mv z/d w/ &&
707 test_tick &&
708 git commit -m "B"
709 )
da1e295e 710}
21b53733 711
da1e295e
EN
712test_expect_success '3b: Avoid implicit rename if involved as source on current side' '
713 test_setup_3b &&
21b53733
EN
714 (
715 cd 3b &&
716
717 git checkout A^0 &&
718
8c8e5bd6 719 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
21b53733
EN
720 test_i18ngrep CONFLICT.*rename/rename.*z/d.*x/d.*w/d out &&
721 test_i18ngrep ! CONFLICT.*rename/rename.*y/d out &&
722
723 git ls-files -s >out &&
724 test_line_count = 5 out &&
725 git ls-files -u >out &&
726 test_line_count = 3 out &&
727 git ls-files -o >out &&
728 test_line_count = 1 out &&
729
730 git rev-parse >actual \
731 :0:y/b :0:y/c :1:z/d :2:x/d :3:w/d &&
732 git rev-parse >expect \
733 O:z/b O:z/c O:z/d O:z/d O:z/d &&
734 test_cmp expect actual &&
735
736 test_path_is_missing z/d &&
737 git hash-object >actual \
738 x/d w/d &&
739 git rev-parse >expect \
740 O:z/d O:z/d &&
741 test_cmp expect actual
742 )
743'
744
745###########################################################################
746# Rules suggested by section 3:
747#
748# Avoid directory-rename-detection for a path, if that path is the source
749# of a rename on either side of a merge.
750###########################################################################
751
de632e4e
EN
752
753###########################################################################
754# SECTION 4: Partially renamed directory; still exists on both sides of merge
755#
756# What if we were to attempt to do directory rename detection when someone
757# "mostly" moved a directory but still left some files around, or,
7a40cf15 758# equivalently, fully renamed a directory in one commit and then recreated
de632e4e
EN
759# that directory in a later commit adding some new files and then tried to
760# merge?
761#
762# It's hard to divine user intent in these cases, because you can make an
763# argument that, depending on the intermediate history of the side being
764# merged, that some users will want files in that directory to
765# automatically be detected and renamed, while users with a different
766# intermediate history wouldn't want that rename to happen.
767#
768# I think that it is best to simply not have directory rename detection
769# apply to such cases. My reasoning for this is four-fold: (1) it's
770# easiest for users in general to figure out what happened if we don't
771# apply directory rename detection in any such case, (2) it's an easy rule
772# to explain ["We don't do directory rename detection if the directory
773# still exists on both sides of the merge"], (3) we can get some hairy
774# edge/corner cases that would be really confusing and possibly not even
775# representable in the index if we were to even try, and [related to 3] (4)
776# attempting to resolve this issue of divining user intent by examining
777# intermediate history goes against the spirit of three-way merges and is a
778# path towards crazy corner cases that are far more complex than what we're
779# already dealing with.
780#
781# Note that the wording of the rule ("We don't do directory rename
782# detection if the directory still exists on both sides of the merge.")
783# also excludes "renaming" of a directory into a subdirectory of itself
784# (e.g. /some/dir/* -> /some/dir/subdir/*). It may be possible to carve
785# out an exception for "renaming"-beneath-itself cases without opening
786# weird edge/corner cases for other partial directory renames, but for now
787# we are keeping the rule simple.
788#
789# This section contains a test for a partially-renamed-directory case.
790###########################################################################
791
792# Testcase 4a, Directory split, with original directory still present
793# (Related to testcase 1f)
794# Commit O: z/{b,c,d,e}
795# Commit A: y/{b,c,d}, z/e
796# Commit B: z/{b,c,d,e,f}
797# Expected: y/{b,c,d}, z/{e,f}
798# NOTE: Even though most files from z moved to y, we don't want f to follow.
799
da1e295e 800test_setup_4a () {
de632e4e
EN
801 test_create_repo 4a &&
802 (
803 cd 4a &&
804
805 mkdir z &&
806 echo b >z/b &&
807 echo c >z/c &&
808 echo d >z/d &&
809 echo e >z/e &&
810 git add z &&
811 test_tick &&
812 git commit -m "O" &&
813
814 git branch O &&
815 git branch A &&
816 git branch B &&
817
818 git checkout A &&
819 mkdir y &&
820 git mv z/b y/ &&
821 git mv z/c y/ &&
822 git mv z/d y/ &&
823 test_tick &&
824 git commit -m "A" &&
825
826 git checkout B &&
827 echo f >z/f &&
828 git add z/f &&
829 test_tick &&
830 git commit -m "B"
831 )
da1e295e 832}
de632e4e 833
da1e295e
EN
834test_expect_success '4a: Directory split, with original directory still present' '
835 test_setup_4a &&
de632e4e
EN
836 (
837 cd 4a &&
838
839 git checkout A^0 &&
840
8c8e5bd6 841 git -c merge.directoryRenames=true merge -s recursive B^0 &&
de632e4e
EN
842
843 git ls-files -s >out &&
844 test_line_count = 5 out &&
845 git ls-files -u >out &&
846 test_line_count = 0 out &&
847 git ls-files -o >out &&
848 test_line_count = 1 out &&
849
850 git rev-parse >actual \
851 HEAD:y/b HEAD:y/c HEAD:y/d HEAD:z/e HEAD:z/f &&
852 git rev-parse >expect \
853 O:z/b O:z/c O:z/d O:z/e B:z/f &&
854 test_cmp expect actual
855 )
856'
857
858###########################################################################
859# Rules suggested by section 4:
860#
861# Directory-rename-detection should be turned off for any directories (as
862# a source for renames) that exist on both sides of the merge. (The "as
863# a source for renames" clarification is due to cases like 1c where
864# the target directory exists on both sides and we do want the rename
865# detection.) But, sadly, see testcase 8b.
866###########################################################################
867
c449947a
EN
868
869###########################################################################
870# SECTION 5: Files/directories in the way of subset of to-be-renamed paths
871#
872# Implicitly renaming files due to a detected directory rename could run
873# into problems if there are files or directories in the way of the paths
874# we want to rename. Explore such cases in this section.
875###########################################################################
876
877# Testcase 5a, Merge directories, other side adds files to original and target
878# Commit O: z/{b,c}, y/d
879# Commit A: z/{b,c,e_1,f}, y/{d,e_2}
880# Commit B: y/{b,c,d}
881# Expected: z/e_1, y/{b,c,d,e_2,f} + CONFLICT warning
882# NOTE: While directory rename detection is active here causing z/f to
883# become y/f, we did not apply this for z/e_1 because that would
884# give us an add/add conflict for y/e_1 vs y/e_2. This problem with
885# this add/add, is that both versions of y/e are from the same side
886# of history, giving us no way to represent this conflict in the
887# index.
888
da1e295e 889test_setup_5a () {
c449947a
EN
890 test_create_repo 5a &&
891 (
892 cd 5a &&
893
894 mkdir z &&
895 echo b >z/b &&
896 echo c >z/c &&
897 mkdir y &&
898 echo d >y/d &&
899 git add z y &&
900 test_tick &&
901 git commit -m "O" &&
902
903 git branch O &&
904 git branch A &&
905 git branch B &&
906
907 git checkout A &&
908 echo e1 >z/e &&
909 echo f >z/f &&
910 echo e2 >y/e &&
911 git add z/e z/f y/e &&
912 test_tick &&
913 git commit -m "A" &&
914
915 git checkout B &&
916 git mv z/b y/ &&
917 git mv z/c y/ &&
918 rmdir z &&
919 test_tick &&
920 git commit -m "B"
921 )
da1e295e 922}
c449947a 923
da1e295e
EN
924test_expect_success '5a: Merge directories, other side adds files to original and target' '
925 test_setup_5a &&
c449947a
EN
926 (
927 cd 5a &&
928
929 git checkout A^0 &&
930
8c8e5bd6 931 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
c449947a
EN
932 test_i18ngrep "CONFLICT.*implicit dir rename" out &&
933
934 git ls-files -s >out &&
935 test_line_count = 6 out &&
936 git ls-files -u >out &&
937 test_line_count = 0 out &&
938 git ls-files -o >out &&
939 test_line_count = 1 out &&
940
941 git rev-parse >actual \
942 :0:y/b :0:y/c :0:y/d :0:y/e :0:z/e :0:y/f &&
943 git rev-parse >expect \
944 O:z/b O:z/c O:y/d A:y/e A:z/e A:z/f &&
945 test_cmp expect actual
946 )
947'
948
949# Testcase 5b, Rename/delete in order to get add/add/add conflict
950# (Related to testcase 8d; these may appear slightly inconsistent to users;
951# Also related to testcases 7d and 7e)
952# Commit O: z/{b,c,d_1}
953# Commit A: y/{b,c,d_2}
954# Commit B: z/{b,c,d_1,e}, y/d_3
955# Expected: y/{b,c,e}, CONFLICT(add/add: y/d_2 vs. y/d_3)
956# NOTE: If z/d_1 in commit B were to be involved in dir rename detection, as
7a40cf15 957# we normally would since z/ is being renamed to y/, then this would be
c449947a
EN
958# a rename/delete (z/d_1 -> y/d_1 vs. deleted) AND an add/add/add
959# conflict of y/d_1 vs. y/d_2 vs. y/d_3. Add/add/add is not
960# representable in the index, so the existence of y/d_3 needs to
961# cause us to bail on directory rename detection for that path, falling
962# back to git behavior without the directory rename detection.
963
da1e295e 964test_setup_5b () {
c449947a
EN
965 test_create_repo 5b &&
966 (
967 cd 5b &&
968
969 mkdir z &&
970 echo b >z/b &&
971 echo c >z/c &&
972 echo d1 >z/d &&
973 git add z &&
974 test_tick &&
975 git commit -m "O" &&
976
977 git branch O &&
978 git branch A &&
979 git branch B &&
980
981 git checkout A &&
982 git rm z/d &&
983 git mv z y &&
984 echo d2 >y/d &&
985 git add y/d &&
986 test_tick &&
987 git commit -m "A" &&
988
989 git checkout B &&
990 mkdir y &&
991 echo d3 >y/d &&
992 echo e >z/e &&
993 git add y/d z/e &&
994 test_tick &&
995 git commit -m "B"
996 )
da1e295e 997}
c449947a 998
da1e295e
EN
999test_expect_success '5b: Rename/delete in order to get add/add/add conflict' '
1000 test_setup_5b &&
c449947a
EN
1001 (
1002 cd 5b &&
1003
1004 git checkout A^0 &&
1005
8c8e5bd6 1006 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
c449947a
EN
1007 test_i18ngrep "CONFLICT (add/add).* y/d" out &&
1008
1009 git ls-files -s >out &&
1010 test_line_count = 5 out &&
1011 git ls-files -u >out &&
1012 test_line_count = 2 out &&
1013 git ls-files -o >out &&
1014 test_line_count = 1 out &&
1015
1016 git rev-parse >actual \
1017 :0:y/b :0:y/c :0:y/e :2:y/d :3:y/d &&
1018 git rev-parse >expect \
1019 O:z/b O:z/c B:z/e A:y/d B:y/d &&
1020 test_cmp expect actual &&
1021
1022 test_must_fail git rev-parse :1:y/d &&
1023 test_path_is_file y/d
1024 )
1025'
1026
1027# Testcase 5c, Transitive rename would cause rename/rename/rename/add/add/add
1028# (Directory rename detection would result in transitive rename vs.
1029# rename/rename(1to2) and turn it into a rename/rename(1to3). Further,
1030# rename paths conflict with separate adds on the other side)
1031# (Related to testcases 3b and 7c)
1032# Commit O: z/{b,c}, x/d_1
1033# Commit A: y/{b,c,d_2}, w/d_1
1034# Commit B: z/{b,c,d_1,e}, w/d_3, y/d_4
1035# Expected: A mess, but only a rename/rename(1to2)/add/add mess. Use the
1036# presence of y/d_4 in B to avoid doing transitive rename of
1037# x/d_1 -> z/d_1 -> y/d_1, so that the only paths we have at
1038# y/d are y/d_2 and y/d_4. We still do the move from z/e to y/e,
1039# though, because it doesn't have anything in the way.
1040
da1e295e 1041test_setup_5c () {
c449947a
EN
1042 test_create_repo 5c &&
1043 (
1044 cd 5c &&
1045
1046 mkdir z &&
1047 echo b >z/b &&
1048 echo c >z/c &&
1049 mkdir x &&
1050 echo d1 >x/d &&
1051 git add z x &&
1052 test_tick &&
1053 git commit -m "O" &&
1054
1055 git branch O &&
1056 git branch A &&
1057 git branch B &&
1058
1059 git checkout A &&
1060 git mv z y &&
1061 echo d2 >y/d &&
1062 git add y/d &&
1063 git mv x w &&
1064 test_tick &&
1065 git commit -m "A" &&
1066
1067 git checkout B &&
1068 git mv x/d z/ &&
1069 mkdir w &&
1070 mkdir y &&
1071 echo d3 >w/d &&
1072 echo d4 >y/d &&
1073 echo e >z/e &&
1074 git add w/ y/ z/e &&
1075 test_tick &&
1076 git commit -m "B"
1077 )
da1e295e 1078}
c449947a 1079
da1e295e
EN
1080test_expect_success '5c: Transitive rename would cause rename/rename/rename/add/add/add' '
1081 test_setup_5c &&
c449947a
EN
1082 (
1083 cd 5c &&
1084
1085 git checkout A^0 &&
1086
8c8e5bd6 1087 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
c449947a
EN
1088 test_i18ngrep "CONFLICT (rename/rename).*x/d.*w/d.*z/d" out &&
1089 test_i18ngrep "CONFLICT (add/add).* y/d" out &&
1090
1091 git ls-files -s >out &&
1092 test_line_count = 9 out &&
1093 git ls-files -u >out &&
1094 test_line_count = 6 out &&
1095 git ls-files -o >out &&
48c9cb9d 1096 test_line_count = 1 out &&
c449947a
EN
1097
1098 git rev-parse >actual \
1099 :0:y/b :0:y/c :0:y/e &&
1100 git rev-parse >expect \
1101 O:z/b O:z/c B:z/e &&
1102 test_cmp expect actual &&
1103
1104 test_must_fail git rev-parse :1:y/d &&
1105 git rev-parse >actual \
1106 :2:w/d :3:w/d :1:x/d :2:y/d :3:y/d :3:z/d &&
1107 git rev-parse >expect \
1108 O:x/d B:w/d O:x/d A:y/d B:y/d O:x/d &&
1109 test_cmp expect actual &&
1110
1111 git hash-object >actual \
48c9cb9d 1112 z/d &&
c449947a 1113 git rev-parse >expect \
48c9cb9d 1114 O:x/d &&
c449947a
EN
1115 test_cmp expect actual &&
1116 test_path_is_missing x/d &&
1117 test_path_is_file y/d &&
1118 grep -q "<<<<" y/d # conflict markers should be present
1119 )
1120'
1121
1122# Testcase 5d, Directory/file/file conflict due to directory rename
1123# Commit O: z/{b,c}
1124# Commit A: y/{b,c,d_1}
1125# Commit B: z/{b,c,d_2,f}, y/d/e
1126# Expected: y/{b,c,d/e,f}, z/d_2, CONFLICT(file/directory), y/d_1~HEAD
1127# Note: The fact that y/d/ exists in B makes us bail on directory rename
1128# detection for z/d_2, but that doesn't prevent us from applying the
1129# directory rename detection for z/f -> y/f.
1130
da1e295e 1131test_setup_5d () {
c449947a
EN
1132 test_create_repo 5d &&
1133 (
1134 cd 5d &&
1135
1136 mkdir z &&
1137 echo b >z/b &&
1138 echo c >z/c &&
1139 git add z &&
1140 test_tick &&
1141 git commit -m "O" &&
1142
1143 git branch O &&
1144 git branch A &&
1145 git branch B &&
1146
1147 git checkout A &&
1148 git mv z y &&
1149 echo d1 >y/d &&
1150 git add y/d &&
1151 test_tick &&
1152 git commit -m "A" &&
1153
1154 git checkout B &&
1155 mkdir -p y/d &&
1156 echo e >y/d/e &&
1157 echo d2 >z/d &&
1158 echo f >z/f &&
1159 git add y/d/e z/d z/f &&
1160 test_tick &&
1161 git commit -m "B"
1162 )
da1e295e 1163}
c449947a 1164
da1e295e
EN
1165test_expect_success '5d: Directory/file/file conflict due to directory rename' '
1166 test_setup_5d &&
c449947a
EN
1167 (
1168 cd 5d &&
1169
1170 git checkout A^0 &&
1171
8c8e5bd6 1172 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
c449947a
EN
1173 test_i18ngrep "CONFLICT (file/directory).*y/d" out &&
1174
1175 git ls-files -s >out &&
1176 test_line_count = 6 out &&
1177 git ls-files -u >out &&
1178 test_line_count = 1 out &&
1179 git ls-files -o >out &&
ef527787
EN
1180 if test "$GIT_TEST_MERGE_ALGORITHM" = ort
1181 then
1182 test_line_count = 1 out &&
1183
1184 git rev-parse >actual \
1185 :0:y/b :0:y/c :0:z/d :0:y/f :2:y/d~HEAD :0:y/d/e
1186 else
1187 test_line_count = 2 out &&
1188
1189 git rev-parse >actual \
1190 :0:y/b :0:y/c :0:z/d :0:y/f :2:y/d :0:y/d/e
1191 fi &&
c449947a
EN
1192 git rev-parse >expect \
1193 O:z/b O:z/c B:z/d B:z/f A:y/d B:y/d/e &&
1194 test_cmp expect actual &&
1195
1196 git hash-object y/d~HEAD >actual &&
1197 git rev-parse A:y/d >expect &&
1198 test_cmp expect actual
1199 )
1200'
1201
1202###########################################################################
1203# Rules suggested by section 5:
1204#
1205# If a subset of to-be-renamed files have a file or directory in the way,
1206# "turn off" the directory rename for those specific sub-paths, falling
1207# back to old handling. But, sadly, see testcases 8a and 8b.
1208###########################################################################
1209
f3499876
EN
1210
1211###########################################################################
1212# SECTION 6: Same side of the merge was the one that did the rename
1213#
1214# It may sound obvious that you only want to apply implicit directory
1215# renames to directories if the _other_ side of history did the renaming.
1216# If you did make an implementation that didn't explicitly enforce this
1217# rule, the majority of cases that would fall under this section would
1218# also be solved by following the rules from the above sections. But
1219# there are still a few that stick out, so this section covers them just
1220# to make sure we also get them right.
1221###########################################################################
1222
1223# Testcase 6a, Tricky rename/delete
1224# Commit O: z/{b,c,d}
1225# Commit A: z/b
1226# Commit B: y/{b,c}, z/d
1227# Expected: y/b, CONFLICT(rename/delete, z/c -> y/c vs. NULL)
1228# Note: We're just checking here that the rename of z/b and z/c to put
1229# them under y/ doesn't accidentally catch z/d and make it look like
1230# it is also involved in a rename/delete conflict.
1231
da1e295e 1232test_setup_6a () {
f3499876
EN
1233 test_create_repo 6a &&
1234 (
1235 cd 6a &&
1236
1237 mkdir z &&
1238 echo b >z/b &&
1239 echo c >z/c &&
1240 echo d >z/d &&
1241 git add z &&
1242 test_tick &&
1243 git commit -m "O" &&
1244
1245 git branch O &&
1246 git branch A &&
1247 git branch B &&
1248
1249 git checkout A &&
1250 git rm z/c &&
1251 git rm z/d &&
1252 test_tick &&
1253 git commit -m "A" &&
1254
1255 git checkout B &&
1256 mkdir y &&
1257 git mv z/b y/ &&
1258 git mv z/c y/ &&
1259 test_tick &&
1260 git commit -m "B"
1261 )
da1e295e 1262}
f3499876 1263
da1e295e
EN
1264test_expect_success '6a: Tricky rename/delete' '
1265 test_setup_6a &&
f3499876
EN
1266 (
1267 cd 6a &&
1268
1269 git checkout A^0 &&
1270
8c8e5bd6 1271 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
f3499876
EN
1272 test_i18ngrep "CONFLICT (rename/delete).*z/c.*y/c" out &&
1273
1274 git ls-files -s >out &&
1275 test_line_count = 2 out &&
1276 git ls-files -u >out &&
1277 test_line_count = 1 out &&
1278 git ls-files -o >out &&
1279 test_line_count = 1 out &&
1280
1281 git rev-parse >actual \
1282 :0:y/b :3:y/c &&
1283 git rev-parse >expect \
1284 O:z/b O:z/c &&
1285 test_cmp expect actual
1286 )
1287'
1288
8536821d
EN
1289# Testcase 6b1, Same rename done on both sides
1290# (Related to testcase 6b2 and 8e)
1291# Commit O: z/{b,c,d,e}
1292# Commit A: y/{b,c,d}, x/e
1293# Commit B: y/{b,c,d}, z/{e,f}
1294# Expected: y/{b,c,d,f}, x/e
1295# Note: Directory rename detection says A renamed z/ -> y/ (3 paths renamed
1296# to y/ and only 1 renamed to x/), therefore the new file 'z/f' in B
1297# should be moved to 'y/f'.
1298#
1299# This is a bit of an edge case where any behavior might surprise users,
1300# whether that is treating A as renaming z/ -> y/, treating A as renaming
1301# z/ -> x/, or treating A as not doing any directory rename. However, I
1302# think this answer is the least confusing and most consistent with the
1303# rules elsewhere.
1304#
1305# A note about z/ -> x/, since it may not be clear how that could come
1306# about: If we were to ignore files renamed by both sides
1307# (i.e. z/{b,c,d}), as directory rename detection did in git-2.18 thru
1308# at least git-2.28, then we would note there are no renames from z/ to
1309# y/ and one rename from z/ to x/ and thus come to the conclusion that
1310# A renamed z/ -> x/. This seems more confusing for end users than a
1311# rename of z/ to y/, it makes directory rename detection behavior
1312# harder for them to predict. As such, we modified the rule, changed
1313# the behavior on testcases 6b2 and 8e, and introduced this 6b1 testcase.
1314
1315test_setup_6b1 () {
1316 test_create_repo 6b1 &&
1317 (
1318 cd 6b1 &&
1319
1320 mkdir z &&
1321 echo b >z/b &&
1322 echo c >z/c &&
1323 echo d >z/d &&
1324 echo e >z/e &&
1325 git add z &&
1326 test_tick &&
1327 git commit -m "O" &&
1328
1329 git branch O &&
1330 git branch A &&
1331 git branch B &&
1332
1333 git checkout A &&
1334 git mv z y &&
1335 mkdir x &&
1336 git mv y/e x/e &&
1337 test_tick &&
1338 git commit -m "A" &&
1339
1340 git checkout B &&
1341 git mv z y &&
1342 mkdir z &&
1343 git mv y/e z/e &&
1344 echo f >z/f &&
1345 git add z/f &&
1346 test_tick &&
1347 git commit -m "B"
1348 )
1349}
1350
f06481f1 1351test_expect_merge_algorithm failure success '6b1: Same renames done on both sides, plus another rename' '
8536821d
EN
1352 test_setup_6b1 &&
1353 (
1354 cd 6b1 &&
1355
1356 git checkout A^0 &&
1357
1358 git -c merge.directoryRenames=true merge -s recursive B^0 &&
1359
1360 git ls-files -s >out &&
1361 test_line_count = 5 out &&
1362 git ls-files -u >out &&
1363 test_line_count = 0 out &&
1364 git ls-files -o >out &&
1365 test_line_count = 1 out &&
1366
1367 git rev-parse >actual \
1368 HEAD:y/b HEAD:y/c HEAD:y/d HEAD:x/e HEAD:y/f &&
1369 git rev-parse >expect \
1370 O:z/b O:z/c O:z/d O:z/e B:z/f &&
1371 test_cmp expect actual
1372 )
1373'
1374
1375# Testcase 6b2, Same rename done on both sides
f3499876
EN
1376# (Related to testcases 6c and 8e)
1377# Commit O: z/{b,c}
1378# Commit A: y/{b,c}
1379# Commit B: y/{b,c}, z/d
8536821d
EN
1380# Expected: y/{b,c,d}
1381# Alternate: y/{b,c}, z/d
1382# Note: Directory rename detection says A renamed z/ -> y/, therefore the new
1383# file 'z/d' in B should be moved to 'y/d'.
1384#
1385# We could potentially ignore the renames of z/{b,c} on side A since
1386# those were renamed on both sides. However, it's a bit of a corner
1387# case because what if there was also a z/e that side A moved to x/e
1388# and side B left alone? If we used the "ignore renames done on both
1389# sides" logic, then we'd compute that A renamed z/ -> x/, and move
1390# z/d to x/d. That seems more surprising and uglier than allowing
1391# the z/ -> y/ rename.
1392
1393test_setup_6b2 () {
1394 test_create_repo 6b2 &&
f3499876 1395 (
8536821d 1396 cd 6b2 &&
f3499876
EN
1397
1398 mkdir z &&
1399 echo b >z/b &&
1400 echo c >z/c &&
1401 git add z &&
1402 test_tick &&
1403 git commit -m "O" &&
1404
1405 git branch O &&
1406 git branch A &&
1407 git branch B &&
1408
1409 git checkout A &&
1410 git mv z y &&
1411 test_tick &&
1412 git commit -m "A" &&
1413
1414 git checkout B &&
1415 git mv z y &&
1416 mkdir z &&
1417 echo d >z/d &&
1418 git add z/d &&
1419 test_tick &&
1420 git commit -m "B"
1421 )
da1e295e 1422}
f3499876 1423
f06481f1 1424test_expect_merge_algorithm failure success '6b2: Same rename done on both sides' '
8536821d 1425 test_setup_6b2 &&
f3499876 1426 (
8536821d 1427 cd 6b2 &&
f3499876
EN
1428
1429 git checkout A^0 &&
1430
8c8e5bd6 1431 git -c merge.directoryRenames=true merge -s recursive B^0 &&
f3499876
EN
1432
1433 git ls-files -s >out &&
1434 test_line_count = 3 out &&
1435 git ls-files -u >out &&
1436 test_line_count = 0 out &&
1437 git ls-files -o >out &&
1438 test_line_count = 1 out &&
1439
1440 git rev-parse >actual \
8536821d 1441 HEAD:y/b HEAD:y/c HEAD:y/d &&
f3499876
EN
1442 git rev-parse >expect \
1443 O:z/b O:z/c B:z/d &&
1444 test_cmp expect actual
1445 )
1446'
1447
1448# Testcase 6c, Rename only done on same side
8536821d 1449# (Related to testcases 6b1, 6b2, and 8e)
f3499876
EN
1450# Commit O: z/{b,c}
1451# Commit A: z/{b,c} (no change)
1452# Commit B: y/{b,c}, z/d
1453# Expected: y/{b,c}, z/d
1454# NOTE: Seems obvious, but just checking that the implementation doesn't
1455# "accidentally detect a rename" and give us y/{b,c,d}.
1456
da1e295e 1457test_setup_6c () {
f3499876
EN
1458 test_create_repo 6c &&
1459 (
1460 cd 6c &&
1461
1462 mkdir z &&
1463 echo b >z/b &&
1464 echo c >z/c &&
1465 git add z &&
1466 test_tick &&
1467 git commit -m "O" &&
1468
1469 git branch O &&
1470 git branch A &&
1471 git branch B &&
1472
1473 git checkout A &&
1474 test_tick &&
1475 git commit --allow-empty -m "A" &&
1476
1477 git checkout B &&
1478 git mv z y &&
1479 mkdir z &&
1480 echo d >z/d &&
1481 git add z/d &&
1482 test_tick &&
1483 git commit -m "B"
1484 )
da1e295e 1485}
f3499876 1486
da1e295e
EN
1487test_expect_success '6c: Rename only done on same side' '
1488 test_setup_6c &&
f3499876
EN
1489 (
1490 cd 6c &&
1491
1492 git checkout A^0 &&
1493
8c8e5bd6 1494 git -c merge.directoryRenames=true merge -s recursive B^0 &&
f3499876
EN
1495
1496 git ls-files -s >out &&
1497 test_line_count = 3 out &&
1498 git ls-files -u >out &&
1499 test_line_count = 0 out &&
1500 git ls-files -o >out &&
1501 test_line_count = 1 out &&
1502
1503 git rev-parse >actual \
1504 HEAD:y/b HEAD:y/c HEAD:z/d &&
1505 git rev-parse >expect \
1506 O:z/b O:z/c B:z/d &&
1507 test_cmp expect actual
1508 )
1509'
1510
1511# Testcase 6d, We don't always want transitive renaming
1512# (Related to testcase 1c)
1513# Commit O: z/{b,c}, x/d
1514# Commit A: z/{b,c}, x/d (no change)
1515# Commit B: y/{b,c}, z/d
1516# Expected: y/{b,c}, z/d
1517# NOTE: Again, this seems obvious but just checking that the implementation
1518# doesn't "accidentally detect a rename" and give us y/{b,c,d}.
1519
da1e295e 1520test_setup_6d () {
f3499876
EN
1521 test_create_repo 6d &&
1522 (
1523 cd 6d &&
1524
1525 mkdir z &&
1526 echo b >z/b &&
1527 echo c >z/c &&
1528 mkdir x &&
1529 echo d >x/d &&
1530 git add z x &&
1531 test_tick &&
1532 git commit -m "O" &&
1533
1534 git branch O &&
1535 git branch A &&
1536 git branch B &&
1537
1538 git checkout A &&
1539 test_tick &&
1540 git commit --allow-empty -m "A" &&
1541
1542 git checkout B &&
1543 git mv z y &&
1544 git mv x z &&
1545 test_tick &&
1546 git commit -m "B"
1547 )
da1e295e 1548}
f3499876 1549
da1e295e
EN
1550test_expect_success '6d: We do not always want transitive renaming' '
1551 test_setup_6d &&
f3499876
EN
1552 (
1553 cd 6d &&
1554
1555 git checkout A^0 &&
1556
8c8e5bd6 1557 git -c merge.directoryRenames=true merge -s recursive B^0 &&
f3499876
EN
1558
1559 git ls-files -s >out &&
1560 test_line_count = 3 out &&
1561 git ls-files -u >out &&
1562 test_line_count = 0 out &&
1563 git ls-files -o >out &&
1564 test_line_count = 1 out &&
1565
1566 git rev-parse >actual \
1567 HEAD:y/b HEAD:y/c HEAD:z/d &&
1568 git rev-parse >expect \
1569 O:z/b O:z/c O:x/d &&
1570 test_cmp expect actual
1571 )
1572'
1573
1574# Testcase 6e, Add/add from one-side
1575# Commit O: z/{b,c}
1576# Commit A: z/{b,c} (no change)
1577# Commit B: y/{b,c,d_1}, z/d_2
1578# Expected: y/{b,c,d_1}, z/d_2
1579# NOTE: Again, this seems obvious but just checking that the implementation
1580# doesn't "accidentally detect a rename" and give us y/{b,c} +
1581# add/add conflict on y/d_1 vs y/d_2.
1582
da1e295e 1583test_setup_6e () {
f3499876
EN
1584 test_create_repo 6e &&
1585 (
1586 cd 6e &&
1587
1588 mkdir z &&
1589 echo b >z/b &&
1590 echo c >z/c &&
1591 git add z &&
1592 test_tick &&
1593 git commit -m "O" &&
1594
1595 git branch O &&
1596 git branch A &&
1597 git branch B &&
1598
1599 git checkout A &&
1600 test_tick &&
1601 git commit --allow-empty -m "A" &&
1602
1603 git checkout B &&
1604 git mv z y &&
1605 echo d1 > y/d &&
1606 mkdir z &&
1607 echo d2 > z/d &&
1608 git add y/d z/d &&
1609 test_tick &&
1610 git commit -m "B"
1611 )
da1e295e 1612}
f3499876 1613
da1e295e
EN
1614test_expect_success '6e: Add/add from one side' '
1615 test_setup_6e &&
f3499876
EN
1616 (
1617 cd 6e &&
1618
1619 git checkout A^0 &&
1620
8c8e5bd6 1621 git -c merge.directoryRenames=true merge -s recursive B^0 &&
f3499876
EN
1622
1623 git ls-files -s >out &&
1624 test_line_count = 4 out &&
1625 git ls-files -u >out &&
1626 test_line_count = 0 out &&
1627 git ls-files -o >out &&
1628 test_line_count = 1 out &&
1629
1630 git rev-parse >actual \
1631 HEAD:y/b HEAD:y/c HEAD:y/d HEAD:z/d &&
1632 git rev-parse >expect \
1633 O:z/b O:z/c B:y/d B:z/d &&
1634 test_cmp expect actual
1635 )
1636'
1637
1638###########################################################################
1639# Rules suggested by section 6:
1640#
1641# Only apply implicit directory renames to directories if the other
1642# side of history is the one doing the renaming.
1643###########################################################################
1644
f95de960
EN
1645
1646###########################################################################
1647# SECTION 7: More involved Edge/Corner cases
1648#
1649# The ruleset we have generated in the above sections seems to provide
1650# well-defined merges. But can we find edge/corner cases that either (a)
1651# are harder for users to understand, or (b) have a resolution that is
1652# non-intuitive or suboptimal?
1653#
1654# The testcases in this section dive into cases that I've tried to craft in
1655# a way to find some that might be surprising to users or difficult for
1656# them to understand (the next section will look at non-intuitive or
1657# suboptimal merge results). Some of the testcases are similar to ones
1658# from past sections, but have been simplified to try to highlight error
1659# messages using a "modified" path (due to the directory rename). Are
1660# users okay with these?
1661#
1662# In my opinion, testcases that are difficult to understand from this
1663# section is due to difficulty in the testcase rather than the directory
1664# renaming (similar to how t6042 and t6036 have difficult resolutions due
1665# to the problem setup itself being complex). And I don't think the
1666# error messages are a problem.
1667#
1668# On the other hand, the testcases in section 8 worry me slightly more...
1669###########################################################################
1670
1671# Testcase 7a, rename-dir vs. rename-dir (NOT split evenly) PLUS add-other-file
1672# Commit O: z/{b,c}
1673# Commit A: y/{b,c}
1674# Commit B: w/b, x/c, z/d
1675# Expected: y/d, CONFLICT(rename/rename for both z/b and z/c)
1676# NOTE: There's a rename of z/ here, y/ has more renames, so z/d -> y/d.
1677
da1e295e 1678test_setup_7a () {
f95de960
EN
1679 test_create_repo 7a &&
1680 (
1681 cd 7a &&
1682
1683 mkdir z &&
1684 echo b >z/b &&
1685 echo c >z/c &&
1686 git add z &&
1687 test_tick &&
1688 git commit -m "O" &&
1689
1690 git branch O &&
1691 git branch A &&
1692 git branch B &&
1693
1694 git checkout A &&
1695 git mv z y &&
1696 test_tick &&
1697 git commit -m "A" &&
1698
1699 git checkout B &&
1700 mkdir w &&
1701 mkdir x &&
1702 git mv z/b w/ &&
1703 git mv z/c x/ &&
1704 echo d > z/d &&
1705 git add z/d &&
1706 test_tick &&
1707 git commit -m "B"
1708 )
da1e295e 1709}
f95de960 1710
da1e295e
EN
1711test_expect_success '7a: rename-dir vs. rename-dir (NOT split evenly) PLUS add-other-file' '
1712 test_setup_7a &&
f95de960
EN
1713 (
1714 cd 7a &&
1715
1716 git checkout A^0 &&
1717
8c8e5bd6 1718 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
f95de960
EN
1719 test_i18ngrep "CONFLICT (rename/rename).*z/b.*y/b.*w/b" out &&
1720 test_i18ngrep "CONFLICT (rename/rename).*z/c.*y/c.*x/c" out &&
1721
1722 git ls-files -s >out &&
1723 test_line_count = 7 out &&
1724 git ls-files -u >out &&
1725 test_line_count = 6 out &&
1726 git ls-files -o >out &&
1727 test_line_count = 1 out &&
1728
1729 git rev-parse >actual \
1730 :1:z/b :2:y/b :3:w/b :1:z/c :2:y/c :3:x/c :0:y/d &&
1731 git rev-parse >expect \
1732 O:z/b O:z/b O:z/b O:z/c O:z/c O:z/c B:z/d &&
1733 test_cmp expect actual &&
1734
1735 git hash-object >actual \
1736 y/b w/b y/c x/c &&
1737 git rev-parse >expect \
1738 O:z/b O:z/b O:z/c O:z/c &&
1739 test_cmp expect actual
1740 )
1741'
1742
1743# Testcase 7b, rename/rename(2to1), but only due to transitive rename
1744# (Related to testcase 1d)
1745# Commit O: z/{b,c}, x/d_1, w/d_2
1746# Commit A: y/{b,c,d_2}, x/d_1
1747# Commit B: z/{b,c,d_1}, w/d_2
1748# Expected: y/{b,c}, CONFLICT(rename/rename(2to1): x/d_1, w/d_2 -> y_d)
1749
da1e295e 1750test_setup_7b () {
f95de960
EN
1751 test_create_repo 7b &&
1752 (
1753 cd 7b &&
1754
1755 mkdir z &&
1756 mkdir x &&
1757 mkdir w &&
1758 echo b >z/b &&
1759 echo c >z/c &&
1760 echo d1 > x/d &&
1761 echo d2 > w/d &&
1762 git add z x w &&
1763 test_tick &&
1764 git commit -m "O" &&
1765
1766 git branch O &&
1767 git branch A &&
1768 git branch B &&
1769
1770 git checkout A &&
1771 git mv z y &&
1772 git mv w/d y/ &&
1773 test_tick &&
1774 git commit -m "A" &&
1775
1776 git checkout B &&
1777 git mv x/d z/ &&
1778 rmdir x &&
1779 test_tick &&
1780 git commit -m "B"
1781 )
da1e295e 1782}
f95de960 1783
da1e295e
EN
1784test_expect_success '7b: rename/rename(2to1), but only due to transitive rename' '
1785 test_setup_7b &&
f95de960
EN
1786 (
1787 cd 7b &&
1788
1789 git checkout A^0 &&
1790
8c8e5bd6 1791 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
e8eb99d4 1792 test_i18ngrep "CONFLICT (\(.*\)/\1)" out &&
f95de960
EN
1793
1794 git ls-files -s >out &&
1795 test_line_count = 4 out &&
1796 git ls-files -u >out &&
1797 test_line_count = 2 out &&
1798 git ls-files -o >out &&
bbafc9c4 1799 test_line_count = 1 out &&
f95de960
EN
1800
1801 git rev-parse >actual \
1802 :0:y/b :0:y/c :2:y/d :3:y/d &&
1803 git rev-parse >expect \
1804 O:z/b O:z/c O:w/d O:x/d &&
1805 test_cmp expect actual &&
1806
bbafc9c4
EN
1807 # Test that the two-way merge in y/d is as expected
1808 git cat-file -p :2:y/d >expect &&
1809 git cat-file -p :3:y/d >other &&
1810 >empty &&
1811 test_must_fail git merge-file \
1812 -L "HEAD" \
1813 -L "" \
1814 -L "B^0" \
1815 expect empty other &&
1816 test_cmp expect y/d
f95de960
EN
1817 )
1818'
1819
1820# Testcase 7c, rename/rename(1to...2or3); transitive rename may add complexity
1821# (Related to testcases 3b and 5c)
1822# Commit O: z/{b,c}, x/d
1823# Commit A: y/{b,c}, w/d
1824# Commit B: z/{b,c,d}
1825# Expected: y/{b,c}, CONFLICT(x/d -> w/d vs. y/d)
1826# NOTE: z/ was renamed to y/ so we do want to report
1827# neither CONFLICT(x/d -> w/d vs. z/d)
1828# nor CONFLiCT x/d -> w/d vs. y/d vs. z/d)
1829
da1e295e 1830test_setup_7c () {
f95de960
EN
1831 test_create_repo 7c &&
1832 (
1833 cd 7c &&
1834
1835 mkdir z &&
1836 echo b >z/b &&
1837 echo c >z/c &&
1838 mkdir x &&
1839 echo d >x/d &&
1840 git add z x &&
1841 test_tick &&
1842 git commit -m "O" &&
1843
1844 git branch O &&
1845 git branch A &&
1846 git branch B &&
1847
1848 git checkout A &&
1849 git mv z y &&
1850 git mv x w &&
1851 test_tick &&
1852 git commit -m "A" &&
1853
1854 git checkout B &&
1855 git mv x/d z/ &&
1856 rmdir x &&
1857 test_tick &&
1858 git commit -m "B"
1859 )
da1e295e 1860}
f95de960 1861
da1e295e
EN
1862test_expect_success '7c: rename/rename(1to...2or3); transitive rename may add complexity' '
1863 test_setup_7c &&
f95de960
EN
1864 (
1865 cd 7c &&
1866
1867 git checkout A^0 &&
1868
8c8e5bd6 1869 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
f95de960
EN
1870 test_i18ngrep "CONFLICT (rename/rename).*x/d.*w/d.*y/d" out &&
1871
1872 git ls-files -s >out &&
1873 test_line_count = 5 out &&
1874 git ls-files -u >out &&
1875 test_line_count = 3 out &&
1876 git ls-files -o >out &&
1877 test_line_count = 1 out &&
1878
1879 git rev-parse >actual \
1880 :0:y/b :0:y/c :1:x/d :2:w/d :3:y/d &&
1881 git rev-parse >expect \
1882 O:z/b O:z/c O:x/d O:x/d O:x/d &&
1883 test_cmp expect actual
1884 )
1885'
1886
1887# Testcase 7d, transitive rename involved in rename/delete; how is it reported?
1888# (Related somewhat to testcases 5b and 8d)
1889# Commit O: z/{b,c}, x/d
1890# Commit A: y/{b,c}
1891# Commit B: z/{b,c,d}
1892# Expected: y/{b,c}, CONFLICT(delete x/d vs rename to y/d)
1893# NOTE: z->y so NOT CONFLICT(delete x/d vs rename to z/d)
1894
da1e295e 1895test_setup_7d () {
f95de960
EN
1896 test_create_repo 7d &&
1897 (
1898 cd 7d &&
1899
1900 mkdir z &&
1901 echo b >z/b &&
1902 echo c >z/c &&
1903 mkdir x &&
1904 echo d >x/d &&
1905 git add z x &&
1906 test_tick &&
1907 git commit -m "O" &&
1908
1909 git branch O &&
1910 git branch A &&
1911 git branch B &&
1912
1913 git checkout A &&
1914 git mv z y &&
1915 git rm -rf x &&
1916 test_tick &&
1917 git commit -m "A" &&
1918
1919 git checkout B &&
1920 git mv x/d z/ &&
1921 rmdir x &&
1922 test_tick &&
1923 git commit -m "B"
1924 )
da1e295e 1925}
f95de960 1926
da1e295e
EN
1927test_expect_success '7d: transitive rename involved in rename/delete; how is it reported?' '
1928 test_setup_7d &&
f95de960
EN
1929 (
1930 cd 7d &&
1931
1932 git checkout A^0 &&
1933
8c8e5bd6 1934 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
f95de960
EN
1935 test_i18ngrep "CONFLICT (rename/delete).*x/d.*y/d" out &&
1936
1937 git ls-files -s >out &&
1938 test_line_count = 3 out &&
1939 git ls-files -u >out &&
1940 test_line_count = 1 out &&
1941 git ls-files -o >out &&
1942 test_line_count = 1 out &&
1943
1944 git rev-parse >actual \
1945 :0:y/b :0:y/c :3:y/d &&
1946 git rev-parse >expect \
1947 O:z/b O:z/c O:x/d &&
1948 test_cmp expect actual
1949 )
1950'
1951
1952# Testcase 7e, transitive rename in rename/delete AND dirs in the way
1953# (Very similar to 'both rename source and destination involved in D/F conflict' from t6022-merge-rename.sh)
1954# (Also related to testcases 9c and 9d)
1955# Commit O: z/{b,c}, x/d_1
1956# Commit A: y/{b,c,d/g}, x/d/f
1957# Commit B: z/{b,c,d_1}
1958# Expected: rename/delete(x/d_1->y/d_1 vs. None) + D/F conflict on y/d
1959# y/{b,c,d/g}, y/d_1~B^0, x/d/f
1960
1961# NOTE: The main path of interest here is d_1 and where it ends up, but
1962# this is actually a case that has two potential directory renames
1963# involved and D/F conflict(s), so it makes sense to walk through
1964# each step.
1965#
1966# Commit A renames z/ -> y/. Thus everything that B adds to z/
1967# should be instead moved to y/. This gives us the D/F conflict on
1968# y/d because x/d_1 -> z/d_1 -> y/d_1 conflicts with y/d/g.
1969#
1970# Further, commit B renames x/ -> z/, thus everything A adds to x/
1971# should instead be moved to z/...BUT we removed z/ and renamed it
1972# to y/, so maybe everything should move not from x/ to z/, but
1973# from x/ to z/ to y/. Doing so might make sense from the logic so
1974# far, but note that commit A had both an x/ and a y/; it did the
1975# renaming of z/ to y/ and created x/d/f and it clearly made these
1976# things separate, so it doesn't make much sense to push these
1977# together. Doing so is what I'd call a doubly transitive rename;
1978# see testcases 9c and 9d for further discussion of this issue and
1979# how it's resolved.
1980
da1e295e 1981test_setup_7e () {
f95de960
EN
1982 test_create_repo 7e &&
1983 (
1984 cd 7e &&
1985
1986 mkdir z &&
1987 echo b >z/b &&
1988 echo c >z/c &&
1989 mkdir x &&
1990 echo d1 >x/d &&
1991 git add z x &&
1992 test_tick &&
1993 git commit -m "O" &&
1994
1995 git branch O &&
1996 git branch A &&
1997 git branch B &&
1998
1999 git checkout A &&
2000 git mv z y &&
2001 git rm x/d &&
2002 mkdir -p x/d &&
2003 mkdir -p y/d &&
2004 echo f >x/d/f &&
2005 echo g >y/d/g &&
2006 git add x/d/f y/d/g &&
2007 test_tick &&
2008 git commit -m "A" &&
2009
2010 git checkout B &&
2011 git mv x/d z/ &&
2012 rmdir x &&
2013 test_tick &&
2014 git commit -m "B"
2015 )
da1e295e 2016}
f95de960 2017
da1e295e
EN
2018test_expect_success '7e: transitive rename in rename/delete AND dirs in the way' '
2019 test_setup_7e &&
f95de960
EN
2020 (
2021 cd 7e &&
2022
2023 git checkout A^0 &&
2024
8c8e5bd6 2025 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
f95de960
EN
2026 test_i18ngrep "CONFLICT (rename/delete).*x/d.*y/d" out &&
2027
ef527787
EN
2028 if test "$GIT_TEST_MERGE_ALGORITHM" = ort
2029 then
2030 git ls-files -s >out &&
2031 test_line_count = 6 out &&
2032 git ls-files -u >out &&
2033 test_line_count = 2 out &&
2034 git ls-files -o >out &&
2035 test_line_count = 1 out &&
2036
2037 git rev-parse >actual \
2038 :0:x/d/f :0:y/d/g :0:y/b :0:y/c :1:y/d~B^0 :3:y/d~B^0 &&
2039 git rev-parse >expect \
2040 A:x/d/f A:y/d/g O:z/b O:z/c O:x/d O:x/d
2041 else
2042 git ls-files -s >out &&
2043 test_line_count = 5 out &&
2044 git ls-files -u >out &&
2045 test_line_count = 1 out &&
2046 git ls-files -o >out &&
2047 test_line_count = 2 out &&
2048
2049 git rev-parse >actual \
2050 :0:x/d/f :0:y/d/g :0:y/b :0:y/c :3:y/d &&
2051 git rev-parse >expect \
2052 A:x/d/f A:y/d/g O:z/b O:z/c O:x/d
2053 fi &&
f95de960
EN
2054 test_cmp expect actual &&
2055
2056 git hash-object y/d~B^0 >actual &&
2057 git rev-parse O:x/d >expect &&
2058 test_cmp expect actual
2059 )
2060'
2061
362ab315
EN
2062###########################################################################
2063# SECTION 8: Suboptimal merges
2064#
2065# As alluded to in the last section, the ruleset we have built up for
2066# detecting directory renames unfortunately has some special cases where it
2067# results in slightly suboptimal or non-intuitive behavior. This section
2068# explores these cases.
2069#
2070# To be fair, we already had non-intuitive or suboptimal behavior for most
2071# of these cases in git before introducing implicit directory rename
2072# detection, but it'd be nice if there was a modified ruleset out there
2073# that handled these cases a bit better.
2074###########################################################################
2075
2076# Testcase 8a, Dual-directory rename, one into the others' way
2077# Commit O. x/{a,b}, y/{c,d}
2078# Commit A. x/{a,b,e}, y/{c,d,f}
2079# Commit B. y/{a,b}, z/{c,d}
2080#
2081# Possible Resolutions:
2082# w/o dir-rename detection: y/{a,b,f}, z/{c,d}, x/e
2083# Currently expected: y/{a,b,e,f}, z/{c,d}
2084# Optimal: y/{a,b,e}, z/{c,d,f}
2085#
2086# Note: Both x and y got renamed and it'd be nice to detect both, and we do
2087# better with directory rename detection than git did without, but the
2088# simple rule from section 5 prevents me from handling this as optimally as
2089# we potentially could.
2090
da1e295e 2091test_setup_8a () {
362ab315
EN
2092 test_create_repo 8a &&
2093 (
2094 cd 8a &&
2095
2096 mkdir x &&
2097 mkdir y &&
2098 echo a >x/a &&
2099 echo b >x/b &&
2100 echo c >y/c &&
2101 echo d >y/d &&
2102 git add x y &&
2103 test_tick &&
2104 git commit -m "O" &&
2105
2106 git branch O &&
2107 git branch A &&
2108 git branch B &&
2109
2110 git checkout A &&
2111 echo e >x/e &&
2112 echo f >y/f &&
2113 git add x/e y/f &&
2114 test_tick &&
2115 git commit -m "A" &&
2116
2117 git checkout B &&
2118 git mv y z &&
2119 git mv x y &&
2120 test_tick &&
2121 git commit -m "B"
2122 )
da1e295e 2123}
362ab315 2124
da1e295e
EN
2125test_expect_success '8a: Dual-directory rename, one into the others way' '
2126 test_setup_8a &&
362ab315
EN
2127 (
2128 cd 8a &&
2129
2130 git checkout A^0 &&
2131
8c8e5bd6 2132 git -c merge.directoryRenames=true merge -s recursive B^0 &&
362ab315
EN
2133
2134 git ls-files -s >out &&
2135 test_line_count = 6 out &&
2136 git ls-files -u >out &&
2137 test_line_count = 0 out &&
2138 git ls-files -o >out &&
2139 test_line_count = 1 out &&
2140
2141 git rev-parse >actual \
2142 HEAD:y/a HEAD:y/b HEAD:y/e HEAD:y/f HEAD:z/c HEAD:z/d &&
2143 git rev-parse >expect \
2144 O:x/a O:x/b A:x/e A:y/f O:y/c O:y/d &&
2145 test_cmp expect actual
2146 )
2147'
2148
2149# Testcase 8b, Dual-directory rename, one into the others' way, with conflicting filenames
2150# Commit O. x/{a_1,b_1}, y/{a_2,b_2}
2151# Commit A. x/{a_1,b_1,e_1}, y/{a_2,b_2,e_2}
2152# Commit B. y/{a_1,b_1}, z/{a_2,b_2}
2153#
2154# w/o dir-rename detection: y/{a_1,b_1,e_2}, z/{a_2,b_2}, x/e_1
2155# Currently expected: <same>
2156# Scary: y/{a_1,b_1}, z/{a_2,b_2}, CONFLICT(add/add, e_1 vs. e_2)
2157# Optimal: y/{a_1,b_1,e_1}, z/{a_2,b_2,e_2}
2158#
2159# Note: Very similar to 8a, except instead of 'e' and 'f' in directories x and
2160# y, both are named 'e'. Without directory rename detection, neither file
2161# moves directories. Implement directory rename detection suboptimally, and
2162# you get an add/add conflict, but both files were added in commit A, so this
2163# is an add/add conflict where one side of history added both files --
2164# something we can't represent in the index. Obviously, we'd prefer the last
2165# resolution, but our previous rules are too coarse to allow it. Using both
2166# the rules from section 4 and section 5 save us from the Scary resolution,
2167# making us fall back to pre-directory-rename-detection behavior for both
2168# e_1 and e_2.
2169
da1e295e 2170test_setup_8b () {
362ab315
EN
2171 test_create_repo 8b &&
2172 (
2173 cd 8b &&
2174
2175 mkdir x &&
2176 mkdir y &&
2177 echo a1 >x/a &&
2178 echo b1 >x/b &&
2179 echo a2 >y/a &&
2180 echo b2 >y/b &&
2181 git add x y &&
2182 test_tick &&
2183 git commit -m "O" &&
2184
2185 git branch O &&
2186 git branch A &&
2187 git branch B &&
2188
2189 git checkout A &&
2190 echo e1 >x/e &&
2191 echo e2 >y/e &&
2192 git add x/e y/e &&
2193 test_tick &&
2194 git commit -m "A" &&
2195
2196 git checkout B &&
2197 git mv y z &&
2198 git mv x y &&
2199 test_tick &&
2200 git commit -m "B"
2201 )
da1e295e 2202}
362ab315 2203
da1e295e
EN
2204test_expect_success '8b: Dual-directory rename, one into the others way, with conflicting filenames' '
2205 test_setup_8b &&
362ab315
EN
2206 (
2207 cd 8b &&
2208
2209 git checkout A^0 &&
2210
8c8e5bd6 2211 git -c merge.directoryRenames=true merge -s recursive B^0 &&
362ab315
EN
2212
2213 git ls-files -s >out &&
2214 test_line_count = 6 out &&
2215 git ls-files -u >out &&
2216 test_line_count = 0 out &&
2217 git ls-files -o >out &&
2218 test_line_count = 1 out &&
2219
2220 git rev-parse >actual \
2221 HEAD:y/a HEAD:y/b HEAD:z/a HEAD:z/b HEAD:x/e HEAD:y/e &&
2222 git rev-parse >expect \
2223 O:x/a O:x/b O:y/a O:y/b A:x/e A:y/e &&
2224 test_cmp expect actual
2225 )
2226'
2227
6e7e027f
EN
2228# Testcase 8c, modify/delete or rename+modify/delete?
2229# (Related to testcases 5b, 8d, and 9h)
362ab315
EN
2230# Commit O: z/{b,c,d}
2231# Commit A: y/{b,c}
2232# Commit B: z/{b,c,d_modified,e}
6e7e027f 2233# Expected: y/{b,c,e}, CONFLICT(modify/delete: on z/d)
362ab315 2234#
6e7e027f
EN
2235# Note: It could easily be argued that the correct resolution here is
2236# y/{b,c,e}, CONFLICT(rename/delete: z/d -> y/d vs deleted)
7a40cf15 2237# and that the modified version of d should be present in y/ after
6e7e027f
EN
2238# the merge, just marked as conflicted. Indeed, I previously did
2239# argue that. But applying directory renames to the side of
2240# history where a file is merely modified results in spurious
2241# rename/rename(1to2) conflicts -- see testcase 9h. See also
2242# notes in 8d.
2243
da1e295e 2244test_setup_8c () {
362ab315
EN
2245 test_create_repo 8c &&
2246 (
2247 cd 8c &&
2248
2249 mkdir z &&
2250 echo b >z/b &&
2251 echo c >z/c &&
2252 test_seq 1 10 >z/d &&
2253 git add z &&
2254 test_tick &&
2255 git commit -m "O" &&
2256
2257 git branch O &&
2258 git branch A &&
2259 git branch B &&
2260
2261 git checkout A &&
2262 git rm z/d &&
2263 git mv z y &&
2264 test_tick &&
2265 git commit -m "A" &&
2266
2267 git checkout B &&
2268 echo 11 >z/d &&
2269 test_chmod +x z/d &&
2270 echo e >z/e &&
2271 git add z/d z/e &&
2272 test_tick &&
2273 git commit -m "B"
2274 )
da1e295e 2275}
362ab315 2276
da1e295e
EN
2277test_expect_success '8c: modify/delete or rename+modify/delete' '
2278 test_setup_8c &&
362ab315
EN
2279 (
2280 cd 8c &&
2281
2282 git checkout A^0 &&
2283
8c8e5bd6 2284 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
6e7e027f 2285 test_i18ngrep "CONFLICT (modify/delete).* z/d" out &&
362ab315
EN
2286
2287 git ls-files -s >out &&
6e7e027f 2288 test_line_count = 5 out &&
362ab315 2289 git ls-files -u >out &&
6e7e027f 2290 test_line_count = 2 out &&
362ab315
EN
2291 git ls-files -o >out &&
2292 test_line_count = 1 out &&
2293
2294 git rev-parse >actual \
6e7e027f 2295 :0:y/b :0:y/c :0:y/e :1:z/d :3:z/d &&
362ab315 2296 git rev-parse >expect \
6e7e027f 2297 O:z/b O:z/c B:z/e O:z/d B:z/d &&
362ab315
EN
2298 test_cmp expect actual &&
2299
6e7e027f
EN
2300 test_must_fail git rev-parse :2:z/d &&
2301 git ls-files -s z/d | grep ^100755 &&
2302 test_path_is_file z/d &&
2303 test_path_is_missing y/d
362ab315
EN
2304 )
2305'
2306
2307# Testcase 8d, rename/delete...or not?
2308# (Related to testcase 5b; these may appear slightly inconsistent to users;
2309# Also related to testcases 7d and 7e)
2310# Commit O: z/{b,c,d}
2311# Commit A: y/{b,c}
2312# Commit B: z/{b,c,d,e}
2313# Expected: y/{b,c,e}
2314#
2315# Note: It would also be somewhat reasonable to resolve this as
2316# y/{b,c,e}, CONFLICT(rename/delete: x/d -> y/d or deleted)
362ab315
EN
2317#
2318# In this case, I'm leaning towards: commit A was the one that deleted z/d
2319# and it did the rename of z to y, so the two "conflicts" (rename vs.
2320# delete) are both coming from commit A, which is illogical. Conflicts
2321# during merging are supposed to be about opposite sides doing things
2322# differently.
2323
da1e295e 2324test_setup_8d () {
362ab315
EN
2325 test_create_repo 8d &&
2326 (
2327 cd 8d &&
2328
2329 mkdir z &&
2330 echo b >z/b &&
2331 echo c >z/c &&
2332 test_seq 1 10 >z/d &&
2333 git add z &&
2334 test_tick &&
2335 git commit -m "O" &&
2336
2337 git branch O &&
2338 git branch A &&
2339 git branch B &&
2340
2341 git checkout A &&
2342 git rm z/d &&
2343 git mv z y &&
2344 test_tick &&
2345 git commit -m "A" &&
2346
2347 git checkout B &&
2348 echo e >z/e &&
2349 git add z/e &&
2350 test_tick &&
2351 git commit -m "B"
2352 )
da1e295e 2353}
362ab315 2354
da1e295e
EN
2355test_expect_success '8d: rename/delete...or not?' '
2356 test_setup_8d &&
362ab315
EN
2357 (
2358 cd 8d &&
2359
2360 git checkout A^0 &&
2361
8c8e5bd6 2362 git -c merge.directoryRenames=true merge -s recursive B^0 &&
362ab315
EN
2363
2364 git ls-files -s >out &&
2365 test_line_count = 3 out &&
2366
2367 git rev-parse >actual \
2368 HEAD:y/b HEAD:y/c HEAD:y/e &&
2369 git rev-parse >expect \
2370 O:z/b O:z/c B:z/e &&
2371 test_cmp expect actual
2372 )
2373'
2374
2375# Testcase 8e, Both sides rename, one side adds to original directory
2376# Commit O: z/{b,c}
2377# Commit A: y/{b,c}
2378# Commit B: w/{b,c}, z/d
2379#
2380# Possible Resolutions:
6c74948f
EN
2381# if z not considered renamed: z/d, CONFLICT(z/b -> y/b vs. w/b),
2382# CONFLICT(z/c -> y/c vs. w/c)
2383# if z->y rename considered: y/d, CONFLICT(z/b -> y/b vs. w/b),
2384# CONFLICT(z/c -> y/c vs. w/c)
2385# Optimal: ??
362ab315
EN
2386#
2387# Notes: In commit A, directory z got renamed to y. In commit B, directory z
2388# did NOT get renamed; the directory is still present; instead it is
2389# considered to have just renamed a subset of paths in directory z
8536821d
EN
2390# elsewhere. This is much like testcase 6b2 (where commit B moves all
2391# the original paths out of z/ but opted to keep d within z/).
2392#
2393# It was not clear in the past what should be done with this testcase;
2394# in fact, I noted that I "just picked one" previously. However,
2395# following the new logic for testcase 6b2, we should take the rename
2396# and move z/d to y/d.
362ab315 2397#
8536821d
EN
2398# 6b1, 6b2, and this case are definitely somewhat fuzzy in terms of
2399# whether they are optimal for end users, but (a) the default for
2400# directory rename detection is to mark these all as conflicts
2401# anyway, (b) it feels like this is less prone to higher order corner
2402# case confusion, and (c) the current algorithm requires less global
2403# knowledge (i.e. less coupling in the algorithm between renames done
2404# on both sides) which thus means users are better able to predict
2405# the behavior, and predict it without computing as many details.
362ab315 2406
da1e295e 2407test_setup_8e () {
362ab315
EN
2408 test_create_repo 8e &&
2409 (
2410 cd 8e &&
2411
2412 mkdir z &&
2413 echo b >z/b &&
2414 echo c >z/c &&
2415 git add z &&
2416 test_tick &&
2417 git commit -m "O" &&
2418
2419 git branch O &&
2420 git branch A &&
2421 git branch B &&
2422
2423 git checkout A &&
2424 git mv z y &&
2425 test_tick &&
2426 git commit -m "A" &&
2427
2428 git checkout B &&
2429 git mv z w &&
2430 mkdir z &&
2431 echo d >z/d &&
2432 git add z/d &&
2433 test_tick &&
2434 git commit -m "B"
2435 )
da1e295e 2436}
362ab315 2437
da1e295e
EN
2438test_expect_success '8e: Both sides rename, one side adds to original directory' '
2439 test_setup_8e &&
362ab315
EN
2440 (
2441 cd 8e &&
2442
2443 git checkout A^0 &&
2444
8c8e5bd6 2445 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
362ab315
EN
2446 test_i18ngrep CONFLICT.*rename/rename.*z/c.*y/c.*w/c out &&
2447 test_i18ngrep CONFLICT.*rename/rename.*z/b.*y/b.*w/b out &&
2448
2449 git ls-files -s >out &&
2450 test_line_count = 7 out &&
2451 git ls-files -u >out &&
2452 test_line_count = 6 out &&
2453 git ls-files -o >out &&
2454 test_line_count = 2 out &&
2455
2456 git rev-parse >actual \
2457 :1:z/b :2:y/b :3:w/b :1:z/c :2:y/c :3:w/c :0:y/d &&
2458 git rev-parse >expect \
2459 O:z/b O:z/b O:z/b O:z/c O:z/c O:z/c B:z/d &&
2460 test_cmp expect actual &&
2461
2462 git hash-object >actual \
2463 y/b w/b y/c w/c &&
2464 git rev-parse >expect \
2465 O:z/b O:z/b O:z/c O:z/c &&
2466 test_cmp expect actual &&
2467
2468 test_path_is_missing z/b &&
2469 test_path_is_missing z/c
2470 )
2471'
2472
792e1371
EN
2473###########################################################################
2474# SECTION 9: Other testcases
2475#
2476# This section consists of miscellaneous testcases I thought of during
2477# the implementation which round out the testing.
2478###########################################################################
2479
2480# Testcase 9a, Inner renamed directory within outer renamed directory
2481# (Related to testcase 1f)
2482# Commit O: z/{b,c,d/{e,f,g}}
2483# Commit A: y/{b,c}, x/w/{e,f,g}
2484# Commit B: z/{b,c,d/{e,f,g,h},i}
2485# Expected: y/{b,c,i}, x/w/{e,f,g,h}
2486# NOTE: The only reason this one is interesting is because when a directory
2487# is split into multiple other directories, we determine by the weight
2488# of which one had the most paths going to it. A naive implementation
2489# of that could take the new file in commit B at z/i to x/w/i or x/i.
2490
da1e295e 2491test_setup_9a () {
792e1371
EN
2492 test_create_repo 9a &&
2493 (
2494 cd 9a &&
2495
2496 mkdir -p z/d &&
2497 echo b >z/b &&
2498 echo c >z/c &&
2499 echo e >z/d/e &&
2500 echo f >z/d/f &&
2501 echo g >z/d/g &&
2502 git add z &&
2503 test_tick &&
2504 git commit -m "O" &&
2505
2506 git branch O &&
2507 git branch A &&
2508 git branch B &&
2509
2510 git checkout A &&
2511 mkdir x &&
2512 git mv z/d x/w &&
2513 git mv z y &&
2514 test_tick &&
2515 git commit -m "A" &&
2516
2517 git checkout B &&
2518 echo h >z/d/h &&
2519 echo i >z/i &&
2520 git add z &&
2521 test_tick &&
2522 git commit -m "B"
2523 )
da1e295e 2524}
792e1371 2525
da1e295e
EN
2526test_expect_success '9a: Inner renamed directory within outer renamed directory' '
2527 test_setup_9a &&
792e1371
EN
2528 (
2529 cd 9a &&
2530
2531 git checkout A^0 &&
2532
8c8e5bd6 2533 git -c merge.directoryRenames=true merge -s recursive B^0 &&
792e1371
EN
2534
2535 git ls-files -s >out &&
2536 test_line_count = 7 out &&
2537 git ls-files -u >out &&
2538 test_line_count = 0 out &&
2539 git ls-files -o >out &&
2540 test_line_count = 1 out &&
2541
2542 git rev-parse >actual \
2543 HEAD:y/b HEAD:y/c HEAD:y/i &&
2544 git rev-parse >expect \
2545 O:z/b O:z/c B:z/i &&
2546 test_cmp expect actual &&
2547
2548 git rev-parse >actual \
2549 HEAD:x/w/e HEAD:x/w/f HEAD:x/w/g HEAD:x/w/h &&
2550 git rev-parse >expect \
2551 O:z/d/e O:z/d/f O:z/d/g B:z/d/h &&
2552 test_cmp expect actual
2553 )
2554'
2555
2556# Testcase 9b, Transitive rename with content merge
2557# (Related to testcase 1c)
2558# Commit O: z/{b,c}, x/d_1
2559# Commit A: y/{b,c}, x/d_2
2560# Commit B: z/{b,c,d_3}
2561# Expected: y/{b,c,d_merged}
2562
da1e295e 2563test_setup_9b () {
792e1371
EN
2564 test_create_repo 9b &&
2565 (
2566 cd 9b &&
2567
2568 mkdir z &&
2569 echo b >z/b &&
2570 echo c >z/c &&
2571 mkdir x &&
2572 test_seq 1 10 >x/d &&
2573 git add z x &&
2574 test_tick &&
2575 git commit -m "O" &&
2576
2577 git branch O &&
2578 git branch A &&
2579 git branch B &&
2580
2581 git checkout A &&
2582 git mv z y &&
2583 test_seq 1 11 >x/d &&
2584 git add x/d &&
2585 test_tick &&
2586 git commit -m "A" &&
2587
2588 git checkout B &&
2589 test_seq 0 10 >x/d &&
2590 git mv x/d z/d &&
2591 git add z/d &&
2592 test_tick &&
2593 git commit -m "B"
2594 )
da1e295e 2595}
792e1371 2596
da1e295e
EN
2597test_expect_success '9b: Transitive rename with content merge' '
2598 test_setup_9b &&
792e1371
EN
2599 (
2600 cd 9b &&
2601
2602 git checkout A^0 &&
2603
8c8e5bd6 2604 git -c merge.directoryRenames=true merge -s recursive B^0 &&
792e1371
EN
2605
2606 git ls-files -s >out &&
2607 test_line_count = 3 out &&
2608
2609 test_seq 0 11 >expected &&
2610 test_cmp expected y/d &&
2611 git add expected &&
2612 git rev-parse >actual \
2613 HEAD:y/b HEAD:y/c HEAD:y/d &&
2614 git rev-parse >expect \
2615 O:z/b O:z/c :0:expected &&
2616 test_cmp expect actual &&
2617 test_must_fail git rev-parse HEAD:x/d &&
2618 test_must_fail git rev-parse HEAD:z/d &&
2619 test_path_is_missing z/d &&
2620
2621 test $(git rev-parse HEAD:y/d) != $(git rev-parse O:x/d) &&
2622 test $(git rev-parse HEAD:y/d) != $(git rev-parse A:x/d) &&
2623 test $(git rev-parse HEAD:y/d) != $(git rev-parse B:z/d)
2624 )
2625'
2626
2627# Testcase 9c, Doubly transitive rename?
2628# (Related to testcase 1c, 7e, and 9d)
2629# Commit O: z/{b,c}, x/{d,e}, w/f
2630# Commit A: y/{b,c}, x/{d,e,f,g}
2631# Commit B: z/{b,c,d,e}, w/f
2632# Expected: y/{b,c,d,e}, x/{f,g}
2633#
2634# NOTE: x/f and x/g may be slightly confusing here. The rename from w/f to
2635# x/f is clear. Let's look beyond that. Here's the logic:
2636# Commit B renamed x/ -> z/
2637# Commit A renamed z/ -> y/
2638# So, we could possibly further rename x/f to z/f to y/f, a doubly
2639# transient rename. However, where does it end? We can chain these
2640# indefinitely (see testcase 9d). What if there is a D/F conflict
2641# at z/f/ or y/f/? Or just another file conflict at one of those
2642# paths? In the case of an N-long chain of transient renamings,
2643# where do we "abort" the rename at? Can the user make sense of
2644# the resulting conflict and resolve it?
2645#
2646# To avoid this confusion I use the simple rule that if the other side
2647# of history did a directory rename to a path that your side renamed
2648# away, then ignore that particular rename from the other side of
2649# history for any implicit directory renames.
2650
da1e295e 2651test_setup_9c () {
792e1371
EN
2652 test_create_repo 9c &&
2653 (
2654 cd 9c &&
2655
2656 mkdir z &&
2657 echo b >z/b &&
2658 echo c >z/c &&
2659 mkdir x &&
2660 echo d >x/d &&
2661 echo e >x/e &&
2662 mkdir w &&
2663 echo f >w/f &&
2664 git add z x w &&
2665 test_tick &&
2666 git commit -m "O" &&
2667
2668 git branch O &&
2669 git branch A &&
2670 git branch B &&
2671
2672 git checkout A &&
2673 git mv z y &&
2674 git mv w/f x/ &&
2675 echo g >x/g &&
2676 git add x/g &&
2677 test_tick &&
2678 git commit -m "A" &&
2679
2680 git checkout B &&
2681 git mv x/d z/d &&
2682 git mv x/e z/e &&
2683 test_tick &&
2684 git commit -m "B"
2685 )
da1e295e 2686}
792e1371 2687
da1e295e
EN
2688test_expect_success '9c: Doubly transitive rename?' '
2689 test_setup_9c &&
792e1371
EN
2690 (
2691 cd 9c &&
2692
2693 git checkout A^0 &&
2694
8c8e5bd6 2695 git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
792e1371
EN
2696 test_i18ngrep "WARNING: Avoiding applying x -> z rename to x/f" out &&
2697
2698 git ls-files -s >out &&
2699 test_line_count = 6 out &&
2700 git ls-files -o >out &&
2701 test_line_count = 1 out &&
2702
2703 git rev-parse >actual \
2704 HEAD:y/b HEAD:y/c HEAD:y/d HEAD:y/e HEAD:x/f HEAD:x/g &&
2705 git rev-parse >expect \
2706 O:z/b O:z/c O:x/d O:x/e O:w/f A:x/g &&
2707 test_cmp expect actual
2708 )
2709'
2710
2711# Testcase 9d, N-fold transitive rename?
2712# (Related to testcase 9c...and 1c and 7e)
2713# Commit O: z/a, y/b, x/c, w/d, v/e, u/f
2714# Commit A: y/{a,b}, w/{c,d}, u/{e,f}
2715# Commit B: z/{a,t}, x/{b,c}, v/{d,e}, u/f
2716# Expected: <see NOTE first>
2717#
2718# NOTE: z/ -> y/ (in commit A)
2719# y/ -> x/ (in commit B)
2720# x/ -> w/ (in commit A)
2721# w/ -> v/ (in commit B)
2722# v/ -> u/ (in commit A)
2723# So, if we add a file to z, say z/t, where should it end up? In u?
2724# What if there's another file or directory named 't' in one of the
2725# intervening directories and/or in u itself? Also, shouldn't the
2726# same logic that places 't' in u/ also move ALL other files to u/?
2727# What if there are file or directory conflicts in any of them? If
2728# we attempted to do N-way (N-fold? N-ary? N-uple?) transitive renames
2729# like this, would the user have any hope of understanding any
2730# conflicts or how their working tree ended up? I think not, so I'm
2731# ruling out N-ary transitive renames for N>1.
2732#
2733# Therefore our expected result is:
2734# z/t, y/a, x/b, w/c, u/d, u/e, u/f
2735# The reason that v/d DOES get transitively renamed to u/d is that u/ isn't
2736# renamed somewhere. A slightly sub-optimal result, but it uses fairly
2737# simple rules that are consistent with what we need for all the other
2738# testcases and simplifies things for the user.
2739
da1e295e 2740test_setup_9d () {
792e1371
EN
2741 test_create_repo 9d &&
2742 (
2743 cd 9d &&
2744
2745 mkdir z y x w v u &&
2746 echo a >z/a &&
2747 echo b >y/b &&
2748 echo c >x/c &&
2749 echo d >w/d &&
2750 echo e >v/e &&
2751 echo f >u/f &&
2752 git add z y x w v u &&
2753 test_tick &&
2754 git commit -m "O" &&
2755
2756 git branch O &&
2757 git branch A &&
2758 git branch B &&
2759
2760 git checkout A &&
2761 git mv z/a y/ &&
2762 git mv x/c w/ &&
2763 git mv v/e u/ &&
2764 test_tick &&
2765 git commit -m "A" &&
2766
2767 git checkout B &&
2768 echo t >z/t &&
2769 git mv y/b x/ &&
2770 git mv w/d v/ &&
2771 git add z/t &&
2772 test_tick &&
2773 git commit -m "B"
2774 )
da1e295e 2775}
792e1371 2776
da1e295e
EN
2777test_expect_success '9d: N-way transitive rename?' '
2778 test_setup_9d &&
792e1371
EN
2779 (
2780 cd 9d &&
2781
2782 git checkout A^0 &&
2783
8c8e5bd6 2784 git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
792e1371
EN
2785 test_i18ngrep "WARNING: Avoiding applying z -> y rename to z/t" out &&
2786 test_i18ngrep "WARNING: Avoiding applying y -> x rename to y/a" out &&
2787 test_i18ngrep "WARNING: Avoiding applying x -> w rename to x/b" out &&
2788 test_i18ngrep "WARNING: Avoiding applying w -> v rename to w/c" out &&
2789
2790 git ls-files -s >out &&
2791 test_line_count = 7 out &&
2792 git ls-files -o >out &&
2793 test_line_count = 1 out &&
2794
2795 git rev-parse >actual \
2796 HEAD:z/t \
2797 HEAD:y/a HEAD:x/b HEAD:w/c \
2798 HEAD:u/d HEAD:u/e HEAD:u/f &&
2799 git rev-parse >expect \
2800 B:z/t \
2801 O:z/a O:y/b O:x/c \
2802 O:w/d O:v/e A:u/f &&
2803 test_cmp expect actual
2804 )
2805'
2806
2807# Testcase 9e, N-to-1 whammo
2808# (Related to testcase 9c...and 1c and 7e)
2809# Commit O: dir1/{a,b}, dir2/{d,e}, dir3/{g,h}, dirN/{j,k}
2810# Commit A: dir1/{a,b,c,yo}, dir2/{d,e,f,yo}, dir3/{g,h,i,yo}, dirN/{j,k,l,yo}
2811# Commit B: combined/{a,b,d,e,g,h,j,k}
2812# Expected: combined/{a,b,c,d,e,f,g,h,i,j,k,l}, CONFLICT(Nto1) warnings,
2813# dir1/yo, dir2/yo, dir3/yo, dirN/yo
2814
da1e295e 2815test_setup_9e () {
792e1371
EN
2816 test_create_repo 9e &&
2817 (
2818 cd 9e &&
2819
2820 mkdir dir1 dir2 dir3 dirN &&
2821 echo a >dir1/a &&
2822 echo b >dir1/b &&
2823 echo d >dir2/d &&
2824 echo e >dir2/e &&
2825 echo g >dir3/g &&
2826 echo h >dir3/h &&
2827 echo j >dirN/j &&
2828 echo k >dirN/k &&
2829 git add dir* &&
2830 test_tick &&
2831 git commit -m "O" &&
2832
2833 git branch O &&
2834 git branch A &&
2835 git branch B &&
2836
2837 git checkout A &&
2838 echo c >dir1/c &&
2839 echo yo >dir1/yo &&
2840 echo f >dir2/f &&
2841 echo yo >dir2/yo &&
2842 echo i >dir3/i &&
2843 echo yo >dir3/yo &&
2844 echo l >dirN/l &&
2845 echo yo >dirN/yo &&
2846 git add dir* &&
2847 test_tick &&
2848 git commit -m "A" &&
2849
2850 git checkout B &&
2851 git mv dir1 combined &&
2852 git mv dir2/* combined/ &&
2853 git mv dir3/* combined/ &&
2854 git mv dirN/* combined/ &&
2855 test_tick &&
2856 git commit -m "B"
2857 )
da1e295e 2858}
792e1371 2859
da1e295e
EN
2860test_expect_success C_LOCALE_OUTPUT '9e: N-to-1 whammo' '
2861 test_setup_9e &&
792e1371
EN
2862 (
2863 cd 9e &&
2864
2865 git checkout A^0 &&
2866
8c8e5bd6 2867 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
792e1371
EN
2868 grep "CONFLICT (implicit dir rename): Cannot map more than one path to combined/yo" out >error_line &&
2869 grep -q dir1/yo error_line &&
2870 grep -q dir2/yo error_line &&
2871 grep -q dir3/yo error_line &&
2872 grep -q dirN/yo error_line &&
2873
2874 git ls-files -s >out &&
2875 test_line_count = 16 out &&
2876 git ls-files -u >out &&
2877 test_line_count = 0 out &&
2878 git ls-files -o >out &&
2879 test_line_count = 2 out &&
2880
2881 git rev-parse >actual \
2882 :0:combined/a :0:combined/b :0:combined/c \
2883 :0:combined/d :0:combined/e :0:combined/f \
2884 :0:combined/g :0:combined/h :0:combined/i \
2885 :0:combined/j :0:combined/k :0:combined/l &&
2886 git rev-parse >expect \
2887 O:dir1/a O:dir1/b A:dir1/c \
2888 O:dir2/d O:dir2/e A:dir2/f \
2889 O:dir3/g O:dir3/h A:dir3/i \
2890 O:dirN/j O:dirN/k A:dirN/l &&
2891 test_cmp expect actual &&
2892
2893 git rev-parse >actual \
2894 :0:dir1/yo :0:dir2/yo :0:dir3/yo :0:dirN/yo &&
2895 git rev-parse >expect \
2896 A:dir1/yo A:dir2/yo A:dir3/yo A:dirN/yo &&
2897 test_cmp expect actual
2898 )
2899'
2900
2901# Testcase 9f, Renamed directory that only contained immediate subdirs
2902# (Related to testcases 1e & 9g)
2903# Commit O: goal/{a,b}/$more_files
2904# Commit A: priority/{a,b}/$more_files
2905# Commit B: goal/{a,b}/$more_files, goal/c
2906# Expected: priority/{a,b}/$more_files, priority/c
2907
da1e295e 2908test_setup_9f () {
792e1371
EN
2909 test_create_repo 9f &&
2910 (
2911 cd 9f &&
2912
2913 mkdir -p goal/a &&
2914 mkdir -p goal/b &&
2915 echo foo >goal/a/foo &&
2916 echo bar >goal/b/bar &&
2917 echo baz >goal/b/baz &&
2918 git add goal &&
2919 test_tick &&
2920 git commit -m "O" &&
2921
2922 git branch O &&
2923 git branch A &&
2924 git branch B &&
2925
2926 git checkout A &&
2927 git mv goal/ priority &&
2928 test_tick &&
2929 git commit -m "A" &&
2930
2931 git checkout B &&
2932 echo c >goal/c &&
2933 git add goal/c &&
2934 test_tick &&
2935 git commit -m "B"
2936 )
da1e295e 2937}
792e1371 2938
da1e295e
EN
2939test_expect_success '9f: Renamed directory that only contained immediate subdirs' '
2940 test_setup_9f &&
792e1371
EN
2941 (
2942 cd 9f &&
2943
2944 git checkout A^0 &&
2945
8c8e5bd6 2946 git -c merge.directoryRenames=true merge -s recursive B^0 &&
792e1371
EN
2947
2948 git ls-files -s >out &&
2949 test_line_count = 4 out &&
2950
2951 git rev-parse >actual \
2952 HEAD:priority/a/foo \
2953 HEAD:priority/b/bar \
2954 HEAD:priority/b/baz \
2955 HEAD:priority/c &&
2956 git rev-parse >expect \
2957 O:goal/a/foo \
2958 O:goal/b/bar \
2959 O:goal/b/baz \
2960 B:goal/c &&
2961 test_cmp expect actual &&
2962 test_must_fail git rev-parse HEAD:goal/c
2963 )
2964'
2965
2966# Testcase 9g, Renamed directory that only contained immediate subdirs, immediate subdirs renamed
2967# (Related to testcases 1e & 9f)
2968# Commit O: goal/{a,b}/$more_files
2969# Commit A: priority/{alpha,bravo}/$more_files
2970# Commit B: goal/{a,b}/$more_files, goal/c
2971# Expected: priority/{alpha,bravo}/$more_files, priority/c
1cb58877
EN
2972# We currently fail this test because the directory renames we detect are
2973# goal/a/ -> priority/alpha/
2974# goal/b/ -> priority/bravo/
2975# We do not detect
2976# goal/ -> priority/
2977# because of no files found within goal/, and the fact that "a" != "alpha"
2978# and "b" != "bravo". But I'm not sure it's really a failure given that
2979# viewpoint...
792e1371 2980
da1e295e 2981test_setup_9g () {
792e1371
EN
2982 test_create_repo 9g &&
2983 (
2984 cd 9g &&
2985
2986 mkdir -p goal/a &&
2987 mkdir -p goal/b &&
2988 echo foo >goal/a/foo &&
2989 echo bar >goal/b/bar &&
2990 echo baz >goal/b/baz &&
2991 git add goal &&
2992 test_tick &&
2993 git commit -m "O" &&
2994
2995 git branch O &&
2996 git branch A &&
2997 git branch B &&
2998
2999 git checkout A &&
3000 mkdir priority &&
3001 git mv goal/a/ priority/alpha &&
3002 git mv goal/b/ priority/beta &&
3003 rmdir goal/ &&
3004 test_tick &&
3005 git commit -m "A" &&
3006
3007 git checkout B &&
3008 echo c >goal/c &&
3009 git add goal/c &&
3010 test_tick &&
3011 git commit -m "B"
3012 )
da1e295e 3013}
792e1371 3014
da1e295e 3015test_expect_failure '9g: Renamed directory that only contained immediate subdirs, immediate subdirs renamed' '
a0601b2e 3016 test_setup_9g &&
792e1371
EN
3017 (
3018 cd 9g &&
3019
3020 git checkout A^0 &&
3021
8c8e5bd6 3022 git -c merge.directoryRenames=true merge -s recursive B^0 &&
792e1371
EN
3023
3024 git ls-files -s >out &&
3025 test_line_count = 4 out &&
3026
3027 git rev-parse >actual \
3028 HEAD:priority/alpha/foo \
3029 HEAD:priority/beta/bar \
3030 HEAD:priority/beta/baz \
3031 HEAD:priority/c &&
3032 git rev-parse >expect \
3033 O:goal/a/foo \
3034 O:goal/b/bar \
3035 O:goal/b/baz \
3036 B:goal/c &&
3037 test_cmp expect actual &&
3038 test_must_fail git rev-parse HEAD:goal/c
3039 )
3040'
3041
bc71c4ee
EN
3042# Testcase 9h, Avoid implicit rename if involved as source on other side
3043# (Extremely closely related to testcase 3a)
3044# Commit O: z/{b,c,d_1}
3045# Commit A: z/{b,c,d_2}
3046# Commit B: y/{b,c}, x/d_1
3047# Expected: y/{b,c}, x/d_2
3048# NOTE: If we applied the z/ -> y/ rename to z/d, then we'd end up with
3049# a rename/rename(1to2) conflict (z/d -> y/d vs. x/d)
da1e295e 3050test_setup_9h () {
bc71c4ee
EN
3051 test_create_repo 9h &&
3052 (
3053 cd 9h &&
3054
3055 mkdir z &&
3056 echo b >z/b &&
3057 echo c >z/c &&
3058 printf "1\n2\n3\n4\n5\n6\n7\n8\nd\n" >z/d &&
3059 git add z &&
3060 test_tick &&
3061 git commit -m "O" &&
3062
3063 git branch O &&
3064 git branch A &&
3065 git branch B &&
3066
3067 git checkout A &&
3068 test_tick &&
3069 echo more >>z/d &&
3070 git add z/d &&
3071 git commit -m "A" &&
3072
3073 git checkout B &&
3074 mkdir y &&
3075 mkdir x &&
3076 git mv z/b y/ &&
3077 git mv z/c y/ &&
3078 git mv z/d x/ &&
3079 rmdir z &&
3080 test_tick &&
3081 git commit -m "B"
3082 )
da1e295e 3083}
bc71c4ee 3084
da1e295e
EN
3085test_expect_success '9h: Avoid dir rename on merely modified path' '
3086 test_setup_9h &&
bc71c4ee
EN
3087 (
3088 cd 9h &&
3089
3090 git checkout A^0 &&
3091
8c8e5bd6 3092 git -c merge.directoryRenames=true merge -s recursive B^0 &&
bc71c4ee
EN
3093
3094 git ls-files -s >out &&
3095 test_line_count = 3 out &&
3096
3097 git rev-parse >actual \
3098 HEAD:y/b HEAD:y/c HEAD:x/d &&
3099 git rev-parse >expect \
3100 O:z/b O:z/c A:z/d &&
3101 test_cmp expect actual
3102 )
3103'
3104
792e1371
EN
3105###########################################################################
3106# Rules suggested by section 9:
3107#
3108# If the other side of history did a directory rename to a path that your
3109# side renamed away, then ignore that particular rename from the other
3110# side of history for any implicit directory renames.
3111###########################################################################
3112
a0b0a151
EN
3113###########################################################################
3114# SECTION 10: Handling untracked files
3115#
3116# unpack_trees(), upon which the recursive merge algorithm is based, aborts
3117# the operation if untracked or dirty files would be deleted or overwritten
3118# by the merge. Unfortunately, unpack_trees() does not understand renames,
3119# and if it doesn't abort, then it muddies up the working directory before
3120# we even get to the point of detecting renames, so we need some special
3121# handling, at least in the case of directory renames.
3122###########################################################################
3123
3124# Testcase 10a, Overwrite untracked: normal rename/delete
3125# Commit O: z/{b,c_1}
3126# Commit A: z/b + untracked z/c + untracked z/d
3127# Commit B: z/{b,d_1}
3128# Expected: Aborted Merge +
3129# ERROR_MSG(untracked working tree files would be overwritten by merge)
3130
da1e295e 3131test_setup_10a () {
a0b0a151
EN
3132 test_create_repo 10a &&
3133 (
3134 cd 10a &&
3135
3136 mkdir z &&
3137 echo b >z/b &&
3138 echo c >z/c &&
3139 git add z &&
3140 test_tick &&
3141 git commit -m "O" &&
3142
3143 git branch O &&
3144 git branch A &&
3145 git branch B &&
3146
3147 git checkout A &&
3148 git rm z/c &&
3149 test_tick &&
3150 git commit -m "A" &&
3151
3152 git checkout B &&
3153 git mv z/c z/d &&
3154 test_tick &&
3155 git commit -m "B"
3156 )
da1e295e 3157}
a0b0a151 3158
da1e295e
EN
3159test_expect_success '10a: Overwrite untracked with normal rename/delete' '
3160 test_setup_10a &&
a0b0a151
EN
3161 (
3162 cd 10a &&
3163
3164 git checkout A^0 &&
3165 echo very >z/c &&
3166 echo important >z/d &&
3167
8c8e5bd6 3168 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
a0b0a151
EN
3169 test_i18ngrep "The following untracked working tree files would be overwritten by merge" err &&
3170
3171 git ls-files -s >out &&
3172 test_line_count = 1 out &&
3173 git ls-files -o >out &&
3174 test_line_count = 4 out &&
3175
3176 echo very >expect &&
3177 test_cmp expect z/c &&
3178
3179 echo important >expect &&
3180 test_cmp expect z/d &&
3181
3182 git rev-parse HEAD:z/b >actual &&
3183 git rev-parse O:z/b >expect &&
3184 test_cmp expect actual
3185 )
3186'
3187
3188# Testcase 10b, Overwrite untracked: dir rename + delete
3189# Commit O: z/{b,c_1}
3190# Commit A: y/b + untracked y/{c,d,e}
3191# Commit B: z/{b,d_1,e}
3192# Expected: Failed Merge; y/b + untracked y/c + untracked y/d on disk +
3193# z/c_1 -> z/d_1 rename recorded at stage 3 for y/d +
3194# ERROR_MSG(refusing to lose untracked file at 'y/d')
3195
da1e295e 3196test_setup_10b () {
a0b0a151
EN
3197 test_create_repo 10b &&
3198 (
3199 cd 10b &&
3200
3201 mkdir z &&
3202 echo b >z/b &&
3203 echo c >z/c &&
3204 git add z &&
3205 test_tick &&
3206 git commit -m "O" &&
3207
3208 git branch O &&
3209 git branch A &&
3210 git branch B &&
3211
3212 git checkout A &&
3213 git rm z/c &&
3214 git mv z/ y/ &&
3215 test_tick &&
3216 git commit -m "A" &&
3217
3218 git checkout B &&
3219 git mv z/c z/d &&
3220 echo e >z/e &&
3221 git add z/e &&
3222 test_tick &&
3223 git commit -m "B"
3224 )
da1e295e 3225}
a0b0a151 3226
da1e295e
EN
3227test_expect_success '10b: Overwrite untracked with dir rename + delete' '
3228 test_setup_10b &&
a0b0a151
EN
3229 (
3230 cd 10b &&
3231
3232 git checkout A^0 &&
3233 echo very >y/c &&
3234 echo important >y/d &&
3235 echo contents >y/e &&
3236
8c8e5bd6 3237 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
a0b0a151
EN
3238 test_i18ngrep "CONFLICT (rename/delete).*Version B\^0 of y/d left in tree at y/d~B\^0" out &&
3239 test_i18ngrep "Error: Refusing to lose untracked file at y/e; writing to y/e~B\^0 instead" out &&
3240
3241 git ls-files -s >out &&
3242 test_line_count = 3 out &&
3243 git ls-files -u >out &&
3244 test_line_count = 2 out &&
3245 git ls-files -o >out &&
3246 test_line_count = 5 out &&
3247
3248 git rev-parse >actual \
3249 :0:y/b :3:y/d :3:y/e &&
3250 git rev-parse >expect \
3251 O:z/b O:z/c B:z/e &&
3252 test_cmp expect actual &&
3253
3254 echo very >expect &&
3255 test_cmp expect y/c &&
3256
3257 echo important >expect &&
3258 test_cmp expect y/d &&
3259
3260 echo contents >expect &&
3261 test_cmp expect y/e
3262 )
3263'
3264
3265# Testcase 10c, Overwrite untracked: dir rename/rename(1to2)
3266# Commit O: z/{a,b}, x/{c,d}
3267# Commit A: y/{a,b}, w/c, x/d + different untracked y/c
3268# Commit B: z/{a,b,c}, x/d
3269# Expected: Failed Merge; y/{a,b} + x/d + untracked y/c +
3270# CONFLICT(rename/rename) x/c -> w/c vs y/c +
3271# y/c~B^0 +
3272# ERROR_MSG(Refusing to lose untracked file at y/c)
3273
da1e295e
EN
3274test_setup_10c () {
3275 test_create_repo 10c_$1 &&
a0b0a151 3276 (
da1e295e 3277 cd 10c_$1 &&
a0b0a151
EN
3278
3279 mkdir z x &&
3280 echo a >z/a &&
3281 echo b >z/b &&
3282 echo c >x/c &&
3283 echo d >x/d &&
3284 git add z x &&
3285 test_tick &&
3286 git commit -m "O" &&
3287
3288 git branch O &&
3289 git branch A &&
3290 git branch B &&
3291
3292 git checkout A &&
3293 mkdir w &&
3294 git mv x/c w/c &&
3295 git mv z/ y/ &&
3296 test_tick &&
3297 git commit -m "A" &&
3298
3299 git checkout B &&
3300 git mv x/c z/ &&
3301 test_tick &&
3302 git commit -m "B"
3303 )
da1e295e 3304}
a0b0a151 3305
da1e295e
EN
3306test_expect_success '10c1: Overwrite untracked with dir rename/rename(1to2)' '
3307 test_setup_10c 1 &&
a0b0a151 3308 (
da1e295e 3309 cd 10c_1 &&
a0b0a151
EN
3310
3311 git checkout A^0 &&
3312 echo important >y/c &&
3313
8c8e5bd6 3314 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
a0b0a151
EN
3315 test_i18ngrep "CONFLICT (rename/rename)" out &&
3316 test_i18ngrep "Refusing to lose untracked file at y/c; adding as y/c~B\^0 instead" out &&
3317
3318 git ls-files -s >out &&
3319 test_line_count = 6 out &&
3320 git ls-files -u >out &&
3321 test_line_count = 3 out &&
3322 git ls-files -o >out &&
3323 test_line_count = 3 out &&
3324
3325 git rev-parse >actual \
3326 :0:y/a :0:y/b :0:x/d :1:x/c :2:w/c :3:y/c &&
3327 git rev-parse >expect \
3328 O:z/a O:z/b O:x/d O:x/c O:x/c O:x/c &&
3329 test_cmp expect actual &&
3330
3331 git hash-object y/c~B^0 >actual &&
3332 git rev-parse O:x/c >expect &&
3333 test_cmp expect actual &&
3334
3335 echo important >expect &&
3336 test_cmp expect y/c
3337 )
3338'
3339
da1e295e
EN
3340test_expect_success '10c2: Overwrite untracked with dir rename/rename(1to2), other direction' '
3341 test_setup_10c 2 &&
b8cd1bb7 3342 (
da1e295e 3343 cd 10c_2 &&
b8cd1bb7
EN
3344
3345 git reset --hard &&
3346 git clean -fdqx &&
3347
3348 git checkout B^0 &&
3349 mkdir y &&
3350 echo important >y/c &&
3351
8c8e5bd6 3352 test_must_fail git -c merge.directoryRenames=true merge -s recursive A^0 >out 2>err &&
b8cd1bb7
EN
3353 test_i18ngrep "CONFLICT (rename/rename)" out &&
3354 test_i18ngrep "Refusing to lose untracked file at y/c; adding as y/c~HEAD instead" out &&
3355
3356 git ls-files -s >out &&
3357 test_line_count = 6 out &&
3358 git ls-files -u >out &&
3359 test_line_count = 3 out &&
3360 git ls-files -o >out &&
3361 test_line_count = 3 out &&
3362
3363 git rev-parse >actual \
3364 :0:y/a :0:y/b :0:x/d :1:x/c :3:w/c :2:y/c &&
3365 git rev-parse >expect \
3366 O:z/a O:z/b O:x/d O:x/c O:x/c O:x/c &&
3367 test_cmp expect actual &&
3368
3369 git hash-object y/c~HEAD >actual &&
3370 git rev-parse O:x/c >expect &&
3371 test_cmp expect actual &&
3372
3373 echo important >expect &&
3374 test_cmp expect y/c
3375 )
3376'
3377
a0b0a151
EN
3378# Testcase 10d, Delete untracked w/ dir rename/rename(2to1)
3379# Commit O: z/{a,b,c_1}, x/{d,e,f_2}
3380# Commit A: y/{a,b}, x/{d,e,f_2,wham_1} + untracked y/wham
3381# Commit B: z/{a,b,c_1,wham_2}, y/{d,e}
bbafc9c4 3382# Expected: Failed Merge; y/{a,b,d,e} + untracked y/{wham,wham~merged}+
a0b0a151
EN
3383# CONFLICT(rename/rename) z/c_1 vs x/f_2 -> y/wham
3384# ERROR_MSG(Refusing to lose untracked file at y/wham)
3385
da1e295e 3386test_setup_10d () {
a0b0a151
EN
3387 test_create_repo 10d &&
3388 (
3389 cd 10d &&
3390
3391 mkdir z x &&
3392 echo a >z/a &&
3393 echo b >z/b &&
3394 echo c >z/c &&
3395 echo d >x/d &&
3396 echo e >x/e &&
3397 echo f >x/f &&
3398 git add z x &&
3399 test_tick &&
3400 git commit -m "O" &&
3401
3402 git branch O &&
3403 git branch A &&
3404 git branch B &&
3405
3406 git checkout A &&
3407 git mv z/c x/wham &&
3408 git mv z/ y/ &&
3409 test_tick &&
3410 git commit -m "A" &&
3411
3412 git checkout B &&
3413 git mv x/f z/wham &&
3414 git mv x/ y/ &&
3415 test_tick &&
3416 git commit -m "B"
3417 )
da1e295e 3418}
a0b0a151 3419
da1e295e
EN
3420test_expect_success '10d: Delete untracked with dir rename/rename(2to1)' '
3421 test_setup_10d &&
a0b0a151
EN
3422 (
3423 cd 10d &&
3424
3425 git checkout A^0 &&
3426 echo important >y/wham &&
3427
8c8e5bd6 3428 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
a0b0a151
EN
3429 test_i18ngrep "CONFLICT (rename/rename)" out &&
3430 test_i18ngrep "Refusing to lose untracked file at y/wham" out &&
3431
3432 git ls-files -s >out &&
3433 test_line_count = 6 out &&
3434 git ls-files -u >out &&
3435 test_line_count = 2 out &&
3436 git ls-files -o >out &&
bbafc9c4 3437 test_line_count = 3 out &&
a0b0a151
EN
3438
3439 git rev-parse >actual \
3440 :0:y/a :0:y/b :0:y/d :0:y/e :2:y/wham :3:y/wham &&
3441 git rev-parse >expect \
3442 O:z/a O:z/b O:x/d O:x/e O:z/c O:x/f &&
3443 test_cmp expect actual &&
3444
3445 test_must_fail git rev-parse :1:y/wham &&
3446
3447 echo important >expect &&
3448 test_cmp expect y/wham &&
3449
bbafc9c4
EN
3450 # Test that the two-way merge in y/wham~merged is as expected
3451 git cat-file -p :2:y/wham >expect &&
3452 git cat-file -p :3:y/wham >other &&
3453 >empty &&
3454 test_must_fail git merge-file \
3455 -L "HEAD" \
3456 -L "" \
3457 -L "B^0" \
3458 expect empty other &&
3459 test_cmp expect y/wham~merged
a0b0a151
EN
3460 )
3461'
3462
3463# Testcase 10e, Does git complain about untracked file that's not in the way?
3464# Commit O: z/{a,b}
3465# Commit A: y/{a,b} + untracked z/c
3466# Commit B: z/{a,b,c}
3467# Expected: y/{a,b,c} + untracked z/c
3468
da1e295e 3469test_setup_10e () {
a0b0a151
EN
3470 test_create_repo 10e &&
3471 (
3472 cd 10e &&
3473
3474 mkdir z &&
3475 echo a >z/a &&
3476 echo b >z/b &&
3477 git add z &&
3478 test_tick &&
3479 git commit -m "O" &&
3480
3481 git branch O &&
3482 git branch A &&
3483 git branch B &&
3484
3485 git checkout A &&
3486 git mv z/ y/ &&
3487 test_tick &&
3488 git commit -m "A" &&
3489
3490 git checkout B &&
3491 echo c >z/c &&
3492 git add z/c &&
3493 test_tick &&
3494 git commit -m "B"
3495 )
da1e295e 3496}
a0b0a151 3497
f06481f1 3498test_expect_merge_algorithm failure success '10e: Does git complain about untracked file that is not really in the way?' '
a0601b2e 3499 test_setup_10e &&
a0b0a151
EN
3500 (
3501 cd 10e &&
3502
3503 git checkout A^0 &&
3504 mkdir z &&
3505 echo random >z/c &&
3506
8c8e5bd6 3507 git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
a0b0a151
EN
3508 test_i18ngrep ! "following untracked working tree files would be overwritten by merge" err &&
3509
3510 git ls-files -s >out &&
3511 test_line_count = 3 out &&
3512 git ls-files -u >out &&
3513 test_line_count = 0 out &&
3514 git ls-files -o >out &&
3515 test_line_count = 3 out &&
3516
3517 git rev-parse >actual \
3518 :0:y/a :0:y/b :0:y/c &&
3519 git rev-parse >expect \
3520 O:z/a O:z/b B:z/c &&
3521 test_cmp expect actual &&
3522
3523 echo random >expect &&
3524 test_cmp expect z/c
3525 )
3526'
3527
a7a43604
EN
3528###########################################################################
3529# SECTION 11: Handling dirty (not up-to-date) files
3530#
3531# unpack_trees(), upon which the recursive merge algorithm is based, aborts
3532# the operation if untracked or dirty files would be deleted or overwritten
3533# by the merge. Unfortunately, unpack_trees() does not understand renames,
3534# and if it doesn't abort, then it muddies up the working directory before
3535# we even get to the point of detecting renames, so we need some special
3536# handling. This was true even of normal renames, but there are additional
3537# codepaths that need special handling with directory renames. Add
3538# testcases for both renamed-by-directory-rename-detection and standard
3539# rename cases.
3540###########################################################################
3541
3542# Testcase 11a, Avoid losing dirty contents with simple rename
3543# Commit O: z/{a,b_v1},
3544# Commit A: z/{a,c_v1}, and z/c_v1 has uncommitted mods
3545# Commit B: z/{a,b_v2}
3546# Expected: ERROR_MSG(Refusing to lose dirty file at z/c) +
3547# z/a, staged version of z/c has sha1sum matching B:z/b_v2,
3548# z/c~HEAD with contents of B:z/b_v2,
3549# z/c with uncommitted mods on top of A:z/c_v1
3550
da1e295e 3551test_setup_11a () {
a7a43604
EN
3552 test_create_repo 11a &&
3553 (
3554 cd 11a &&
3555
3556 mkdir z &&
3557 echo a >z/a &&
3558 test_seq 1 10 >z/b &&
3559 git add z &&
3560 test_tick &&
3561 git commit -m "O" &&
3562
3563 git branch O &&
3564 git branch A &&
3565 git branch B &&
3566
3567 git checkout A &&
3568 git mv z/b z/c &&
3569 test_tick &&
3570 git commit -m "A" &&
3571
3572 git checkout B &&
3573 echo 11 >>z/b &&
3574 git add z/b &&
3575 test_tick &&
3576 git commit -m "B"
3577 )
da1e295e 3578}
a7a43604 3579
da1e295e
EN
3580test_expect_success '11a: Avoid losing dirty contents with simple rename' '
3581 test_setup_11a &&
a7a43604
EN
3582 (
3583 cd 11a &&
3584
3585 git checkout A^0 &&
3586 echo stuff >>z/c &&
3587
8c8e5bd6 3588 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
a7a43604
EN
3589 test_i18ngrep "Refusing to lose dirty file at z/c" out &&
3590
3591 test_seq 1 10 >expected &&
3592 echo stuff >>expected &&
3593 test_cmp expected z/c &&
3594
3595 git ls-files -s >out &&
3596 test_line_count = 2 out &&
3597 git ls-files -u >out &&
3598 test_line_count = 1 out &&
3599 git ls-files -o >out &&
3600 test_line_count = 4 out &&
3601
3602 git rev-parse >actual \
3603 :0:z/a :2:z/c &&
3604 git rev-parse >expect \
3605 O:z/a B:z/b &&
3606 test_cmp expect actual &&
3607
3608 git hash-object z/c~HEAD >actual &&
3609 git rev-parse B:z/b >expect &&
3610 test_cmp expect actual
3611 )
3612'
3613
3614# Testcase 11b, Avoid losing dirty file involved in directory rename
3615# Commit O: z/a, x/{b,c_v1}
3616# Commit A: z/{a,c_v1}, x/b, and z/c_v1 has uncommitted mods
3617# Commit B: y/a, x/{b,c_v2}
3618# Expected: y/{a,c_v2}, x/b, z/c_v1 with uncommitted mods untracked,
3619# ERROR_MSG(Refusing to lose dirty file at z/c)
3620
3621
da1e295e 3622test_setup_11b () {
a7a43604
EN
3623 test_create_repo 11b &&
3624 (
3625 cd 11b &&
3626
3627 mkdir z x &&
3628 echo a >z/a &&
3629 echo b >x/b &&
3630 test_seq 1 10 >x/c &&
3631 git add z x &&
3632 test_tick &&
3633 git commit -m "O" &&
3634
3635 git branch O &&
3636 git branch A &&
3637 git branch B &&
3638
3639 git checkout A &&
3640 git mv x/c z/c &&
3641 test_tick &&
3642 git commit -m "A" &&
3643
3644 git checkout B &&
3645 git mv z y &&
3646 echo 11 >>x/c &&
3647 git add x/c &&
3648 test_tick &&
3649 git commit -m "B"
3650 )
da1e295e 3651}
a7a43604 3652
da1e295e
EN
3653test_expect_success '11b: Avoid losing dirty file involved in directory rename' '
3654 test_setup_11b &&
a7a43604
EN
3655 (
3656 cd 11b &&
3657
3658 git checkout A^0 &&
3659 echo stuff >>z/c &&
3660
8c8e5bd6 3661 git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
a7a43604
EN
3662 test_i18ngrep "Refusing to lose dirty file at z/c" out &&
3663
3664 grep -q stuff z/c &&
3665 test_seq 1 10 >expected &&
3666 echo stuff >>expected &&
3667 test_cmp expected z/c &&
3668
3669 git ls-files -s >out &&
3670 test_line_count = 3 out &&
3671 git ls-files -u >out &&
3672 test_line_count = 0 out &&
3673 git ls-files -m >out &&
3674 test_line_count = 0 out &&
3675 git ls-files -o >out &&
3676 test_line_count = 4 out &&
3677
3678 git rev-parse >actual \
3679 :0:x/b :0:y/a :0:y/c &&
3680 git rev-parse >expect \
3681 O:x/b O:z/a B:x/c &&
3682 test_cmp expect actual &&
3683
3684 git hash-object y/c >actual &&
3685 git rev-parse B:x/c >expect &&
3686 test_cmp expect actual
3687 )
3688'
3689
3690# Testcase 11c, Avoid losing not-up-to-date with rename + D/F conflict
3691# Commit O: y/a, x/{b,c_v1}
3692# Commit A: y/{a,c_v1}, x/b, and y/c_v1 has uncommitted mods
3693# Commit B: y/{a,c/d}, x/{b,c_v2}
3694# Expected: Abort_msg("following files would be overwritten by merge") +
3695# y/c left untouched (still has uncommitted mods)
3696
da1e295e 3697test_setup_11c () {
a7a43604
EN
3698 test_create_repo 11c &&
3699 (
3700 cd 11c &&
3701
3702 mkdir y x &&
3703 echo a >y/a &&
3704 echo b >x/b &&
3705 test_seq 1 10 >x/c &&
3706 git add y x &&
3707 test_tick &&
3708 git commit -m "O" &&
3709
3710 git branch O &&
3711 git branch A &&
3712 git branch B &&
3713
3714 git checkout A &&
3715 git mv x/c y/c &&
3716 test_tick &&
3717 git commit -m "A" &&
3718
3719 git checkout B &&
3720 mkdir y/c &&
3721 echo d >y/c/d &&
3722 echo 11 >>x/c &&
3723 git add x/c y/c/d &&
3724 test_tick &&
3725 git commit -m "B"
3726 )
da1e295e 3727}
a7a43604 3728
da1e295e
EN
3729test_expect_success '11c: Avoid losing not-uptodate with rename + D/F conflict' '
3730 test_setup_11c &&
a7a43604
EN
3731 (
3732 cd 11c &&
3733
3734 git checkout A^0 &&
3735 echo stuff >>y/c &&
3736
8c8e5bd6 3737 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
a7a43604
EN
3738 test_i18ngrep "following files would be overwritten by merge" err &&
3739
3740 grep -q stuff y/c &&
3741 test_seq 1 10 >expected &&
3742 echo stuff >>expected &&
3743 test_cmp expected y/c &&
3744
3745 git ls-files -s >out &&
3746 test_line_count = 3 out &&
3747 git ls-files -u >out &&
3748 test_line_count = 0 out &&
3749 git ls-files -m >out &&
3750 test_line_count = 1 out &&
3751 git ls-files -o >out &&
3752 test_line_count = 3 out
3753 )
3754'
3755
3756# Testcase 11d, Avoid losing not-up-to-date with rename + D/F conflict
3757# Commit O: z/a, x/{b,c_v1}
3758# Commit A: z/{a,c_v1}, x/b, and z/c_v1 has uncommitted mods
3759# Commit B: y/{a,c/d}, x/{b,c_v2}
3760# Expected: D/F: y/c_v2 vs y/c/d) +
3761# Warning_Msg("Refusing to lose dirty file at z/c) +
3762# y/{a,c~HEAD,c/d}, x/b, now-untracked z/c_v1 with uncommitted mods
3763
da1e295e 3764test_setup_11d () {
a7a43604
EN
3765 test_create_repo 11d &&
3766 (
3767 cd 11d &&
3768
3769 mkdir z x &&
3770 echo a >z/a &&
3771 echo b >x/b &&
3772 test_seq 1 10 >x/c &&
3773 git add z x &&
3774 test_tick &&
3775 git commit -m "O" &&
3776
3777 git branch O &&
3778 git branch A &&
3779 git branch B &&
3780
3781 git checkout A &&
3782 git mv x/c z/c &&
3783 test_tick &&
3784 git commit -m "A" &&
3785
3786 git checkout B &&
3787 git mv z y &&
3788 mkdir y/c &&
3789 echo d >y/c/d &&
3790 echo 11 >>x/c &&
3791 git add x/c y/c/d &&
3792 test_tick &&
3793 git commit -m "B"
3794 )
da1e295e 3795}
a7a43604 3796
da1e295e
EN
3797test_expect_success '11d: Avoid losing not-uptodate with rename + D/F conflict' '
3798 test_setup_11d &&
a7a43604
EN
3799 (
3800 cd 11d &&
3801
3802 git checkout A^0 &&
3803 echo stuff >>z/c &&
3804
8c8e5bd6 3805 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
a7a43604
EN
3806 test_i18ngrep "Refusing to lose dirty file at z/c" out &&
3807
3808 grep -q stuff z/c &&
3809 test_seq 1 10 >expected &&
3810 echo stuff >>expected &&
c8ce3763 3811 test_cmp expected z/c &&
a7a43604
EN
3812
3813 git ls-files -s >out &&
3814 test_line_count = 4 out &&
3815 git ls-files -u >out &&
3816 test_line_count = 1 out &&
3817 git ls-files -o >out &&
3818 test_line_count = 5 out &&
3819
3820 git rev-parse >actual \
3821 :0:x/b :0:y/a :0:y/c/d :3:y/c &&
3822 git rev-parse >expect \
3823 O:x/b O:z/a B:y/c/d B:x/c &&
3824 test_cmp expect actual &&
3825
3826 git hash-object y/c~HEAD >actual &&
3827 git rev-parse B:x/c >expect &&
3828 test_cmp expect actual
3829 )
3830'
3831
3832# Testcase 11e, Avoid deleting not-up-to-date with dir rename/rename(1to2)/add
3833# Commit O: z/{a,b}, x/{c_1,d}
3834# Commit A: y/{a,b,c_2}, x/d, w/c_1, and y/c_2 has uncommitted mods
3835# Commit B: z/{a,b,c_1}, x/d
3836# Expected: Failed Merge; y/{a,b} + x/d +
3837# CONFLICT(rename/rename) x/c_1 -> w/c_1 vs y/c_1 +
3838# ERROR_MSG(Refusing to lose dirty file at y/c)
3839# y/c~B^0 has O:x/c_1 contents
3840# y/c~HEAD has A:y/c_2 contents
3841# y/c has dirty file from before merge
3842
da1e295e 3843test_setup_11e () {
a7a43604
EN
3844 test_create_repo 11e &&
3845 (
3846 cd 11e &&
3847
3848 mkdir z x &&
3849 echo a >z/a &&
3850 echo b >z/b &&
3851 echo c >x/c &&
3852 echo d >x/d &&
3853 git add z x &&
3854 test_tick &&
3855 git commit -m "O" &&
3856
3857 git branch O &&
3858 git branch A &&
3859 git branch B &&
3860
3861 git checkout A &&
3862 git mv z/ y/ &&
3863 echo different >y/c &&
3864 mkdir w &&
3865 git mv x/c w/ &&
3866 git add y/c &&
3867 test_tick &&
3868 git commit -m "A" &&
3869
3870 git checkout B &&
3871 git mv x/c z/ &&
3872 test_tick &&
3873 git commit -m "B"
3874 )
da1e295e 3875}
a7a43604 3876
da1e295e
EN
3877test_expect_success '11e: Avoid deleting not-uptodate with dir rename/rename(1to2)/add' '
3878 test_setup_11e &&
a7a43604
EN
3879 (
3880 cd 11e &&
3881
3882 git checkout A^0 &&
3883 echo mods >>y/c &&
3884
8c8e5bd6 3885 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
a7a43604
EN
3886 test_i18ngrep "CONFLICT (rename/rename)" out &&
3887 test_i18ngrep "Refusing to lose dirty file at y/c" out &&
3888
3889 git ls-files -s >out &&
3890 test_line_count = 7 out &&
3891 git ls-files -u >out &&
3892 test_line_count = 4 out &&
3893 git ls-files -o >out &&
48c9cb9d 3894 test_line_count = 3 out &&
a7a43604
EN
3895
3896 echo different >expected &&
3897 echo mods >>expected &&
3898 test_cmp expected y/c &&
3899
3900 git rev-parse >actual \
3901 :0:y/a :0:y/b :0:x/d :1:x/c :2:w/c :2:y/c :3:y/c &&
3902 git rev-parse >expect \
3903 O:z/a O:z/b O:x/d O:x/c O:x/c A:y/c O:x/c &&
3904 test_cmp expect actual &&
3905
48c9cb9d
EN
3906 # See if y/c~merged has expected contents; requires manually
3907 # doing the expected file merge
3908 git cat-file -p A:y/c >c1 &&
3909 git cat-file -p B:z/c >c2 &&
3910 >empty &&
3911 test_must_fail git merge-file \
3912 -L "HEAD" \
3913 -L "" \
3914 -L "B^0" \
3915 c1 empty c2 &&
3916 test_cmp c1 y/c~merged
a7a43604
EN
3917 )
3918'
3919
3920# Testcase 11f, Avoid deleting not-up-to-date w/ dir rename/rename(2to1)
3921# Commit O: z/{a,b}, x/{c_1,d_2}
3922# Commit A: y/{a,b,wham_1}, x/d_2, except y/wham has uncommitted mods
3923# Commit B: z/{a,b,wham_2}, x/c_1
bbafc9c4 3924# Expected: Failed Merge; y/{a,b} + untracked y/{wham~merged} +
a7a43604
EN
3925# y/wham with dirty changes from before merge +
3926# CONFLICT(rename/rename) x/c vs x/d -> y/wham
3927# ERROR_MSG(Refusing to lose dirty file at y/wham)
3928
da1e295e 3929test_setup_11f () {
a7a43604
EN
3930 test_create_repo 11f &&
3931 (
3932 cd 11f &&
3933
3934 mkdir z x &&
3935 echo a >z/a &&
3936 echo b >z/b &&
3937 test_seq 1 10 >x/c &&
3938 echo d >x/d &&
3939 git add z x &&
3940 test_tick &&
3941 git commit -m "O" &&
3942
3943 git branch O &&
3944 git branch A &&
3945 git branch B &&
3946
3947 git checkout A &&
3948 git mv z/ y/ &&
3949 git mv x/c y/wham &&
3950 test_tick &&
3951 git commit -m "A" &&
3952
3953 git checkout B &&
3954 git mv x/d z/wham &&
3955 test_tick &&
3956 git commit -m "B"
3957 )
da1e295e 3958}
a7a43604 3959
da1e295e
EN
3960test_expect_success '11f: Avoid deleting not-uptodate with dir rename/rename(2to1)' '
3961 test_setup_11f &&
a7a43604
EN
3962 (
3963 cd 11f &&
3964
3965 git checkout A^0 &&
3966 echo important >>y/wham &&
3967
8c8e5bd6 3968 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
a7a43604
EN
3969 test_i18ngrep "CONFLICT (rename/rename)" out &&
3970 test_i18ngrep "Refusing to lose dirty file at y/wham" out &&
3971
3972 git ls-files -s >out &&
3973 test_line_count = 4 out &&
3974 git ls-files -u >out &&
3975 test_line_count = 2 out &&
3976 git ls-files -o >out &&
bbafc9c4 3977 test_line_count = 3 out &&
a7a43604
EN
3978
3979 test_seq 1 10 >expected &&
3980 echo important >>expected &&
3981 test_cmp expected y/wham &&
3982
3983 test_must_fail git rev-parse :1:y/wham &&
a7a43604
EN
3984
3985 git rev-parse >actual \
3986 :0:y/a :0:y/b :2:y/wham :3:y/wham &&
3987 git rev-parse >expect \
3988 O:z/a O:z/b O:x/c O:x/d &&
bbafc9c4
EN
3989 test_cmp expect actual &&
3990
3991 # Test that the two-way merge in y/wham~merged is as expected
3992 git cat-file -p :2:y/wham >expect &&
3993 git cat-file -p :3:y/wham >other &&
3994 >empty &&
3995 test_must_fail git merge-file \
3996 -L "HEAD" \
3997 -L "" \
3998 -L "B^0" \
3999 expect empty other &&
4000 test_cmp expect y/wham~merged
a7a43604
EN
4001 )
4002'
4003
bc71c4ee
EN
4004###########################################################################
4005# SECTION 12: Everything else
4006#
4007# Tests suggested by others. Tests added after implementation completed
4008# and submitted. Grab bag.
4009###########################################################################
4010
4011# Testcase 12a, Moving one directory hierarchy into another
4012# (Related to testcase 9a)
4013# Commit O: node1/{leaf1,leaf2}, node2/{leaf3,leaf4}
4014# Commit A: node1/{leaf1,leaf2,node2/{leaf3,leaf4}}
4015# Commit B: node1/{leaf1,leaf2,leaf5}, node2/{leaf3,leaf4,leaf6}
4016# Expected: node1/{leaf1,leaf2,leaf5,node2/{leaf3,leaf4,leaf6}}
4017
da1e295e 4018test_setup_12a () {
bc71c4ee
EN
4019 test_create_repo 12a &&
4020 (
4021 cd 12a &&
4022
4023 mkdir -p node1 node2 &&
4024 echo leaf1 >node1/leaf1 &&
4025 echo leaf2 >node1/leaf2 &&
4026 echo leaf3 >node2/leaf3 &&
4027 echo leaf4 >node2/leaf4 &&
4028 git add node1 node2 &&
4029 test_tick &&
4030 git commit -m "O" &&
4031
4032 git branch O &&
4033 git branch A &&
4034 git branch B &&
4035
4036 git checkout A &&
4037 git mv node2/ node1/ &&
4038 test_tick &&
4039 git commit -m "A" &&
4040
4041 git checkout B &&
4042 echo leaf5 >node1/leaf5 &&
4043 echo leaf6 >node2/leaf6 &&
4044 git add node1 node2 &&
4045 test_tick &&
4046 git commit -m "B"
4047 )
da1e295e 4048}
bc71c4ee 4049
da1e295e
EN
4050test_expect_success '12a: Moving one directory hierarchy into another' '
4051 test_setup_12a &&
bc71c4ee
EN
4052 (
4053 cd 12a &&
4054
4055 git checkout A^0 &&
4056
8c8e5bd6 4057 git -c merge.directoryRenames=true merge -s recursive B^0 &&
bc71c4ee
EN
4058
4059 git ls-files -s >out &&
4060 test_line_count = 6 out &&
4061
4062 git rev-parse >actual \
4063 HEAD:node1/leaf1 HEAD:node1/leaf2 HEAD:node1/leaf5 \
4064 HEAD:node1/node2/leaf3 \
4065 HEAD:node1/node2/leaf4 \
4066 HEAD:node1/node2/leaf6 &&
4067 git rev-parse >expect \
4068 O:node1/leaf1 O:node1/leaf2 B:node1/leaf5 \
4069 O:node2/leaf3 \
4070 O:node2/leaf4 \
4071 B:node2/leaf6 &&
4072 test_cmp expect actual
4073 )
4074'
4075
c64432aa 4076# Testcase 12b1, Moving two directory hierarchies into each other
bc71c4ee
EN
4077# (Related to testcases 1c and 12c)
4078# Commit O: node1/{leaf1, leaf2}, node2/{leaf3, leaf4}
4079# Commit A: node1/{leaf1, leaf2, node2/{leaf3, leaf4}}
4080# Commit B: node2/{leaf3, leaf4, node1/{leaf1, leaf2}}
c64432aa
EN
4081# Expected: node1/node2/{leaf3, leaf4}
4082# node2/node1/{leaf1, leaf2}
4083# NOTE: If there were new files added to the old node1/ or node2/ directories,
4084# then we would need to detect renames for those directories and would
4085# find that:
4086# commit A renames node2/ -> node1/node2/
4087# commit B renames node1/ -> node2/node1/
4088# Applying those directory renames to the initial result (making all
4089# four paths experience a transitive renaming), yields
4090# node1/node2/node1/{leaf1, leaf2}
bc71c4ee 4091# node2/node1/node2/{leaf3, leaf4}
c64432aa
EN
4092# as the result. It may be really weird to have two directories
4093# rename each other, but simple rules give weird results when given
4094# weird inputs. HOWEVER, the "If" at the beginning of those NOTE was
4095# false; there were no new files added and thus there is no directory
4096# rename detection to perform. As such, we just have simple renames
4097# and the expected answer is:
4098# node1/node2/{leaf3, leaf4}
4099# node2/node1/{leaf1, leaf2}
4100
4101test_setup_12b1 () {
4102 test_create_repo 12b1 &&
4103 (
4104 cd 12b1 &&
4105
4106 mkdir -p node1 node2 &&
4107 echo leaf1 >node1/leaf1 &&
4108 echo leaf2 >node1/leaf2 &&
4109 echo leaf3 >node2/leaf3 &&
4110 echo leaf4 >node2/leaf4 &&
4111 git add node1 node2 &&
4112 test_tick &&
4113 git commit -m "O" &&
4114
4115 git branch O &&
4116 git branch A &&
4117 git branch B &&
4118
4119 git checkout A &&
4120 git mv node2/ node1/ &&
4121 test_tick &&
4122 git commit -m "A" &&
4123
4124 git checkout B &&
4125 git mv node1/ node2/ &&
4126 test_tick &&
4127 git commit -m "B"
4128 )
4129}
4130
f06481f1 4131test_expect_merge_algorithm failure success '12b1: Moving two directory hierarchies into each other' '
c64432aa
EN
4132 test_setup_12b1 &&
4133 (
4134 cd 12b1 &&
4135
4136 git checkout A^0 &&
4137
4138 git -c merge.directoryRenames=true merge -s recursive B^0 &&
4139
4140 git ls-files -s >out &&
4141 test_line_count = 4 out &&
4142
4143 git rev-parse >actual \
4144 HEAD:node2/node1/leaf1 \
4145 HEAD:node2/node1/leaf2 \
4146 HEAD:node1/node2/leaf3 \
4147 HEAD:node1/node2/leaf4 &&
4148 git rev-parse >expect \
4149 O:node1/leaf1 \
4150 O:node1/leaf2 \
4151 O:node2/leaf3 \
4152 O:node2/leaf4 &&
4153 test_cmp expect actual
4154 )
4155'
4156
4157# Testcase 12b2, Moving two directory hierarchies into each other
4158# (Related to testcases 1c and 12c)
4159# Commit O: node1/{leaf1, leaf2}, node2/{leaf3, leaf4}
4160# Commit A: node1/{leaf1, leaf2, leaf5, node2/{leaf3, leaf4}}
4161# Commit B: node2/{leaf3, leaf4, leaf6, node1/{leaf1, leaf2}}
4162# Expected: node1/node2/{node1/{leaf1, leaf2}, leaf6}
4163# node2/node1/{node2/{leaf3, leaf4}, leaf5}
bc71c4ee 4164# NOTE: Without directory renames, we would expect
c64432aa
EN
4165# A: node2/leaf3 -> node1/node2/leaf3
4166# A: node2/leaf1 -> node1/node2/leaf4
4167# A: Adds node1/leaf5
4168# B: node1/leaf1 -> node2/node1/leaf1
4169# B: node1/leaf2 -> node2/node1/leaf2
4170# B: Adds node2/leaf6
bc71c4ee
EN
4171# with directory rename detection, we note that
4172# commit A renames node2/ -> node1/node2/
4173# commit B renames node1/ -> node2/node1/
c64432aa
EN
4174# therefore, applying A's directory rename to the paths added in B gives:
4175# B: node1/leaf1 -> node1/node2/node1/leaf1
4176# B: node1/leaf2 -> node1/node2/node1/leaf2
4177# B: Adds node1/node2/leaf6
4178# and applying B's directory rename to the paths added in A gives:
4179# A: node2/leaf3 -> node2/node1/node2/leaf3
4180# A: node2/leaf1 -> node2/node1/node2/leaf4
4181# A: Adds node2/node1/leaf5
4182# resulting in the expected
4183# node1/node2/{node1/{leaf1, leaf2}, leaf6}
4184# node2/node1/{node2/{leaf3, leaf4}, leaf5}
bc71c4ee
EN
4185#
4186# You may ask, is it weird to have two directories rename each other?
4187# To which, I can do no more than shrug my shoulders and say that
4188# even simple rules give weird results when given weird inputs.
4189
c64432aa
EN
4190test_setup_12b2 () {
4191 test_create_repo 12b2 &&
bc71c4ee 4192 (
c64432aa 4193 cd 12b2 &&
bc71c4ee
EN
4194
4195 mkdir -p node1 node2 &&
4196 echo leaf1 >node1/leaf1 &&
4197 echo leaf2 >node1/leaf2 &&
4198 echo leaf3 >node2/leaf3 &&
4199 echo leaf4 >node2/leaf4 &&
4200 git add node1 node2 &&
4201 test_tick &&
4202 git commit -m "O" &&
4203
4204 git branch O &&
4205 git branch A &&
4206 git branch B &&
4207
4208 git checkout A &&
4209 git mv node2/ node1/ &&
c64432aa
EN
4210 echo leaf5 >node1/leaf5 &&
4211 git add node1/leaf5 &&
bc71c4ee
EN
4212 test_tick &&
4213 git commit -m "A" &&
4214
4215 git checkout B &&
4216 git mv node1/ node2/ &&
c64432aa
EN
4217 echo leaf6 >node2/leaf6 &&
4218 git add node2/leaf6 &&
bc71c4ee
EN
4219 test_tick &&
4220 git commit -m "B"
4221 )
da1e295e 4222}
bc71c4ee 4223
c64432aa
EN
4224test_expect_success '12b2: Moving two directory hierarchies into each other' '
4225 test_setup_12b2 &&
bc71c4ee 4226 (
c64432aa 4227 cd 12b2 &&
bc71c4ee
EN
4228
4229 git checkout A^0 &&
4230
8c8e5bd6 4231 git -c merge.directoryRenames=true merge -s recursive B^0 &&
bc71c4ee
EN
4232
4233 git ls-files -s >out &&
c64432aa 4234 test_line_count = 6 out &&
bc71c4ee
EN
4235
4236 git rev-parse >actual \
4237 HEAD:node1/node2/node1/leaf1 \
4238 HEAD:node1/node2/node1/leaf2 \
4239 HEAD:node2/node1/node2/leaf3 \
c64432aa
EN
4240 HEAD:node2/node1/node2/leaf4 \
4241 HEAD:node2/node1/leaf5 \
4242 HEAD:node1/node2/leaf6 &&
bc71c4ee
EN
4243 git rev-parse >expect \
4244 O:node1/leaf1 \
4245 O:node1/leaf2 \
4246 O:node2/leaf3 \
c64432aa
EN
4247 O:node2/leaf4 \
4248 A:node1/leaf5 \
4249 B:node2/leaf6 &&
bc71c4ee
EN
4250 test_cmp expect actual
4251 )
4252'
4253
c64432aa 4254# Testcase 12c1, Moving two directory hierarchies into each other w/ content merge
bc71c4ee
EN
4255# (Related to testcase 12b)
4256# Commit O: node1/{ leaf1_1, leaf2_1}, node2/{leaf3_1, leaf4_1}
4257# Commit A: node1/{ leaf1_2, leaf2_2, node2/{leaf3_2, leaf4_2}}
4258# Commit B: node2/{node1/{leaf1_3, leaf2_3}, leaf3_3, leaf4_3}
4259# Expected: Content merge conflicts for each of:
4260# node1/node2/node1/{leaf1, leaf2},
4261# node2/node1/node2/{leaf3, leaf4}
c64432aa 4262# NOTE: This is *exactly* like 12b1, except that every path is modified on
bc71c4ee
EN
4263# each side of the merge.
4264
c64432aa
EN
4265test_setup_12c1 () {
4266 test_create_repo 12c1 &&
bc71c4ee 4267 (
c64432aa 4268 cd 12c1 &&
bc71c4ee
EN
4269
4270 mkdir -p node1 node2 &&
4271 printf "1\n2\n3\n4\n5\n6\n7\n8\nleaf1\n" >node1/leaf1 &&
4272 printf "1\n2\n3\n4\n5\n6\n7\n8\nleaf2\n" >node1/leaf2 &&
4273 printf "1\n2\n3\n4\n5\n6\n7\n8\nleaf3\n" >node2/leaf3 &&
4274 printf "1\n2\n3\n4\n5\n6\n7\n8\nleaf4\n" >node2/leaf4 &&
4275 git add node1 node2 &&
4276 test_tick &&
4277 git commit -m "O" &&
4278
4279 git branch O &&
4280 git branch A &&
4281 git branch B &&
4282
4283 git checkout A &&
4284 git mv node2/ node1/ &&
4285 for i in `git ls-files`; do echo side A >>$i; done &&
4286 git add -u &&
4287 test_tick &&
4288 git commit -m "A" &&
4289
4290 git checkout B &&
4291 git mv node1/ node2/ &&
4292 for i in `git ls-files`; do echo side B >>$i; done &&
4293 git add -u &&
4294 test_tick &&
4295 git commit -m "B"
4296 )
da1e295e 4297}
bc71c4ee 4298
f06481f1 4299test_expect_merge_algorithm failure success '12c1: Moving one directory hierarchy into another w/ content merge' '
c64432aa 4300 test_setup_12c1 &&
bc71c4ee 4301 (
c64432aa 4302 cd 12c1 &&
bc71c4ee
EN
4303
4304 git checkout A^0 &&
4305
8c8e5bd6 4306 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 &&
bc71c4ee
EN
4307
4308 git ls-files -u >out &&
4309 test_line_count = 12 out &&
4310
c64432aa
EN
4311 git rev-parse >actual \
4312 :1:node2/node1/leaf1 \
4313 :1:node2/node1/leaf2 \
4314 :1:node1/node2/leaf3 \
4315 :1:node1/node2/leaf4 \
4316 :2:node2/node1/leaf1 \
4317 :2:node2/node1/leaf2 \
4318 :2:node1/node2/leaf3 \
4319 :2:node1/node2/leaf4 \
4320 :3:node2/node1/leaf1 \
4321 :3:node2/node1/leaf2 \
4322 :3:node1/node2/leaf3 \
4323 :3:node1/node2/leaf4 &&
4324 git rev-parse >expect \
4325 O:node1/leaf1 \
4326 O:node1/leaf2 \
4327 O:node2/leaf3 \
4328 O:node2/leaf4 \
4329 A:node1/leaf1 \
4330 A:node1/leaf2 \
4331 A:node1/node2/leaf3 \
4332 A:node1/node2/leaf4 \
4333 B:node2/node1/leaf1 \
4334 B:node2/node1/leaf2 \
4335 B:node2/leaf3 \
4336 B:node2/leaf4 &&
4337 test_cmp expect actual
4338 )
4339'
4340
4341# Testcase 12c2, Moving two directory hierarchies into each other w/ content merge
4342# (Related to testcase 12b)
4343# Commit O: node1/{ leaf1_1, leaf2_1}, node2/{leaf3_1, leaf4_1}
4344# Commit A: node1/{ leaf1_2, leaf2_2, node2/{leaf3_2, leaf4_2}, leaf5}
4345# Commit B: node2/{node1/{leaf1_3, leaf2_3}, leaf3_3, leaf4_3, leaf6}
4346# Expected: Content merge conflicts for each of:
4347# node1/node2/node1/{leaf1, leaf2}
4348# node2/node1/node2/{leaf3, leaf4}
4349# plus
4350# node2/node1/leaf5
4351# node1/node2/leaf6
4352# NOTE: This is *exactly* like 12b2, except that every path from O is modified
4353# on each side of the merge.
4354
4355test_setup_12c2 () {
4356 test_create_repo 12c2 &&
4357 (
4358 cd 12c2 &&
4359
4360 mkdir -p node1 node2 &&
4361 printf "1\n2\n3\n4\n5\n6\n7\n8\nleaf1\n" >node1/leaf1 &&
4362 printf "1\n2\n3\n4\n5\n6\n7\n8\nleaf2\n" >node1/leaf2 &&
4363 printf "1\n2\n3\n4\n5\n6\n7\n8\nleaf3\n" >node2/leaf3 &&
4364 printf "1\n2\n3\n4\n5\n6\n7\n8\nleaf4\n" >node2/leaf4 &&
4365 git add node1 node2 &&
4366 test_tick &&
4367 git commit -m "O" &&
4368
4369 git branch O &&
4370 git branch A &&
4371 git branch B &&
4372
4373 git checkout A &&
4374 git mv node2/ node1/ &&
4375 for i in `git ls-files`; do echo side A >>$i; done &&
4376 git add -u &&
4377 echo leaf5 >node1/leaf5 &&
4378 git add node1/leaf5 &&
4379 test_tick &&
4380 git commit -m "A" &&
4381
4382 git checkout B &&
4383 git mv node1/ node2/ &&
4384 for i in `git ls-files`; do echo side B >>$i; done &&
4385 git add -u &&
4386 echo leaf6 >node2/leaf6 &&
4387 git add node2/leaf6 &&
4388 test_tick &&
4389 git commit -m "B"
4390 )
4391}
4392
4393test_expect_success '12c2: Moving one directory hierarchy into another w/ content merge' '
4394 test_setup_12c2 &&
4395 (
4396 cd 12c2 &&
4397
4398 git checkout A^0 &&
4399
4400 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 &&
4401
4402 git ls-files -s >out &&
4403 test_line_count = 14 out &&
4404 git ls-files -u >out &&
4405 test_line_count = 12 out &&
4406
bc71c4ee
EN
4407 git rev-parse >actual \
4408 :1:node1/node2/node1/leaf1 \
4409 :1:node1/node2/node1/leaf2 \
4410 :1:node2/node1/node2/leaf3 \
4411 :1:node2/node1/node2/leaf4 \
4412 :2:node1/node2/node1/leaf1 \
4413 :2:node1/node2/node1/leaf2 \
4414 :2:node2/node1/node2/leaf3 \
4415 :2:node2/node1/node2/leaf4 \
4416 :3:node1/node2/node1/leaf1 \
4417 :3:node1/node2/node1/leaf2 \
4418 :3:node2/node1/node2/leaf3 \
c64432aa
EN
4419 :3:node2/node1/node2/leaf4 \
4420 :0:node2/node1/leaf5 \
4421 :0:node1/node2/leaf6 &&
bc71c4ee
EN
4422 git rev-parse >expect \
4423 O:node1/leaf1 \
4424 O:node1/leaf2 \
4425 O:node2/leaf3 \
4426 O:node2/leaf4 \
4427 A:node1/leaf1 \
4428 A:node1/leaf2 \
4429 A:node1/node2/leaf3 \
4430 A:node1/node2/leaf4 \
4431 B:node2/node1/leaf1 \
4432 B:node2/node1/leaf2 \
4433 B:node2/leaf3 \
c64432aa
EN
4434 B:node2/leaf4 \
4435 A:node1/leaf5 \
4436 B:node2/leaf6 &&
bc71c4ee
EN
4437 test_cmp expect actual
4438 )
4439'
4440
49b8133a
EN
4441# Testcase 12d, Rename/merge of subdirectory into the root
4442# Commit O: a/b/subdir/foo
4443# Commit A: subdir/foo
4444# Commit B: a/b/subdir/foo, a/b/bar
4445# Expected: subdir/foo, bar
4446
da1e295e 4447test_setup_12d () {
49b8133a
EN
4448 test_create_repo 12d &&
4449 (
4450 cd 12d &&
4451
4452 mkdir -p a/b/subdir &&
4453 test_commit a/b/subdir/foo &&
4454
4455 git branch O &&
4456 git branch A &&
4457 git branch B &&
4458
4459 git checkout A &&
4460 mkdir subdir &&
4461 git mv a/b/subdir/foo.t subdir/foo.t &&
4462 test_tick &&
4463 git commit -m "A" &&
4464
4465 git checkout B &&
4466 test_commit a/b/bar
4467 )
da1e295e 4468}
49b8133a 4469
da1e295e
EN
4470test_expect_success '12d: Rename/merge subdir into the root, variant 1' '
4471 test_setup_12d &&
49b8133a
EN
4472 (
4473 cd 12d &&
4474
4475 git checkout A^0 &&
4476
4477 git -c merge.directoryRenames=true merge -s recursive B^0 &&
4478
4479 git ls-files -s >out &&
4480 test_line_count = 2 out &&
4481
4482 git rev-parse >actual \
4483 HEAD:subdir/foo.t HEAD:bar.t &&
4484 git rev-parse >expect \
4485 O:a/b/subdir/foo.t B:a/b/bar.t &&
4486 test_cmp expect actual &&
4487
4488 git hash-object bar.t >actual &&
4489 git rev-parse B:a/b/bar.t >expect &&
4490 test_cmp expect actual &&
4491
4492 test_must_fail git rev-parse HEAD:a/b/subdir/foo.t &&
4493 test_must_fail git rev-parse HEAD:a/b/bar.t &&
4494 test_path_is_missing a/ &&
4495 test_path_is_file bar.t
4496 )
4497'
4498
4499# Testcase 12e, Rename/merge of subdirectory into the root
4500# Commit O: a/b/foo
4501# Commit A: foo
4502# Commit B: a/b/foo, a/b/bar
4503# Expected: foo, bar
4504
da1e295e 4505test_setup_12e () {
49b8133a
EN
4506 test_create_repo 12e &&
4507 (
4508 cd 12e &&
4509
4510 mkdir -p a/b &&
4511 test_commit a/b/foo &&
4512
4513 git branch O &&
4514 git branch A &&
4515 git branch B &&
4516
4517 git checkout A &&
4518 mkdir subdir &&
4519 git mv a/b/foo.t foo.t &&
4520 test_tick &&
4521 git commit -m "A" &&
4522
4523 git checkout B &&
4524 test_commit a/b/bar
4525 )
da1e295e 4526}
49b8133a 4527
da1e295e
EN
4528test_expect_success '12e: Rename/merge subdir into the root, variant 2' '
4529 test_setup_12e &&
49b8133a
EN
4530 (
4531 cd 12e &&
4532
4533 git checkout A^0 &&
4534
4535 git -c merge.directoryRenames=true merge -s recursive B^0 &&
4536
4537 git ls-files -s >out &&
4538 test_line_count = 2 out &&
4539
4540 git rev-parse >actual \
4541 HEAD:foo.t HEAD:bar.t &&
4542 git rev-parse >expect \
4543 O:a/b/foo.t B:a/b/bar.t &&
4544 test_cmp expect actual &&
4545
4546 git hash-object bar.t >actual &&
4547 git rev-parse B:a/b/bar.t >expect &&
4548 test_cmp expect actual &&
4549
4550 test_must_fail git rev-parse HEAD:a/b/foo.t &&
4551 test_must_fail git rev-parse HEAD:a/b/bar.t &&
4552 test_path_is_missing a/ &&
4553 test_path_is_file bar.t
4554 )
4555'
4556
902c521a
EN
4557# Testcase 12f, Rebase of patches with big directory rename
4558# Commit O:
4559# dir/subdir/{a,b,c,d,e_O,Makefile_TOP_O}
4560# dir/subdir/tweaked/{f,g,h,Makefile_SUB_O}
4561# dir/unchanged/<LOTS OF FILES>
4562# Commit A:
4563# (Remove f & g, move e into newsubdir, rename dir/->folder/, modify files)
4564# folder/subdir/{a,b,c,d,Makefile_TOP_A}
4565# folder/subdir/newsubdir/e_A
4566# folder/subdir/tweaked/{h,Makefile_SUB_A}
4567# folder/unchanged/<LOTS OF FILES>
4568# Commit B1:
4569# (add newfile.{c,py}, modify underscored files)
4570# dir/{a,b,c,d,e_B1,Makefile_TOP_B1,newfile.c}
4571# dir/tweaked/{f,g,h,Makefile_SUB_B1,newfile.py}
4572# dir/unchanged/<LOTS OF FILES>
4573# Commit B2:
4574# (Modify e further, add newfile.rs)
4575# dir/{a,b,c,d,e_B2,Makefile_TOP_B1,newfile.c,newfile.rs}
4576# dir/tweaked/{f,g,h,Makefile_SUB_B1,newfile.py}
4577# dir/unchanged/<LOTS OF FILES>
4578# Expected:
4579# B1-picked:
4580# folder/subdir/{a,b,c,d,Makefile_TOP_Merge1,newfile.c}
4581# folder/subdir/newsubdir/e_Merge1
4582# folder/subdir/tweaked/{h,Makefile_SUB_Merge1,newfile.py}
4583# folder/unchanged/<LOTS OF FILES>
4584# B2-picked:
4585# folder/subdir/{a,b,c,d,Makefile_TOP_Merge1,newfile.c,newfile.rs}
4586# folder/subdir/newsubdir/e_Merge2
4587# folder/subdir/tweaked/{h,Makefile_SUB_Merge1,newfile.py}
4588# folder/unchanged/<LOTS OF FILES>
4589#
4590# Notes: This testcase happens to exercise lots of the
4591# optimization-specific codepaths in merge-ort, and also
4592# demonstrated a failing of the directory rename detection algorithm
4593# in merge-recursive; newfile.c should not get pushed into
4594# folder/subdir/newsubdir/, yet merge-recursive put it there because
4595# the rename of dir/subdir/{a,b,c,d} -> folder/subdir/{a,b,c,d}
4596# looks like
4597# dir/ -> folder/,
4598# whereas the rename of dir/subdir/e -> folder/subdir/newsubdir/e
4599# looks like
4600# dir/subdir/ -> folder/subdir/newsubdir/
4601# and if we note that newfile.c is found in dir/subdir/, we might
4602# overlook the dir/ -> folder/ rule that has more weight.
4603
4604test_setup_12f () {
4605 test_create_repo 12f &&
4606 (
4607 cd 12f &&
4608
4609 mkdir -p dir/unchanged &&
4610 mkdir -p dir/subdir/tweaked &&
4611 echo a >dir/subdir/a &&
4612 echo b >dir/subdir/b &&
4613 echo c >dir/subdir/c &&
4614 echo d >dir/subdir/d &&
4615 test_seq 1 10 >dir/subdir/e &&
4616 test_seq 10 20 >dir/subdir/Makefile &&
4617 echo f >dir/subdir/tweaked/f &&
4618 echo g >dir/subdir/tweaked/g &&
4619 echo h >dir/subdir/tweaked/h &&
4620 test_seq 20 30 >dir/subdir/tweaked/Makefile &&
4621 for i in `test_seq 1 88`; do
4622 echo content $i >dir/unchanged/file_$i
4623 done &&
4624 git add . &&
4625 git commit -m "O" &&
4626
4627 git branch O &&
4628 git branch A &&
4629 git branch B &&
4630
4631 git switch A &&
4632 git rm dir/subdir/tweaked/f dir/subdir/tweaked/g &&
4633 test_seq 2 10 >dir/subdir/e &&
4634 test_seq 11 20 >dir/subdir/Makefile &&
4635 test_seq 21 30 >dir/subdir/tweaked/Makefile &&
4636 mkdir dir/subdir/newsubdir &&
4637 git mv dir/subdir/e dir/subdir/newsubdir/ &&
4638 git mv dir folder &&
4639 git add . &&
4640 git commit -m "A" &&
4641
4642 git switch B &&
4643 mkdir dir/subdir/newsubdir/ &&
4644 echo c code >dir/subdir/newfile.c &&
4645 echo python code >dir/subdir/newsubdir/newfile.py &&
4646 test_seq 1 11 >dir/subdir/e &&
4647 test_seq 10 21 >dir/subdir/Makefile &&
4648 test_seq 20 31 >dir/subdir/tweaked/Makefile &&
4649 git add . &&
4650 git commit -m "B1" &&
4651
4652 echo rust code >dir/subdir/newfile.rs &&
4653 test_seq 1 12 >dir/subdir/e &&
4654 git add . &&
4655 git commit -m "B2"
4656 )
4657}
4658
f06481f1 4659test_expect_merge_algorithm failure success '12f: Trivial directory resolve, caching, all kinds of fun' '
902c521a
EN
4660 test_setup_12f &&
4661 (
4662 cd 12f &&
4663
4664 git checkout A^0 &&
4665 git branch Bmod B &&
4666
4667 git -c merge.directoryRenames=true rebase A Bmod &&
4668
4669 echo Checking the pick of B1... &&
4670
4671 test_must_fail git rev-parse Bmod~1:dir &&
4672
4673 git ls-tree -r Bmod~1 >out &&
4674 test_line_count = 98 out &&
4675
4676 git diff --name-status A Bmod~1 >actual &&
4677 q_to_tab >expect <<-\EOF &&
4678 MQfolder/subdir/Makefile
4679 AQfolder/subdir/newfile.c
4680 MQfolder/subdir/newsubdir/e
4681 AQfolder/subdir/newsubdir/newfile.py
4682 MQfolder/subdir/tweaked/Makefile
4683 EOF
4684 test_cmp expect actual &&
4685
4686 # Three-way merged files
4687 test_seq 2 11 >e_Merge1 &&
4688 test_seq 11 21 >Makefile_TOP &&
4689 test_seq 21 31 >Makefile_SUB &&
4690 git hash-object >expect \
4691 e_Merge1 \
4692 Makefile_TOP \
4693 Makefile_SUB &&
4694 git rev-parse >actual \
4695 Bmod~1:folder/subdir/newsubdir/e \
4696 Bmod~1:folder/subdir/Makefile \
4697 Bmod~1:folder/subdir/tweaked/Makefile &&
4698 test_cmp expect actual &&
4699
4700 # New files showed up at the right location with right contents
4701 git rev-parse >expect \
4702 B~1:dir/subdir/newfile.c \
4703 B~1:dir/subdir/newsubdir/newfile.py &&
4704 git rev-parse >actual \
4705 Bmod~1:folder/subdir/newfile.c \
4706 Bmod~1:folder/subdir/newsubdir/newfile.py &&
4707 test_cmp expect actual &&
4708
4709 # Removed files
4710 test_path_is_missing folder/subdir/tweaked/f &&
4711 test_path_is_missing folder/subdir/tweaked/g &&
4712
4713 # Unchanged files or directories
4714 git rev-parse >actual \
4715 Bmod~1:folder/subdir/a \
4716 Bmod~1:folder/subdir/b \
4717 Bmod~1:folder/subdir/c \
4718 Bmod~1:folder/subdir/d \
4719 Bmod~1:folder/unchanged \
4720 Bmod~1:folder/subdir/tweaked/h &&
4721 git rev-parse >expect \
4722 O:dir/subdir/a \
4723 O:dir/subdir/b \
4724 O:dir/subdir/c \
4725 O:dir/subdir/d \
4726 O:dir/unchanged \
4727 O:dir/subdir/tweaked/h &&
4728 test_cmp expect actual &&
4729
4730 echo Checking the pick of B2... &&
4731
4732 test_must_fail git rev-parse Bmod:dir &&
4733
4734 git ls-tree -r Bmod >out &&
4735 test_line_count = 99 out &&
4736
4737 git diff --name-status Bmod~1 Bmod >actual &&
4738 q_to_tab >expect <<-\EOF &&
4739 AQfolder/subdir/newfile.rs
4740 MQfolder/subdir/newsubdir/e
4741 EOF
4742 test_cmp expect actual &&
4743
4744 # Three-way merged file
4745 test_seq 2 12 >e_Merge2 &&
4746 git hash-object e_Merge2 >expect &&
4747 git rev-parse Bmod:folder/subdir/newsubdir/e >actual &&
4748 test_cmp expect actual
4749 )
4750'
4751
8c8e5bd6
EN
4752###########################################################################
4753# SECTION 13: Checking informational and conflict messages
4754#
4755# A year after directory rename detection became the default, it was
4756# instead decided to report conflicts on the pathname on the basis that
4757# some users may expect the new files added or moved into a directory to
4758# be unrelated to all the other files in that directory, and thus that
4759# directory rename detection is unexpected. Test that the messages printed
4760# match our expectation.
4761###########################################################################
4762
4763# Testcase 13a, Basic directory rename with newly added files
4764# Commit O: z/{b,c}
4765# Commit A: y/{b,c}
4766# Commit B: z/{b,c,d,e/f}
4767# Expected: y/{b,c,d,e/f}, with notices/conflicts for both y/d and y/e/f
4768
da1e295e
EN
4769test_setup_13a () {
4770 test_create_repo 13a_$1 &&
8c8e5bd6 4771 (
da1e295e 4772 cd 13a_$1 &&
8c8e5bd6
EN
4773
4774 mkdir z &&
4775 echo b >z/b &&
4776 echo c >z/c &&
4777 git add z &&
4778 test_tick &&
4779 git commit -m "O" &&
4780
4781 git branch O &&
4782 git branch A &&
4783 git branch B &&
4784
4785 git checkout A &&
4786 git mv z y &&
4787 test_tick &&
4788 git commit -m "A" &&
4789
4790 git checkout B &&
4791 echo d >z/d &&
4792 mkdir z/e &&
4793 echo f >z/e/f &&
4794 git add z/d z/e/f &&
4795 test_tick &&
4796 git commit -m "B"
4797 )
da1e295e 4798}
8c8e5bd6 4799
da1e295e
EN
4800test_expect_success '13a(conflict): messages for newly added files' '
4801 test_setup_13a conflict &&
8c8e5bd6 4802 (
da1e295e 4803 cd 13a_conflict &&
8c8e5bd6
EN
4804
4805 git checkout A^0 &&
4806
4807 test_must_fail git merge -s recursive B^0 >out 2>err &&
4808
4809 test_i18ngrep CONFLICT..file.location.*z/e/f.added.in.B^0.*y/e/f out &&
4810 test_i18ngrep CONFLICT..file.location.*z/d.added.in.B^0.*y/d out &&
4811
4812 git ls-files >paths &&
4813 ! grep z/ paths &&
4814 grep "y/[de]" paths &&
4815
4816 test_path_is_missing z/d &&
4817 test_path_is_file y/d &&
4818 test_path_is_missing z/e/f &&
4819 test_path_is_file y/e/f
4820 )
4821'
4822
da1e295e
EN
4823test_expect_success '13a(info): messages for newly added files' '
4824 test_setup_13a info &&
8c8e5bd6 4825 (
da1e295e 4826 cd 13a_info &&
8c8e5bd6
EN
4827
4828 git reset --hard &&
4829 git checkout A^0 &&
4830
4831 git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
4832
4833 test_i18ngrep Path.updated:.*z/e/f.added.in.B^0.*y/e/f out &&
4834 test_i18ngrep Path.updated:.*z/d.added.in.B^0.*y/d out &&
4835
4836 git ls-files >paths &&
4837 ! grep z/ paths &&
4838 grep "y/[de]" paths &&
4839
4840 test_path_is_missing z/d &&
4841 test_path_is_file y/d &&
4842 test_path_is_missing z/e/f &&
4843 test_path_is_file y/e/f
4844 )
4845'
4846
4847# Testcase 13b, Transitive rename with conflicted content merge and default
4848# "conflict" setting
4849# (Related to testcase 1c, 9b)
4850# Commit O: z/{b,c}, x/d_1
4851# Commit A: y/{b,c}, x/d_2
4852# Commit B: z/{b,c,d_3}
4853# Expected: y/{b,c,d_merged}, with two conflict messages for y/d,
4854# one about content, and one about file location
4855
da1e295e
EN
4856test_setup_13b () {
4857 test_create_repo 13b_$1 &&
8c8e5bd6 4858 (
da1e295e 4859 cd 13b_$1 &&
8c8e5bd6
EN
4860
4861 mkdir x &&
4862 mkdir z &&
4863 test_seq 1 10 >x/d &&
4864 echo b >z/b &&
4865 echo c >z/c &&
4866 git add x z &&
4867 test_tick &&
4868 git commit -m "O" &&
4869
4870 git branch O &&
4871 git branch A &&
4872 git branch B &&
4873
4874 git checkout A &&
4875 git mv z y &&
4876 echo 11 >>x/d &&
4877 git add x/d &&
4878 test_tick &&
4879 git commit -m "A" &&
4880
4881 git checkout B &&
4882 echo eleven >>x/d &&
4883 git mv x/d z/d &&
4884 git add z/d &&
4885 test_tick &&
4886 git commit -m "B"
4887 )
da1e295e 4888}
8c8e5bd6 4889
da1e295e
EN
4890test_expect_success '13b(conflict): messages for transitive rename with conflicted content' '
4891 test_setup_13b conflict &&
8c8e5bd6 4892 (
da1e295e 4893 cd 13b_conflict &&
8c8e5bd6
EN
4894
4895 git checkout A^0 &&
4896
4897 test_must_fail git merge -s recursive B^0 >out 2>err &&
4898
4899 test_i18ngrep CONFLICT.*content.*Merge.conflict.in.y/d out &&
4900 test_i18ngrep CONFLICT..file.location.*x/d.renamed.to.z/d.*moved.to.y/d out &&
4901
4902 git ls-files >paths &&
4903 ! grep z/ paths &&
4904 grep "y/d" paths &&
4905
4906 test_path_is_missing z/d &&
4907 test_path_is_file y/d
4908 )
4909'
4910
da1e295e
EN
4911test_expect_success '13b(info): messages for transitive rename with conflicted content' '
4912 test_setup_13b info &&
8c8e5bd6 4913 (
da1e295e 4914 cd 13b_info &&
8c8e5bd6
EN
4915
4916 git reset --hard &&
4917 git checkout A^0 &&
4918
4919 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
4920
4921 test_i18ngrep CONFLICT.*content.*Merge.conflict.in.y/d out &&
4922 test_i18ngrep Path.updated:.*x/d.renamed.to.z/d.in.B^0.*moving.it.to.y/d out &&
4923
4924 git ls-files >paths &&
4925 ! grep z/ paths &&
4926 grep "y/d" paths &&
4927
4928 test_path_is_missing z/d &&
4929 test_path_is_file y/d
4930 )
4931'
4932
4933# Testcase 13c, Rename/rename(1to1) due to directory rename
4934# Commit O: z/{b,c}, x/{d,e}
4935# Commit A: y/{b,c,d}, x/e
4936# Commit B: z/{b,c,d}, x/e
6c74948f 4937# Expected: y/{b,c,d}, x/e, with info or conflict messages for d
8c8e5bd6
EN
4938# A: renamed x/d -> z/d; B: renamed z/ -> y/ AND renamed x/d to y/d
4939# One could argue A had partial knowledge of what was done with
4940# d and B had full knowledge, but that's a slippery slope as
4941# shown in testcase 13d.
4942
da1e295e
EN
4943test_setup_13c () {
4944 test_create_repo 13c_$1 &&
8c8e5bd6 4945 (
da1e295e 4946 cd 13c_$1 &&
8c8e5bd6
EN
4947
4948 mkdir x &&
4949 mkdir z &&
4950 test_seq 1 10 >x/d &&
4951 echo e >x/e &&
4952 echo b >z/b &&
4953 echo c >z/c &&
4954 git add x z &&
4955 test_tick &&
4956 git commit -m "O" &&
4957
4958 git branch O &&
4959 git branch A &&
4960 git branch B &&
4961
4962 git checkout A &&
4963 git mv z y &&
4964 git mv x/d y/ &&
4965 test_tick &&
4966 git commit -m "A" &&
4967
4968 git checkout B &&
4969 git mv x/d z/d &&
4970 git add z/d &&
4971 test_tick &&
4972 git commit -m "B"
4973 )
da1e295e 4974}
8c8e5bd6 4975
da1e295e
EN
4976test_expect_success '13c(conflict): messages for rename/rename(1to1) via transitive rename' '
4977 test_setup_13c conflict &&
8c8e5bd6 4978 (
da1e295e 4979 cd 13c_conflict &&
8c8e5bd6
EN
4980
4981 git checkout A^0 &&
4982
4983 test_must_fail git merge -s recursive B^0 >out 2>err &&
4984
4985 test_i18ngrep CONFLICT..file.location.*x/d.renamed.to.z/d.*moved.to.y/d out &&
4986
4987 git ls-files >paths &&
4988 ! grep z/ paths &&
4989 grep "y/d" paths &&
4990
4991 test_path_is_missing z/d &&
4992 test_path_is_file y/d
4993 )
4994'
4995
da1e295e
EN
4996test_expect_success '13c(info): messages for rename/rename(1to1) via transitive rename' '
4997 test_setup_13c info &&
8c8e5bd6 4998 (
da1e295e 4999 cd 13c_info &&
8c8e5bd6
EN
5000
5001 git reset --hard &&
5002 git checkout A^0 &&
5003
5004 git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
5005
5006 test_i18ngrep Path.updated:.*x/d.renamed.to.z/d.in.B^0.*moving.it.to.y/d out &&
5007
5008 git ls-files >paths &&
5009 ! grep z/ paths &&
5010 grep "y/d" paths &&
5011
5012 test_path_is_missing z/d &&
5013 test_path_is_file y/d
5014 )
5015'
5016
5017# Testcase 13d, Rename/rename(1to1) due to directory rename on both sides
5018# Commit O: a/{z,y}, b/x, c/w
5019# Commit A: a/z, b/{y,x}, d/w
5020# Commit B: a/z, d/x, c/{y,w}
5021# Expected: a/z, d/{y,x,w} with no file location conflict for x
5022# Easy cases:
5023# * z is always in a; so it stays in a.
5024# * x starts in b, only modified on one side to move into d/
5025# * w starts in c, only modified on one side to move into d/
5026# Hard case:
5027# * A renames a/y to b/y, and B renames b/->d/ => a/y -> d/y
5028# * B renames a/y to c/y, and A renames c/->d/ => a/y -> d/y
5029# No conflict in where a/y ends up, so put it in d/y.
5030
da1e295e
EN
5031test_setup_13d () {
5032 test_create_repo 13d_$1 &&
8c8e5bd6 5033 (
da1e295e 5034 cd 13d_$1 &&
8c8e5bd6
EN
5035
5036 mkdir a &&
5037 mkdir b &&
5038 mkdir c &&
5039 echo z >a/z &&
5040 echo y >a/y &&
5041 echo x >b/x &&
5042 echo w >c/w &&
5043 git add a b c &&
5044 test_tick &&
5045 git commit -m "O" &&
5046
5047 git branch O &&
5048 git branch A &&
5049 git branch B &&
5050
5051 git checkout A &&
5052 git mv a/y b/ &&
5053 git mv c/ d/ &&
5054 test_tick &&
5055 git commit -m "A" &&
5056
5057 git checkout B &&
5058 git mv a/y c/ &&
5059 git mv b/ d/ &&
5060 test_tick &&
5061 git commit -m "B"
5062 )
da1e295e 5063}
8c8e5bd6 5064
da1e295e
EN
5065test_expect_success '13d(conflict): messages for rename/rename(1to1) via dual transitive rename' '
5066 test_setup_13d conflict &&
8c8e5bd6 5067 (
da1e295e 5068 cd 13d_conflict &&
8c8e5bd6
EN
5069
5070 git checkout A^0 &&
5071
5072 test_must_fail git merge -s recursive B^0 >out 2>err &&
5073
5074 test_i18ngrep CONFLICT..file.location.*a/y.renamed.to.b/y.*moved.to.d/y out &&
5075 test_i18ngrep CONFLICT..file.location.*a/y.renamed.to.c/y.*moved.to.d/y out &&
5076
5077 git ls-files >paths &&
5078 ! grep b/ paths &&
5079 ! grep c/ paths &&
5080 grep "d/y" paths &&
5081
5082 test_path_is_missing b/y &&
5083 test_path_is_missing c/y &&
5084 test_path_is_file d/y
5085 )
5086'
5087
da1e295e
EN
5088test_expect_success '13d(info): messages for rename/rename(1to1) via dual transitive rename' '
5089 test_setup_13d info &&
8c8e5bd6 5090 (
da1e295e 5091 cd 13d_info &&
8c8e5bd6
EN
5092
5093 git reset --hard &&
5094 git checkout A^0 &&
5095
5096 git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
5097
5098 test_i18ngrep Path.updated.*a/y.renamed.to.b/y.*moving.it.to.d/y out &&
5099 test_i18ngrep Path.updated.*a/y.renamed.to.c/y.*moving.it.to.d/y out &&
5100
5101 git ls-files >paths &&
5102 ! grep b/ paths &&
5103 ! grep c/ paths &&
5104 grep "d/y" paths &&
5105
5106 test_path_is_missing b/y &&
5107 test_path_is_missing c/y &&
5108 test_path_is_file d/y
5109 )
5110'
5111
ff6d5477
EN
5112# Testcase 13e, directory rename in virtual merge base
5113#
5114# This testcase has a slightly different setup than all the above cases, in
5115# order to include a recursive case:
5116#
5117# A C
5118# o - o
5119# / \ / \
5120# O o X ?
5121# \ / \ /
5122# o o
5123# B D
5124#
5125# Commit O: a/{z,y}
5126# Commit A: b/{z,y}
5127# Commit B: a/{z,y,x}
5128# Commit C: b/{z,y,x}
5129# Commit D: b/{z,y}, a/x
5130# Expected: b/{z,y,x} (sort of; see below for why this might not be expected)
5131#
5132# NOTES: 'X' represents a virtual merge base. With the default of
5133# directory rename detection yielding conflicts, merging A and B
5134# results in a conflict complaining about whether 'x' should be
5135# under 'a/' or 'b/'. However, when creating the virtual merge
5136# base 'X', since virtual merge bases need to be written out as a
5137# tree, we cannot have a conflict, so some resolution has to be
5138# picked.
5139#
5140# In choosing the right resolution, it's worth noting here that
5141# commits C & D are merges of A & B that choose different
5142# locations for 'x' (i.e. they resolve the conflict differently),
5143# and so it would be nice when merging C & D if git could detect
5144# this difference of opinion and report a conflict. But the only
5145# way to do so that I can think of would be to have the virtual
5146# merge base place 'x' in some directory other than either 'a/' or
5147# 'b/', which seems a little weird -- especially since it'd result
5148# in a rename/rename(1to2) conflict with a source path that never
5149# existed in any version.
5150#
5151# So, for now, when directory rename detection is set to
5152# 'conflict' just avoid doing directory rename detection at all in
5153# the recursive case. This will not allow us to detect a conflict
5154# in the outer merge for this special kind of setup, but it at
5155# least avoids hitting a BUG().
5156#
da1e295e 5157test_setup_13e () {
ff6d5477
EN
5158 test_create_repo 13e &&
5159 (
5160 cd 13e &&
5161
5162 mkdir a &&
5163 echo z >a/z &&
5164 echo y >a/y &&
5165 git add a &&
5166 test_tick &&
5167 git commit -m "O" &&
5168
5169 git branch O &&
5170 git branch A &&
5171 git branch B &&
5172
5173 git checkout A &&
5174 git mv a/ b/ &&
5175 test_tick &&
5176 git commit -m "A" &&
5177
5178 git checkout B &&
5179 echo x >a/x &&
5180 git add a &&
5181 test_tick &&
5182 git commit -m "B" &&
5183
5184 git branch C A &&
5185 git branch D B &&
5186
5187 git checkout C &&
5188 test_must_fail git -c merge.directoryRenames=conflict merge B &&
5189 git add b/x &&
5190 test_tick &&
5191 git commit -m "C" &&
5192
5193
5194 git checkout D &&
5195 test_must_fail git -c merge.directoryRenames=conflict merge A &&
5196 git add b/x &&
5197 mkdir a &&
5198 git mv b/x a/x &&
5199 test_tick &&
5200 git commit -m "D"
5201 )
da1e295e 5202}
ff6d5477 5203
da1e295e
EN
5204test_expect_success '13e: directory rename detection in recursive case' '
5205 test_setup_13e &&
ff6d5477
EN
5206 (
5207 cd 13e &&
5208
5209 git checkout --quiet D^0 &&
5210
5211 git -c merge.directoryRenames=conflict merge -s recursive C^0 >out 2>err &&
5212
5213 test_i18ngrep ! CONFLICT out &&
5214 test_i18ngrep ! BUG: err &&
5215 test_i18ngrep ! core.dumped err &&
5216 test_must_be_empty err &&
5217
5218 git ls-files >paths &&
5219 ! grep a/x paths &&
5220 grep b/x paths
5221 )
5222'
5223
04550ab5 5224test_done