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