Merge branch 'en/merge-ort-perf'
[git] / t / t6402-merge-rename.sh
1 #!/bin/sh
2
3 test_description='Merge-recursive merging renames'
4 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
5 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
6
7 . ./test-lib.sh
8
9 modify () {
10         sed -e "$1" <"$2" >"$2.x" &&
11         mv "$2.x" "$2"
12 }
13
14 test_expect_success 'setup' '
15         cat >A <<-\EOF &&
16         a aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
17         b bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
18         c cccccccccccccccccccccccccccccccccccccccccccccccc
19         d dddddddddddddddddddddddddddddddddddddddddddddddd
20         e eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
21         f ffffffffffffffffffffffffffffffffffffffffffffffff
22         g gggggggggggggggggggggggggggggggggggggggggggggggg
23         h hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh
24         i iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
25         j jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj
26         k kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
27         l llllllllllllllllllllllllllllllllllllllllllllllll
28         m mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
29         n nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn
30         o oooooooooooooooooooooooooooooooooooooooooooooooo
31         EOF
32
33         cat >M <<-\EOF &&
34         A AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
35         B BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
36         C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
37         D DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
38         E EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
39         F FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
40         G GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG
41         H HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
42         I IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII
43         J JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ
44         K KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK
45         L LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL
46         M MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
47         N NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN
48         O OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
49         EOF
50
51         git add A M &&
52         git commit -m "initial has A and M" &&
53         git branch white &&
54         git branch red &&
55         git branch blue &&
56         git branch yellow &&
57         git branch change &&
58         git branch change+rename &&
59
60         sed -e "/^g /s/.*/g : main changes a line/" <A >A+ &&
61         mv A+ A &&
62         git commit -a -m "main updates A" &&
63
64         git checkout yellow &&
65         rm -f M &&
66         git commit -a -m "yellow removes M" &&
67
68         git checkout white &&
69         sed -e "/^g /s/.*/g : white changes a line/" <A >B &&
70         sed -e "/^G /s/.*/G : colored branch changes a line/" <M >N &&
71         rm -f A M &&
72         git update-index --add --remove A B M N &&
73         git commit -m "white renames A->B, M->N" &&
74
75         git checkout red &&
76         sed -e "/^g /s/.*/g : red changes a line/" <A >B &&
77         sed -e "/^G /s/.*/G : colored branch changes a line/" <M >N &&
78         rm -f A M &&
79         git update-index --add --remove A B M N &&
80         git commit -m "red renames A->B, M->N" &&
81
82         git checkout blue &&
83         sed -e "/^g /s/.*/g : blue changes a line/" <A >C &&
84         sed -e "/^G /s/.*/G : colored branch changes a line/" <M >N &&
85         rm -f A M &&
86         git update-index --add --remove A C M N &&
87         git commit -m "blue renames A->C, M->N" &&
88
89         git checkout change &&
90         sed -e "/^g /s/.*/g : changed line/" <A >A+ &&
91         mv A+ A &&
92         git commit -q -a -m "changed" &&
93
94         git checkout change+rename &&
95         sed -e "/^g /s/.*/g : changed line/" <A >B &&
96         rm A &&
97         git update-index --add B &&
98         git commit -q -a -m "changed and renamed" &&
99
100         git checkout main
101 '
102
103 test_expect_success 'pull renaming branch into unrenaming one' \
104 '
105         git show-branch &&
106         test_expect_code 1 git pull . white &&
107         git ls-files -s &&
108         git ls-files -u B >b.stages &&
109         test_line_count = 3 b.stages &&
110         git ls-files -s N >n.stages &&
111         test_line_count = 1 n.stages &&
112         sed -ne "/^g/{
113         p
114         q
115         }" B | grep main &&
116         git diff --exit-code white N
117 '
118
119 test_expect_success 'pull renaming branch into another renaming one' \
120 '
121         rm -f B &&
122         git reset --hard &&
123         git checkout red &&
124         test_expect_code 1 git pull . white &&
125         git ls-files -u B >b.stages &&
126         test_line_count = 3 b.stages &&
127         git ls-files -s N >n.stages &&
128         test_line_count = 1 n.stages &&
129         sed -ne "/^g/{
130         p
131         q
132         }" B | grep red &&
133         git diff --exit-code white N
134 '
135
136 test_expect_success 'pull unrenaming branch into renaming one' \
137 '
138         git reset --hard &&
139         git show-branch &&
140         test_expect_code 1 git pull . main &&
141         git ls-files -u B >b.stages &&
142         test_line_count = 3 b.stages &&
143         git ls-files -s N >n.stages &&
144         test_line_count = 1 n.stages &&
145         sed -ne "/^g/{
146         p
147         q
148         }" B | grep red &&
149         git diff --exit-code white N
150 '
151
152 test_expect_success 'pull conflicting renames' \
153 '
154         git reset --hard &&
155         git show-branch &&
156         test_expect_code 1 git pull . blue &&
157         git ls-files -u A >a.stages &&
158         test_line_count = 1 a.stages &&
159         git ls-files -u B >b.stages &&
160         test_line_count = 1 b.stages &&
161         git ls-files -u C >c.stages &&
162         test_line_count = 1 c.stages &&
163         git ls-files -s N >n.stages &&
164         test_line_count = 1 n.stages &&
165         sed -ne "/^g/{
166         p
167         q
168         }" B | grep red &&
169         git diff --exit-code white N
170 '
171
172 test_expect_success 'interference with untracked working tree file' '
173         git reset --hard &&
174         git show-branch &&
175         echo >A this file should not matter &&
176         test_expect_code 1 git pull . white &&
177         test_path_is_file A
178 '
179
180 test_expect_success 'interference with untracked working tree file' '
181         git reset --hard &&
182         git checkout white &&
183         git show-branch &&
184         rm -f A &&
185         echo >A this file should not matter &&
186         test_expect_code 1 git pull . red &&
187         test_path_is_file A
188 '
189
190 test_expect_success 'interference with untracked working tree file' '
191         git reset --hard &&
192         rm -f A M &&
193         git checkout -f main &&
194         git tag -f anchor &&
195         git show-branch &&
196         git pull . yellow &&
197         test_path_is_missing M &&
198         git reset --hard anchor
199 '
200
201 test_expect_success 'updated working tree file should prevent the merge' '
202         git reset --hard &&
203         rm -f A M &&
204         git checkout -f main &&
205         git tag -f anchor &&
206         git show-branch &&
207         echo >>M one line addition &&
208         cat M >M.saved &&
209         test_expect_code 128 git pull . yellow &&
210         test_cmp M M.saved &&
211         rm -f M.saved
212 '
213
214 test_expect_success 'updated working tree file should prevent the merge' '
215         git reset --hard &&
216         rm -f A M &&
217         git checkout -f main &&
218         git tag -f anchor &&
219         git show-branch &&
220         echo >>M one line addition &&
221         cat M >M.saved &&
222         git update-index M &&
223         test_expect_code 128 git pull . yellow &&
224         test_cmp M M.saved &&
225         rm -f M.saved
226 '
227
228 test_expect_success 'interference with untracked working tree file' '
229         git reset --hard &&
230         rm -f A M &&
231         git checkout -f yellow &&
232         git tag -f anchor &&
233         git show-branch &&
234         echo >M this file should not matter &&
235         git pull . main &&
236         test_path_is_file M &&
237         ! {
238                 git ls-files -s |
239                 grep M
240         } &&
241         git reset --hard anchor
242 '
243
244 test_expect_success 'merge of identical changes in a renamed file' '
245         rm -f A M N &&
246         git reset --hard &&
247         git checkout change+rename &&
248
249         test-tool chmtime --get -3600 B >old-mtime &&
250         GIT_MERGE_VERBOSITY=3 git merge change >out &&
251
252         test-tool chmtime --get B >new-mtime &&
253         test_cmp old-mtime new-mtime &&
254
255         git reset --hard HEAD^ &&
256         git checkout change &&
257
258         # A will be renamed to B; we check mtimes and file presence
259         test_path_is_missing B &&
260         test-tool chmtime --get -3600 A >old-mtime &&
261         GIT_MERGE_VERBOSITY=3 git merge change+rename >out &&
262
263         test_path_is_missing A &&
264         test-tool chmtime --get B >new-mtime &&
265         test $(cat old-mtime) -lt $(cat new-mtime)
266 '
267
268 test_expect_success 'setup for rename + d/f conflicts' '
269         git reset --hard &&
270         git checkout --orphan dir-in-way &&
271         git rm -rf . &&
272         git clean -fdqx &&
273
274         mkdir sub &&
275         mkdir dir &&
276         printf "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n" >sub/file &&
277         echo foo >dir/file-in-the-way &&
278         git add -A &&
279         git commit -m "Common commit" &&
280
281         echo 11 >>sub/file &&
282         echo more >>dir/file-in-the-way &&
283         git add -u &&
284         git commit -m "Commit to merge, with dir in the way" &&
285
286         git checkout -b dir-not-in-way &&
287         git reset --soft HEAD^ &&
288         git rm -rf dir &&
289         git commit -m "Commit to merge, with dir removed" -- dir sub/file &&
290
291         git checkout -b renamed-file-has-no-conflicts dir-in-way~1 &&
292         git rm -rf dir &&
293         git rm sub/file &&
294         printf "1\n2\n3\n4\n5555\n6\n7\n8\n9\n10\n" >dir &&
295         git add dir &&
296         git commit -m "Independent change" &&
297
298         git checkout -b renamed-file-has-conflicts dir-in-way~1 &&
299         git rm -rf dir &&
300         git mv sub/file dir &&
301         echo 12 >>dir &&
302         git add dir &&
303         git commit -m "Conflicting change"
304 '
305
306 test_expect_success 'Rename+D/F conflict; renamed file merges + dir not in way' '
307         git reset --hard &&
308         git checkout -q renamed-file-has-no-conflicts^0 &&
309
310         git merge --strategy=recursive dir-not-in-way &&
311
312         git diff --quiet &&
313         test_path_is_file dir &&
314         test_write_lines 1 2 3 4 5555 6 7 8 9 10 11 >expected &&
315         test_cmp expected dir
316 '
317
318 test_expect_success 'Rename+D/F conflict; renamed file merges but dir in way' '
319         git reset --hard &&
320         rm -rf dir~* &&
321         git checkout -q renamed-file-has-no-conflicts^0 &&
322         test_must_fail git merge --strategy=recursive dir-in-way >output &&
323
324         test_i18ngrep "CONFLICT (modify/delete): dir/file-in-the-way" output &&
325         test_i18ngrep "Auto-merging dir" output &&
326         if test "$GIT_TEST_MERGE_ALGORITHM" = ort
327         then
328                 test_i18ngrep "moving it to dir~HEAD instead" output
329         else
330                 test_i18ngrep "Adding as dir~HEAD instead" output
331         fi &&
332
333         test 3 -eq "$(git ls-files -u | wc -l)" &&
334         test 2 -eq "$(git ls-files -u dir/file-in-the-way | wc -l)" &&
335
336         test_must_fail git diff --quiet &&
337         test_must_fail git diff --cached --quiet &&
338
339         test_path_is_file dir/file-in-the-way &&
340         test_path_is_file dir~HEAD &&
341         test_cmp expected dir~HEAD
342 '
343
344 test_expect_success 'Same as previous, but merged other way' '
345         git reset --hard &&
346         rm -rf dir~* &&
347         git checkout -q dir-in-way^0 &&
348         test_must_fail git merge --strategy=recursive renamed-file-has-no-conflicts >output 2>errors &&
349
350         ! grep "error: refusing to lose untracked file at" errors &&
351         test_i18ngrep "CONFLICT (modify/delete): dir/file-in-the-way" output &&
352         test_i18ngrep "Auto-merging dir" output &&
353         if test "$GIT_TEST_MERGE_ALGORITHM" = ort
354         then
355                 test_i18ngrep "moving it to dir~renamed-file-has-no-conflicts instead" output
356         else
357                 test_i18ngrep "Adding as dir~renamed-file-has-no-conflicts instead" output
358         fi &&
359
360         test 3 -eq "$(git ls-files -u | wc -l)" &&
361         test 2 -eq "$(git ls-files -u dir/file-in-the-way | wc -l)" &&
362
363         test_must_fail git diff --quiet &&
364         test_must_fail git diff --cached --quiet &&
365
366         test_path_is_file dir/file-in-the-way &&
367         test_path_is_file dir~renamed-file-has-no-conflicts &&
368         test_cmp expected dir~renamed-file-has-no-conflicts
369 '
370
371 test_expect_success 'Rename+D/F conflict; renamed file cannot merge, dir not in way' '
372         git reset --hard &&
373         rm -rf dir~* &&
374         git checkout -q renamed-file-has-conflicts^0 &&
375         test_must_fail git merge --strategy=recursive dir-not-in-way &&
376
377         test 3 -eq "$(git ls-files -u | wc -l)" &&
378         test 3 -eq "$(git ls-files -u dir | wc -l)" &&
379
380         test_must_fail git diff --quiet &&
381         test_must_fail git diff --cached --quiet &&
382
383         test_path_is_file dir &&
384         cat >expected <<-\EOF &&
385         1
386         2
387         3
388         4
389         5
390         6
391         7
392         8
393         9
394         10
395         <<<<<<< HEAD:dir
396         12
397         =======
398         11
399         >>>>>>> dir-not-in-way:sub/file
400         EOF
401         test_cmp expected dir
402 '
403
404 test_expect_success 'Rename+D/F conflict; renamed file cannot merge and dir in the way' '
405         modify s/dir-not-in-way/dir-in-way/ expected &&
406
407         git reset --hard &&
408         rm -rf dir~* &&
409         git checkout -q renamed-file-has-conflicts^0 &&
410         test_must_fail git merge --strategy=recursive dir-in-way &&
411
412         test 5 -eq "$(git ls-files -u | wc -l)" &&
413         if test "$GIT_TEST_MERGE_ALGORITHM" = ort
414         then
415                 test 3 -eq "$(git ls-files -u dir~HEAD | wc -l)"
416         else
417                 test 3 -eq "$(git ls-files -u dir | grep -v file-in-the-way | wc -l)"
418         fi &&
419         test 2 -eq "$(git ls-files -u dir/file-in-the-way | wc -l)" &&
420
421         test_must_fail git diff --quiet &&
422         test_must_fail git diff --cached --quiet &&
423
424         test_path_is_file dir/file-in-the-way &&
425         test_path_is_file dir~HEAD &&
426         test_cmp expected dir~HEAD
427 '
428
429 test_expect_success 'Same as previous, but merged other way' '
430         git reset --hard &&
431         rm -rf dir~* &&
432         git checkout -q dir-in-way^0 &&
433         test_must_fail git merge --strategy=recursive renamed-file-has-conflicts &&
434
435         test 5 -eq "$(git ls-files -u | wc -l)" &&
436         if test "$GIT_TEST_MERGE_ALGORITHM" = ort
437         then
438                 test 3 -eq "$(git ls-files -u dir~renamed-file-has-conflicts | wc -l)"
439         else
440                 test 3 -eq "$(git ls-files -u dir | grep -v file-in-the-way | wc -l)"
441         fi &&
442         test 2 -eq "$(git ls-files -u dir/file-in-the-way | wc -l)" &&
443
444         test_must_fail git diff --quiet &&
445         test_must_fail git diff --cached --quiet &&
446
447         test_path_is_file dir/file-in-the-way &&
448         test_path_is_file dir~renamed-file-has-conflicts &&
449         cat >expected <<-\EOF &&
450         1
451         2
452         3
453         4
454         5
455         6
456         7
457         8
458         9
459         10
460         <<<<<<< HEAD:sub/file
461         11
462         =======
463         12
464         >>>>>>> renamed-file-has-conflicts:dir
465         EOF
466         test_cmp expected dir~renamed-file-has-conflicts
467 '
468
469 test_expect_success 'setup both rename source and destination involved in D/F conflict' '
470         git reset --hard &&
471         git checkout --orphan rename-dest &&
472         git rm -rf . &&
473         git clean -fdqx &&
474
475         mkdir one &&
476         echo stuff >one/file &&
477         git add -A &&
478         git commit -m "Common commit" &&
479
480         git mv one/file destdir &&
481         git commit -m "Renamed to destdir" &&
482
483         git checkout -b source-conflict HEAD~1 &&
484         git rm -rf one &&
485         mkdir destdir &&
486         touch one destdir/foo &&
487         git add -A &&
488         git commit -m "Conflicts in the way"
489 '
490
491 test_expect_success 'both rename source and destination involved in D/F conflict' '
492         git reset --hard &&
493         rm -rf dir~* &&
494         git checkout -q rename-dest^0 &&
495         test_must_fail git merge --strategy=recursive source-conflict &&
496
497         if test "$GIT_TEST_MERGE_ALGORITHM" = ort
498         then
499                 test 2 -eq "$(git ls-files -u | wc -l)"
500         else
501                 test 1 -eq "$(git ls-files -u | wc -l)"
502         fi &&
503
504         test_must_fail git diff --quiet &&
505
506         test_path_is_file destdir/foo &&
507         test_path_is_file one &&
508         test_path_is_file destdir~HEAD &&
509         test "stuff" = "$(cat destdir~HEAD)"
510 '
511
512 test_expect_success 'setup pair rename to parent of other (D/F conflicts)' '
513         git reset --hard &&
514         git checkout --orphan rename-two &&
515         git rm -rf . &&
516         git clean -fdqx &&
517
518         mkdir one &&
519         mkdir two &&
520         echo stuff >one/file &&
521         echo other >two/file &&
522         git add -A &&
523         git commit -m "Common commit" &&
524
525         git rm -rf one &&
526         git mv two/file one &&
527         git commit -m "Rename two/file -> one" &&
528
529         git checkout -b rename-one HEAD~1 &&
530         git rm -rf two &&
531         git mv one/file two &&
532         rm -r one &&
533         git commit -m "Rename one/file -> two"
534 '
535
536 if test "$GIT_TEST_MERGE_ALGORITHM" = ort
537 then
538         test_expect_success 'pair rename to parent of other (D/F conflicts) w/ untracked dir' '
539                 git checkout -q rename-one^0 &&
540                 mkdir one &&
541                 test_must_fail git merge --strategy=recursive rename-two &&
542
543                 test 4 -eq "$(git ls-files -u | wc -l)" &&
544                 test 2 -eq "$(git ls-files -u one | wc -l)" &&
545                 test 2 -eq "$(git ls-files -u two | wc -l)" &&
546
547                 test_must_fail git diff --quiet &&
548
549                 test 3 -eq $(find . | grep -v .git | wc -l) &&
550
551                 test_path_is_file one &&
552                 test_path_is_file two &&
553                 test "other" = $(cat one) &&
554                 test "stuff" = $(cat two)
555         '
556 else
557         test_expect_success 'pair rename to parent of other (D/F conflicts) w/ untracked dir' '
558                 git checkout -q rename-one^0 &&
559                 mkdir one &&
560                 test_must_fail git merge --strategy=recursive rename-two &&
561
562                 test 2 -eq "$(git ls-files -u | wc -l)" &&
563                 test 1 -eq "$(git ls-files -u one | wc -l)" &&
564                 test 1 -eq "$(git ls-files -u two | wc -l)" &&
565
566                 test_must_fail git diff --quiet &&
567
568                 test 4 -eq $(find . | grep -v .git | wc -l) &&
569
570                 test_path_is_dir one &&
571                 test_path_is_file one~rename-two &&
572                 test_path_is_file two &&
573                 test "other" = $(cat one~rename-two) &&
574                 test "stuff" = $(cat two)
575         '
576 fi
577
578 test_expect_success 'pair rename to parent of other (D/F conflicts) w/ clean start' '
579         git reset --hard &&
580         git clean -fdqx &&
581         test_must_fail git merge --strategy=recursive rename-two &&
582
583         if test "$GIT_TEST_MERGE_ALGORITHM" = ort
584         then
585                 test 4 -eq "$(git ls-files -u | wc -l)" &&
586                 test 2 -eq "$(git ls-files -u one | wc -l)" &&
587                 test 2 -eq "$(git ls-files -u two | wc -l)"
588         else
589                 test 2 -eq "$(git ls-files -u | wc -l)" &&
590                 test 1 -eq "$(git ls-files -u one | wc -l)" &&
591                 test 1 -eq "$(git ls-files -u two | wc -l)"
592         fi &&
593
594         test_must_fail git diff --quiet &&
595
596         test 3 -eq $(find . | grep -v .git | wc -l) &&
597
598         test_path_is_file one &&
599         test_path_is_file two &&
600         test "other" = $(cat one) &&
601         test "stuff" = $(cat two)
602 '
603
604 test_expect_success 'setup rename of one file to two, with directories in the way' '
605         git reset --hard &&
606         git checkout --orphan first-rename &&
607         git rm -rf . &&
608         git clean -fdqx &&
609
610         echo stuff >original &&
611         git add -A &&
612         git commit -m "Common commit" &&
613
614         mkdir two &&
615         >two/file &&
616         git add two/file &&
617         git mv original one &&
618         git commit -m "Put two/file in the way, rename to one" &&
619
620         git checkout -b second-rename HEAD~1 &&
621         mkdir one &&
622         >one/file &&
623         git add one/file &&
624         git mv original two &&
625         git commit -m "Put one/file in the way, rename to two"
626 '
627
628 test_expect_success 'check handling of differently renamed file with D/F conflicts' '
629         git checkout -q first-rename^0 &&
630         test_must_fail git merge --strategy=recursive second-rename &&
631
632         if test "$GIT_TEST_MERGE_ALGORITHM" = ort
633         then
634                 test 5 -eq "$(git ls-files -s | wc -l)" &&
635                 test 3 -eq "$(git ls-files -u | wc -l)" &&
636                 test 1 -eq "$(git ls-files -u one~HEAD | wc -l)" &&
637                 test 1 -eq "$(git ls-files -u two~second-rename | wc -l)" &&
638                 test 1 -eq "$(git ls-files -u original | wc -l)" &&
639                 test 0 -eq "$(git ls-files -o | wc -l)"
640         else
641                 test 5 -eq "$(git ls-files -s | wc -l)" &&
642                 test 3 -eq "$(git ls-files -u | wc -l)" &&
643                 test 1 -eq "$(git ls-files -u one | wc -l)" &&
644                 test 1 -eq "$(git ls-files -u two | wc -l)" &&
645                 test 1 -eq "$(git ls-files -u original | wc -l)" &&
646                 test 2 -eq "$(git ls-files -o | wc -l)"
647         fi &&
648
649         test_path_is_file one/file &&
650         test_path_is_file two/file &&
651         test_path_is_file one~HEAD &&
652         test_path_is_file two~second-rename &&
653         test_path_is_missing original
654 '
655
656 test_expect_success 'setup rename one file to two; directories moving out of the way' '
657         git reset --hard &&
658         git checkout --orphan first-rename-redo &&
659         git rm -rf . &&
660         git clean -fdqx &&
661
662         echo stuff >original &&
663         mkdir one two &&
664         touch one/file two/file &&
665         git add -A &&
666         git commit -m "Common commit" &&
667
668         git rm -rf one &&
669         git mv original one &&
670         git commit -m "Rename to one" &&
671
672         git checkout -b second-rename-redo HEAD~1 &&
673         git rm -rf two &&
674         git mv original two &&
675         git commit -m "Rename to two"
676 '
677
678 test_expect_success 'check handling of differently renamed file with D/F conflicts' '
679         git checkout -q first-rename-redo^0 &&
680         test_must_fail git merge --strategy=recursive second-rename-redo &&
681
682         test 3 -eq "$(git ls-files -u | wc -l)" &&
683         test 1 -eq "$(git ls-files -u one | wc -l)" &&
684         test 1 -eq "$(git ls-files -u two | wc -l)" &&
685         test 1 -eq "$(git ls-files -u original | wc -l)" &&
686         test 0 -eq "$(git ls-files -o | wc -l)" &&
687
688         test_path_is_file one &&
689         test_path_is_file two &&
690         test_path_is_missing original
691 '
692
693 test_expect_success 'setup avoid unnecessary update, normal rename' '
694         git reset --hard &&
695         git checkout --orphan avoid-unnecessary-update-1 &&
696         git rm -rf . &&
697         git clean -fdqx &&
698
699         printf "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n" >original &&
700         git add -A &&
701         git commit -m "Common commit" &&
702
703         git mv original rename &&
704         echo 11 >>rename &&
705         git add -u &&
706         git commit -m "Renamed and modified" &&
707
708         git checkout -b merge-branch-1 HEAD~1 &&
709         echo "random content" >random-file &&
710         git add -A &&
711         git commit -m "Random, unrelated changes"
712 '
713
714 test_expect_success 'avoid unnecessary update, normal rename' '
715         git checkout -q avoid-unnecessary-update-1^0 &&
716         test-tool chmtime --get -3600 rename >expect &&
717         git merge merge-branch-1 &&
718         test-tool chmtime --get rename >actual &&
719         test_cmp expect actual # "rename" should have stayed intact
720 '
721
722 test_expect_success 'setup to test avoiding unnecessary update, with D/F conflict' '
723         git reset --hard &&
724         git checkout --orphan avoid-unnecessary-update-2 &&
725         git rm -rf . &&
726         git clean -fdqx &&
727
728         mkdir df &&
729         printf "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n" >df/file &&
730         git add -A &&
731         git commit -m "Common commit" &&
732
733         git mv df/file temp &&
734         rm -rf df &&
735         git mv temp df &&
736         echo 11 >>df &&
737         git add -u &&
738         git commit -m "Renamed and modified" &&
739
740         git checkout -b merge-branch-2 HEAD~1 &&
741         >unrelated-change &&
742         git add unrelated-change &&
743         git commit -m "Only unrelated changes"
744 '
745
746 test_expect_success 'avoid unnecessary update, with D/F conflict' '
747         git checkout -q avoid-unnecessary-update-2^0 &&
748         test-tool chmtime --get -3600 df >expect &&
749         git merge merge-branch-2 &&
750         test-tool chmtime --get df >actual &&
751         test_cmp expect actual # "df" should have stayed intact
752 '
753
754 test_expect_success 'setup avoid unnecessary update, dir->(file,nothing)' '
755         git rm -rf . &&
756         git clean -fdqx &&
757         rm -rf .git &&
758         git init &&
759
760         >irrelevant &&
761         mkdir df &&
762         >df/file &&
763         git add -A &&
764         git commit -mA &&
765
766         git checkout -b side &&
767         git rm -rf df &&
768         git commit -mB &&
769
770         git checkout main &&
771         git rm -rf df &&
772         echo bla >df &&
773         git add -A &&
774         git commit -m "Add a newfile"
775 '
776
777 test_expect_success 'avoid unnecessary update, dir->(file,nothing)' '
778         git checkout -q main^0 &&
779         test-tool chmtime --get -3600 df >expect &&
780         git merge side &&
781         test-tool chmtime --get df >actual &&
782         test_cmp expect actual # "df" should have stayed intact
783 '
784
785 test_expect_success 'setup avoid unnecessary update, modify/delete' '
786         git rm -rf . &&
787         git clean -fdqx &&
788         rm -rf .git &&
789         git init &&
790
791         >irrelevant &&
792         >file &&
793         git add -A &&
794         git commit -mA &&
795
796         git checkout -b side &&
797         git rm -f file &&
798         git commit -m "Delete file" &&
799
800         git checkout main &&
801         echo bla >file &&
802         git add -A &&
803         git commit -m "Modify file"
804 '
805
806 test_expect_success 'avoid unnecessary update, modify/delete' '
807         git checkout -q main^0 &&
808         test-tool chmtime --get -3600 file >expect &&
809         test_must_fail git merge side &&
810         test-tool chmtime --get file >actual &&
811         test_cmp expect actual # "file" should have stayed intact
812 '
813
814 test_expect_success 'setup avoid unnecessary update, rename/add-dest' '
815         git rm -rf . &&
816         git clean -fdqx &&
817         rm -rf .git &&
818         git init &&
819
820         printf "1\n2\n3\n4\n5\n6\n7\n8\n" >file &&
821         git add -A &&
822         git commit -mA &&
823
824         git checkout -b side &&
825         cp file newfile &&
826         git add -A &&
827         git commit -m "Add file copy" &&
828
829         git checkout main &&
830         git mv file newfile &&
831         git commit -m "Rename file"
832 '
833
834 test_expect_success 'avoid unnecessary update, rename/add-dest' '
835         git checkout -q main^0 &&
836         test-tool chmtime --get -3600 newfile >expect &&
837         git merge side &&
838         test-tool chmtime --get newfile >actual &&
839         test_cmp expect actual # "file" should have stayed intact
840 '
841
842 test_expect_success 'setup merge of rename + small change' '
843         git reset --hard &&
844         git checkout --orphan rename-plus-small-change &&
845         git rm -rf . &&
846         git clean -fdqx &&
847
848         echo ORIGINAL >file &&
849         git add file &&
850
851         test_tick &&
852         git commit -m Initial &&
853         git checkout -b rename_branch &&
854         git mv file renamed_file &&
855         git commit -m Rename &&
856         git checkout rename-plus-small-change &&
857         echo NEW-VERSION >file &&
858         git commit -a -m Reformat
859 '
860
861 test_expect_success 'merge rename + small change' '
862         git merge rename_branch &&
863
864         test 1 -eq $(git ls-files -s | wc -l) &&
865         test 0 -eq $(git ls-files -o | wc -l) &&
866         test $(git rev-parse HEAD:renamed_file) = $(git rev-parse HEAD~1:file)
867 '
868
869 test_expect_success 'setup for use of extended merge markers' '
870         git rm -rf . &&
871         git clean -fdqx &&
872         rm -rf .git &&
873         git init &&
874
875         printf "1\n2\n3\n4\n5\n6\n7\n8\n" >original_file &&
876         git add original_file &&
877         git commit -mA &&
878
879         git checkout -b rename &&
880         echo 9 >>original_file &&
881         git add original_file &&
882         git mv original_file renamed_file &&
883         git commit -mB &&
884
885         git checkout main &&
886         echo 8.5 >>original_file &&
887         git add original_file &&
888         git commit -mC
889 '
890
891 test_expect_success 'merge main into rename has correct extended markers' '
892         git checkout rename^0 &&
893         test_must_fail git merge -s recursive main^0 &&
894
895         cat >expected <<-\EOF &&
896         1
897         2
898         3
899         4
900         5
901         6
902         7
903         8
904         <<<<<<< HEAD:renamed_file
905         9
906         =======
907         8.5
908         >>>>>>> main^0:original_file
909         EOF
910         test_cmp expected renamed_file
911 '
912
913 test_expect_success 'merge rename into main has correct extended markers' '
914         git reset --hard &&
915         git checkout main^0 &&
916         test_must_fail git merge -s recursive rename^0 &&
917
918         cat >expected <<-\EOF &&
919         1
920         2
921         3
922         4
923         5
924         6
925         7
926         8
927         <<<<<<< HEAD:original_file
928         8.5
929         =======
930         9
931         >>>>>>> rename^0:renamed_file
932         EOF
933         test_cmp expected renamed_file
934 '
935
936 test_expect_success 'setup spurious "refusing to lose untracked" message' '
937         git rm -rf . &&
938         git clean -fdqx &&
939         rm -rf .git &&
940         git init &&
941
942         > irrelevant_file &&
943         printf "1\n2\n3\n4\n5\n6\n7\n8\n" >original_file &&
944         git add irrelevant_file original_file &&
945         git commit -mA &&
946
947         git checkout -b rename &&
948         git mv original_file renamed_file &&
949         git commit -mB &&
950
951         git checkout main &&
952         git rm original_file &&
953         git commit -mC
954 '
955
956 test_expect_success 'no spurious "refusing to lose untracked" message' '
957         git checkout main^0 &&
958         test_must_fail git merge rename^0 2>errors.txt &&
959         ! grep "refusing to lose untracked file" errors.txt
960 '
961
962 test_expect_success 'do not follow renames for empty files' '
963         git checkout -f -b empty-base &&
964         >empty1 &&
965         git add empty1 &&
966         git commit -m base &&
967         echo content >empty1 &&
968         git add empty1 &&
969         git commit -m fill &&
970         git checkout -b empty-topic HEAD^ &&
971         git mv empty1 empty2 &&
972         git commit -m rename &&
973         test_must_fail git merge empty-base &&
974         test_must_be_empty empty2
975 '
976
977 test_done