Merge branch 'jn/fast-import-blob-access' into next
[git] / t / t6022-merge-rename.sh
1 #!/bin/sh
2
3 test_description='Merge-recursive merging renames'
4 . ./test-lib.sh
5
6 modify () {
7         sed -e "$1" <"$2" >"$2.x" &&
8         mv "$2.x" "$2"
9 }
10
11 test_expect_success setup \
12 '
13 cat >A <<\EOF &&
14 a aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
15 b bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
16 c cccccccccccccccccccccccccccccccccccccccccccccccc
17 d dddddddddddddddddddddddddddddddddddddddddddddddd
18 e eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
19 f ffffffffffffffffffffffffffffffffffffffffffffffff
20 g gggggggggggggggggggggggggggggggggggggggggggggggg
21 h hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh
22 i iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
23 j jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj
24 k kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
25 l llllllllllllllllllllllllllllllllllllllllllllllll
26 m mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
27 n nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn
28 o oooooooooooooooooooooooooooooooooooooooooooooooo
29 EOF
30
31 cat >M <<\EOF &&
32 A AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
33 B BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
34 C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
35 D DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
36 E EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
37 F FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
38 G GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG
39 H HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
40 I IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII
41 J JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ
42 K KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK
43 L LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL
44 M MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
45 N NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN
46 O OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
47 EOF
48
49 git add A M &&
50 git commit -m "initial has A and M" &&
51 git branch white &&
52 git branch red &&
53 git branch blue &&
54 git branch yellow &&
55 git branch change &&
56 git branch change+rename &&
57
58 sed -e "/^g /s/.*/g : master changes a line/" <A >A+ &&
59 mv A+ A &&
60 git commit -a -m "master updates A" &&
61
62 git checkout yellow &&
63 rm -f M &&
64 git commit -a -m "yellow removes M" &&
65
66 git checkout white &&
67 sed -e "/^g /s/.*/g : white changes a line/" <A >B &&
68 sed -e "/^G /s/.*/G : colored branch changes a line/" <M >N &&
69 rm -f A M &&
70 git update-index --add --remove A B M N &&
71 git commit -m "white renames A->B, M->N" &&
72
73 git checkout red &&
74 sed -e "/^g /s/.*/g : red changes a line/" <A >B &&
75 sed -e "/^G /s/.*/G : colored branch changes a line/" <M >N &&
76 rm -f A M &&
77 git update-index --add --remove A B M N &&
78 git commit -m "red renames A->B, M->N" &&
79
80 git checkout blue &&
81 sed -e "/^g /s/.*/g : blue changes a line/" <A >C &&
82 sed -e "/^G /s/.*/G : colored branch changes a line/" <M >N &&
83 rm -f A M &&
84 git update-index --add --remove A C M N &&
85 git commit -m "blue renames A->C, M->N" &&
86
87 git checkout change &&
88 sed -e "/^g /s/.*/g : changed line/" <A >A+ &&
89 mv A+ A &&
90 git commit -q -a -m "changed" &&
91
92 git checkout change+rename &&
93 sed -e "/^g /s/.*/g : changed line/" <A >B &&
94 rm A &&
95 git update-index --add B &&
96 git commit -q -a -m "changed and renamed" &&
97
98 git checkout master'
99
100 test_expect_success 'pull renaming branch into unrenaming one' \
101 '
102         git show-branch &&
103         test_expect_code 1 git pull . white &&
104         git ls-files -s &&
105         git ls-files -u B >b.stages &&
106         test_line_count = 3 b.stages &&
107         git ls-files -s N >n.stages &&
108         test_line_count = 1 n.stages &&
109         sed -ne "/^g/{
110         p
111         q
112         }" B | grep master &&
113         git diff --exit-code white N
114 '
115
116 test_expect_success 'pull renaming branch into another renaming one' \
117 '
118         rm -f B &&
119         git reset --hard &&
120         git checkout red &&
121         test_expect_code 1 git pull . white &&
122         git ls-files -u B >b.stages &&
123         test_line_count = 3 b.stages &&
124         git ls-files -s N >n.stages &&
125         test_line_count = 1 n.stages &&
126         sed -ne "/^g/{
127         p
128         q
129         }" B | grep red &&
130         git diff --exit-code white N
131 '
132
133 test_expect_success 'pull unrenaming branch into renaming one' \
134 '
135         git reset --hard &&
136         git show-branch &&
137         test_expect_code 1 git pull . master &&
138         git ls-files -u B >b.stages &&
139         test_line_count = 3 b.stages &&
140         git ls-files -s N >n.stages &&
141         test_line_count = 1 n.stages &&
142         sed -ne "/^g/{
143         p
144         q
145         }" B | grep red &&
146         git diff --exit-code white N
147 '
148
149 test_expect_success 'pull conflicting renames' \
150 '
151         git reset --hard &&
152         git show-branch &&
153         test_expect_code 1 git pull . blue &&
154         git ls-files -u A >a.stages &&
155         test_line_count = 1 a.stages &&
156         git ls-files -u B >b.stages &&
157         test_line_count = 1 b.stages &&
158         git ls-files -u C >c.stages &&
159         test_line_count = 1 c.stages &&
160         git ls-files -s N >n.stages &&
161         test_line_count = 1 n.stages &&
162         sed -ne "/^g/{
163         p
164         q
165         }" B | grep red &&
166         git diff --exit-code white N
167 '
168
169 test_expect_success 'interference with untracked working tree file' '
170         git reset --hard &&
171         git show-branch &&
172         echo >A this file should not matter &&
173         test_expect_code 1 git pull . white &&
174         test_path_is_file A
175 '
176
177 test_expect_success 'interference with untracked working tree file' '
178         git reset --hard &&
179         git checkout white &&
180         git show-branch &&
181         rm -f A &&
182         echo >A this file should not matter &&
183         test_expect_code 1 git pull . red &&
184         test_path_is_file A
185 '
186
187 test_expect_success 'interference with untracked working tree file' '
188         git reset --hard &&
189         rm -f A M &&
190         git checkout -f master &&
191         git tag -f anchor &&
192         git show-branch &&
193         git pull . yellow &&
194         test_path_is_missing M &&
195         git reset --hard anchor
196 '
197
198 test_expect_success 'updated working tree file should prevent the merge' '
199         git reset --hard &&
200         rm -f A M &&
201         git checkout -f master &&
202         git tag -f anchor &&
203         git show-branch &&
204         echo >>M one line addition &&
205         cat M >M.saved &&
206         test_expect_code 128 git pull . yellow &&
207         test_cmp M M.saved &&
208         rm -f M.saved
209 '
210
211 test_expect_success 'updated working tree file should prevent the merge' '
212         git reset --hard &&
213         rm -f A M &&
214         git checkout -f master &&
215         git tag -f anchor &&
216         git show-branch &&
217         echo >>M one line addition &&
218         cat M >M.saved &&
219         git update-index M &&
220         test_expect_code 128 git pull . yellow &&
221         test_cmp M M.saved &&
222         rm -f M.saved
223 '
224
225 test_expect_success 'interference with untracked working tree file' '
226         git reset --hard &&
227         rm -f A M &&
228         git checkout -f yellow &&
229         git tag -f anchor &&
230         git show-branch &&
231         echo >M this file should not matter &&
232         git pull . master &&
233         test_path_is_file M &&
234         ! {
235                 git ls-files -s |
236                 grep M
237         } &&
238         git reset --hard anchor
239 '
240
241 test_expect_success 'merge of identical changes in a renamed file' '
242         rm -f A M N &&
243         git reset --hard &&
244         git checkout change+rename &&
245         GIT_MERGE_VERBOSITY=3 git merge change | grep "^Skipped B" &&
246         git reset --hard HEAD^ &&
247         git checkout change &&
248         GIT_MERGE_VERBOSITY=3 git merge change+rename | grep "^Skipped B"
249 '
250
251 test_expect_success 'setup for rename + d/f conflicts' '
252         git reset --hard &&
253         git checkout --orphan dir-in-way &&
254         git rm -rf . &&
255
256         mkdir sub &&
257         mkdir dir &&
258         printf "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n" >sub/file &&
259         echo foo >dir/file-in-the-way &&
260         git add -A &&
261         git commit -m "Common commmit" &&
262
263         echo 11 >>sub/file &&
264         echo more >>dir/file-in-the-way &&
265         git add -u &&
266         git commit -m "Commit to merge, with dir in the way" &&
267
268         git checkout -b dir-not-in-way &&
269         git reset --soft HEAD^ &&
270         git rm -rf dir &&
271         git commit -m "Commit to merge, with dir removed" -- dir sub/file &&
272
273         git checkout -b renamed-file-has-no-conflicts dir-in-way~1 &&
274         git rm -rf dir &&
275         git rm sub/file &&
276         printf "1\n2\n3\n4\n5555\n6\n7\n8\n9\n10\n" >dir &&
277         git add dir &&
278         git commit -m "Independent change" &&
279
280         git checkout -b renamed-file-has-conflicts dir-in-way~1 &&
281         git rm -rf dir &&
282         git mv sub/file dir &&
283         echo 12 >>dir &&
284         git add dir &&
285         git commit -m "Conflicting change"
286 '
287
288 printf "1\n2\n3\n4\n5555\n6\n7\n8\n9\n10\n11\n" >expected
289
290 test_expect_success 'Rename+D/F conflict; renamed file merges + dir not in way' '
291         git reset --hard &&
292         git checkout -q renamed-file-has-no-conflicts^0 &&
293         git merge --strategy=recursive dir-not-in-way &&
294         git diff --quiet &&
295         test -f dir &&
296         test_cmp expected dir
297 '
298
299 test_expect_success 'Rename+D/F conflict; renamed file merges but dir in way' '
300         git reset --hard &&
301         rm -rf dir~* &&
302         git checkout -q renamed-file-has-no-conflicts^0 &&
303         test_must_fail git merge --strategy=recursive dir-in-way >output &&
304
305         grep "CONFLICT (delete/modify): dir/file-in-the-way" output &&
306         grep "Auto-merging dir" output &&
307         grep "Adding as dir~HEAD instead" output &&
308
309         test 2 -eq "$(git ls-files -u | wc -l)" &&
310         test 2 -eq "$(git ls-files -u dir/file-in-the-way | wc -l)" &&
311
312         test_must_fail git diff --quiet &&
313         test_must_fail git diff --cached --quiet &&
314
315         test -f dir/file-in-the-way &&
316         test -f dir~HEAD &&
317         test_cmp expected dir~HEAD
318 '
319
320 test_expect_success 'Same as previous, but merged other way' '
321         git reset --hard &&
322         rm -rf dir~* &&
323         git checkout -q dir-in-way^0 &&
324         test_must_fail git merge --strategy=recursive renamed-file-has-no-conflicts >output 2>errors &&
325
326         ! grep "error: refusing to lose untracked file at" errors &&
327         grep "CONFLICT (delete/modify): dir/file-in-the-way" output &&
328         grep "Auto-merging dir" output &&
329         grep "Adding as dir~renamed-file-has-no-conflicts instead" output &&
330
331         test 2 -eq "$(git ls-files -u | wc -l)" &&
332         test 2 -eq "$(git ls-files -u dir/file-in-the-way | wc -l)" &&
333
334         test_must_fail git diff --quiet &&
335         test_must_fail git diff --cached --quiet &&
336
337         test -f dir/file-in-the-way &&
338         test -f dir~renamed-file-has-no-conflicts &&
339         test_cmp expected dir~renamed-file-has-no-conflicts
340 '
341
342 cat >expected <<\EOF &&
343 1
344 2
345 3
346 4
347 5
348 6
349 7
350 8
351 9
352 10
353 <<<<<<< HEAD
354 12
355 =======
356 11
357 >>>>>>> dir-not-in-way
358 EOF
359
360 test_expect_success 'Rename+D/F conflict; renamed file cannot merge, dir not in way' '
361         git reset --hard &&
362         rm -rf dir~* &&
363         git checkout -q renamed-file-has-conflicts^0 &&
364         test_must_fail git merge --strategy=recursive dir-not-in-way &&
365
366         test 3 -eq "$(git ls-files -u | wc -l)" &&
367         test 3 -eq "$(git ls-files -u dir | wc -l)" &&
368
369         test_must_fail git diff --quiet &&
370         test_must_fail git diff --cached --quiet &&
371
372         test -f dir &&
373         test_cmp expected dir
374 '
375
376 test_expect_success 'Rename+D/F conflict; renamed file cannot merge and dir in the way' '
377         modify s/dir-not-in-way/dir-in-way/ expected &&
378
379         git reset --hard &&
380         rm -rf dir~* &&
381         git checkout -q renamed-file-has-conflicts^0 &&
382         test_must_fail git merge --strategy=recursive dir-in-way &&
383
384         test 5 -eq "$(git ls-files -u | wc -l)" &&
385         test 3 -eq "$(git ls-files -u dir | grep -v file-in-the-way | wc -l)" &&
386         test 2 -eq "$(git ls-files -u dir/file-in-the-way | wc -l)" &&
387
388         test_must_fail git diff --quiet &&
389         test_must_fail git diff --cached --quiet &&
390
391         test -f dir/file-in-the-way &&
392         test -f dir~HEAD &&
393         test_cmp expected dir~HEAD
394 '
395
396 cat >expected <<\EOF &&
397 1
398 2
399 3
400 4
401 5
402 6
403 7
404 8
405 9
406 10
407 <<<<<<< HEAD
408 11
409 =======
410 12
411 >>>>>>> renamed-file-has-conflicts
412 EOF
413
414 test_expect_success 'Same as previous, but merged other way' '
415         git reset --hard &&
416         rm -rf dir~* &&
417         git checkout -q dir-in-way^0 &&
418         test_must_fail git merge --strategy=recursive renamed-file-has-conflicts &&
419
420         test 5 -eq "$(git ls-files -u | wc -l)" &&
421         test 3 -eq "$(git ls-files -u dir | grep -v file-in-the-way | wc -l)" &&
422         test 2 -eq "$(git ls-files -u dir/file-in-the-way | wc -l)" &&
423
424         test_must_fail git diff --quiet &&
425         test_must_fail git diff --cached --quiet &&
426
427         test -f dir/file-in-the-way &&
428         test -f dir~renamed-file-has-conflicts &&
429         test_cmp expected dir~renamed-file-has-conflicts
430 '
431
432 test_expect_success 'setup both rename source and destination involved in D/F conflict' '
433         git reset --hard &&
434         git checkout --orphan rename-dest &&
435         git rm -rf . &&
436         git clean -fdqx &&
437
438         mkdir one &&
439         echo stuff >one/file &&
440         git add -A &&
441         git commit -m "Common commmit" &&
442
443         git mv one/file destdir &&
444         git commit -m "Renamed to destdir" &&
445
446         git checkout -b source-conflict HEAD~1 &&
447         git rm -rf one &&
448         mkdir destdir &&
449         touch one destdir/foo &&
450         git add -A &&
451         git commit -m "Conflicts in the way"
452 '
453
454 test_expect_success 'both rename source and destination involved in D/F conflict' '
455         git reset --hard &&
456         rm -rf dir~* &&
457         git checkout -q rename-dest^0 &&
458         test_must_fail git merge --strategy=recursive source-conflict &&
459
460         test 1 -eq "$(git ls-files -u | wc -l)" &&
461
462         test_must_fail git diff --quiet &&
463
464         test -f destdir/foo &&
465         test -f one &&
466         test -f destdir~HEAD &&
467         test "stuff" = "$(cat destdir~HEAD)"
468 '
469
470 test_expect_success 'setup pair rename to parent of other (D/F conflicts)' '
471         git reset --hard &&
472         git checkout --orphan rename-two &&
473         git rm -rf . &&
474         git clean -fdqx &&
475
476         mkdir one &&
477         mkdir two &&
478         echo stuff >one/file &&
479         echo other >two/file &&
480         git add -A &&
481         git commit -m "Common commmit" &&
482
483         git rm -rf one &&
484         git mv two/file one &&
485         git commit -m "Rename two/file -> one" &&
486
487         git checkout -b rename-one HEAD~1 &&
488         git rm -rf two &&
489         git mv one/file two &&
490         rm -r one &&
491         git commit -m "Rename one/file -> two"
492 '
493
494 test_expect_success 'pair rename to parent of other (D/F conflicts) w/ untracked dir' '
495         git checkout -q rename-one^0 &&
496         mkdir one &&
497         test_must_fail git merge --strategy=recursive rename-two &&
498
499         test 2 -eq "$(git ls-files -u | wc -l)" &&
500         test 1 -eq "$(git ls-files -u one | wc -l)" &&
501         test 1 -eq "$(git ls-files -u two | wc -l)" &&
502
503         test_must_fail git diff --quiet &&
504
505         test 4 -eq $(find . | grep -v .git | wc -l) &&
506
507         test -d one &&
508         test -f one~rename-two &&
509         test -f two &&
510         test "other" = $(cat one~rename-two) &&
511         test "stuff" = $(cat two)
512 '
513
514 test_expect_success 'pair rename to parent of other (D/F conflicts) w/ clean start' '
515         git reset --hard &&
516         git clean -fdqx &&
517         test_must_fail git merge --strategy=recursive rename-two &&
518
519         test 2 -eq "$(git ls-files -u | wc -l)" &&
520         test 1 -eq "$(git ls-files -u one | wc -l)" &&
521         test 1 -eq "$(git ls-files -u two | wc -l)" &&
522
523         test_must_fail git diff --quiet &&
524
525         test 3 -eq $(find . | grep -v .git | wc -l) &&
526
527         test -f one &&
528         test -f two &&
529         test "other" = $(cat one) &&
530         test "stuff" = $(cat two)
531 '
532
533 test_expect_success 'setup rename of one file to two, with directories in the way' '
534         git reset --hard &&
535         git checkout --orphan first-rename &&
536         git rm -rf . &&
537         git clean -fdqx &&
538
539         echo stuff >original &&
540         git add -A &&
541         git commit -m "Common commmit" &&
542
543         mkdir two &&
544         >two/file &&
545         git add two/file &&
546         git mv original one &&
547         git commit -m "Put two/file in the way, rename to one" &&
548
549         git checkout -b second-rename HEAD~1 &&
550         mkdir one &&
551         >one/file &&
552         git add one/file &&
553         git mv original two &&
554         git commit -m "Put one/file in the way, rename to two"
555 '
556
557 test_expect_success 'check handling of differently renamed file with D/F conflicts' '
558         git checkout -q first-rename^0 &&
559         test_must_fail git merge --strategy=recursive second-rename &&
560
561         test 5 -eq "$(git ls-files -s | wc -l)" &&
562         test 3 -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         test 1 -eq "$(git ls-files -u original | wc -l)" &&
566         test 2 -eq "$(git ls-files -o | wc -l)" &&
567
568         test -f one/file &&
569         test -f two/file &&
570         test -f one~HEAD &&
571         test -f two~second-rename &&
572         ! test -f original
573 '
574
575 test_expect_success 'setup rename one file to two; directories moving out of the way' '
576         git reset --hard &&
577         git checkout --orphan first-rename-redo &&
578         git rm -rf . &&
579         git clean -fdqx &&
580
581         echo stuff >original &&
582         mkdir one two &&
583         touch one/file two/file &&
584         git add -A &&
585         git commit -m "Common commmit" &&
586
587         git rm -rf one &&
588         git mv original one &&
589         git commit -m "Rename to one" &&
590
591         git checkout -b second-rename-redo HEAD~1 &&
592         git rm -rf two &&
593         git mv original two &&
594         git commit -m "Rename to two"
595 '
596
597 test_expect_success 'check handling of differently renamed file with D/F conflicts' '
598         git checkout -q first-rename-redo^0 &&
599         test_must_fail git merge --strategy=recursive second-rename-redo &&
600
601         test 3 -eq "$(git ls-files -u | wc -l)" &&
602         test 1 -eq "$(git ls-files -u one | wc -l)" &&
603         test 1 -eq "$(git ls-files -u two | wc -l)" &&
604         test 1 -eq "$(git ls-files -u original | wc -l)" &&
605         test 0 -eq "$(git ls-files -o | wc -l)" &&
606
607         test -f one &&
608         test -f two &&
609         ! test -f original
610 '
611
612 test_done