1#!/bin/sh
2
3test_description='Merge-recursive merging renames'
4. ./test-lib.sh
5
6modify () {
7 sed -e "$1" <"$2" >"$2.x" &&
8 mv "$2.x" "$2"
9}
10
11test_expect_success setup \
12'
13cat >A <<\EOF &&
14a aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
15b bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
16c cccccccccccccccccccccccccccccccccccccccccccccccc
17d dddddddddddddddddddddddddddddddddddddddddddddddd
18e eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
19f ffffffffffffffffffffffffffffffffffffffffffffffff
20g gggggggggggggggggggggggggggggggggggggggggggggggg
21h hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh
22i iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
23j jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj
24k kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
25l llllllllllllllllllllllllllllllllllllllllllllllll
26m mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
27n nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn
28o oooooooooooooooooooooooooooooooooooooooooooooooo
29EOF
30
31cat >M <<\EOF &&
32A AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
33B BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
34C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
35D DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
36E EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
37F FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
38G GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG
39H HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
40I IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII
41J JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ
42K KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK
43L LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL
44M MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
45N NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN
46O OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
47EOF
48
49git add A M &&
50git commit -m "initial has A and M" &&
51git branch white &&
52git branch red &&
53git branch blue &&
54git branch yellow &&
55git branch change &&
56git branch change+rename &&
57
58sed -e "/^g /s/.*/g : master changes a line/" <A >A+ &&
59mv A+ A &&
60git commit -a -m "master updates A" &&
61
62git checkout yellow &&
63rm -f M &&
64git commit -a -m "yellow removes M" &&
65
66git checkout white &&
67sed -e "/^g /s/.*/g : white changes a line/" <A >B &&
68sed -e "/^G /s/.*/G : colored branch changes a line/" <M >N &&
69rm -f A M &&
70git update-index --add --remove A B M N &&
71git commit -m "white renames A->B, M->N" &&
72
73git checkout red &&
74sed -e "/^g /s/.*/g : red changes a line/" <A >B &&
75sed -e "/^G /s/.*/G : colored branch changes a line/" <M >N &&
76rm -f A M &&
77git update-index --add --remove A B M N &&
78git commit -m "red renames A->B, M->N" &&
79
80git checkout blue &&
81sed -e "/^g /s/.*/g : blue changes a line/" <A >C &&
82sed -e "/^G /s/.*/G : colored branch changes a line/" <M >N &&
83rm -f A M &&
84git update-index --add --remove A C M N &&
85git commit -m "blue renames A->C, M->N" &&
86
87git checkout change &&
88sed -e "/^g /s/.*/g : changed line/" <A >A+ &&
89mv A+ A &&
90git commit -q -a -m "changed" &&
91
92git checkout change+rename &&
93sed -e "/^g /s/.*/g : changed line/" <A >B &&
94rm A &&
95git update-index --add B &&
96git commit -q -a -m "changed and renamed" &&
97
98git checkout master'
99
100test_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
116test_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
133test_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
149test_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
169test_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
177test_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
187test_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
198test_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
211test_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
225test_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
241test_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
251test_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
288printf "1\n2\n3\n4\n5555\n6\n7\n8\n9\n10\n11\n" >expected
289
290test_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
299test_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
320test_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
342cat >expected <<\EOF &&
3431
3442
3453
3464
3475
3486
3497
3508
3519
35210
353<<<<<<< HEAD
35412
355=======
35611
357>>>>>>> dir-not-in-way
358EOF
359
360test_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
376test_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
396cat >expected <<\EOF &&
3971
3982
3993
4004
4015
4026
4037
4048
4059
40610
407<<<<<<< HEAD
40811
409=======
41012
411>>>>>>> renamed-file-has-conflicts
412EOF
413
414test_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
432test_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
454test_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
470test_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
494test_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
514test_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
533test_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
557test_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
575test_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
597test_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
612test_done