3 test_description
="remember regular & dir renames in sequence of merges"
5 TEST_PASSES_SANITIZE_LEAK
=true
9 # NOTE 1: this testfile tends to not only rename files, but modify on both
10 # sides; without modifying on both sides, optimizations can kick in
11 # which make rename detection irrelevant or trivial. We want to make
12 # sure that we are triggering rename caching rather than rename
15 # NOTE 2: this testfile uses 'test-tool fast-rebase' instead of either
16 # cherry-pick or rebase. sequencer.c is only superficially
17 # integrated with merge-ort; it calls merge_switch_to_result()
18 # after EACH merge, which updates the index and working copy AND
19 # throws away the cached results (because merge_switch_to_result()
20 # is only supposed to be called at the end of the sequence).
21 # Integrating them more deeply is a big task, so for now the tests
22 # use 'test-tool fast-rebase'.
27 # In the following simple testcase:
28 # Base: numbers_1, values_1
29 # Upstream: numbers_2, values_2
32 # or, in english, rename numbers -> sequence in the first commit, and rename
33 # values -> scruples in the second commit.
35 # This shouldn't be a challenge, it's just verifying that cached renames isn't
36 # preventing us from finding new renames.
38 test_expect_success
'caching renames does not preclude finding new ones' '
39 git init caching-renames-and-new-renames &&
41 cd caching-renames-and-new-renames &&
43 test_seq 2 10 >numbers &&
44 test_seq 2 10 >values &&
45 git add numbers values &&
48 git branch upstream &&
51 git switch upstream &&
52 test_seq 1 10 >numbers &&
53 test_seq 1 10 >values &&
54 git add numbers values &&
55 git commit -m "Tweaked both files" &&
59 test_seq 2 12 >numbers &&
61 git mv numbers sequence &&
64 test_seq 2 12 >values &&
66 git mv values scruples &&
73 git switch upstream &&
75 git replay --onto HEAD upstream~1..topic >out &&
76 git update-ref --stdin <out &&
79 git ls-files >tracked-files &&
80 test_line_count = 2 tracked-files &&
81 test_seq 1 12 >expect &&
82 test_cmp expect sequence &&
83 test_cmp expect scruples
88 # In the following testcase:
90 # Upstream: rename numbers_1 -> sequence_2
93 # or, in english, the first commit on the topic branch modifies numbers by
94 # shrinking it (dramatically) and the second commit on topic reverts its
97 # Can git apply both patches?
99 # Traditional cherry-pick/rebase will fail to apply the second commit, the
100 # one that reverted its parent, because despite detecting the rename from
101 # 'numbers' to 'sequence' for the first commit, it fails to detect that
102 # rename when picking the second commit. That's "reasonable" given the
103 # dramatic change in size of the file, but remembering the rename and
104 # reusing it is reasonable too.
106 # We do test here that we expect rename detection to only be run once total
107 # (the topic side of history doesn't need renames, and with caching we
108 # should be able to only run rename detection on the upstream side one
110 test_expect_success
'cherry-pick both a commit and its immediate revert' '
111 git init pick-commit-and-its-immediate-revert &&
113 cd pick-commit-and-its-immediate-revert &&
115 test_seq 11 30 >numbers &&
117 git commit -m orig &&
119 git branch upstream &&
122 git switch upstream &&
123 test_seq 1 30 >numbers &&
125 git mv numbers sequence &&
126 git commit -m "Renamed (and modified) numbers -> sequence" &&
130 test_seq 11 13 >numbers &&
140 git switch upstream &&
142 GIT_TRACE2_PERF="$(pwd)/trace.output" &&
143 export GIT_TRACE2_PERF &&
145 git replay --onto HEAD upstream~1..topic >out &&
146 git update-ref --stdin <out &&
147 git checkout topic &&
149 grep region_enter.*diffcore_rename trace.output >calls &&
150 test_line_count = 1 calls
155 # In the following testcase:
157 # Upstream: rename sequence_1 -> values_2
158 # Topic_1: rename sequence_1 -> values_3
159 # Topic_2: add unrelated sequence_4
160 # or, in english, both sides rename sequence -> values, and then the second
161 # commit on the topic branch adds an unrelated file called sequence.
163 # This testcase presents no problems for git traditionally, but having both
164 # sides do the same rename in effect "uses it up" and if it remains cached,
165 # could cause a spurious rename/add conflict.
167 test_expect_success
'rename same file identically, then reintroduce it' '
168 git init rename-rename-1to1-then-add-old-filename &&
170 cd rename-rename-1to1-then-add-old-filename &&
172 test_seq 3 8 >sequence &&
174 git commit -m orig &&
176 git branch upstream &&
179 git switch upstream &&
180 test_seq 1 8 >sequence &&
182 git mv sequence values &&
183 git commit -m "Renamed (and modified) sequence -> values" &&
187 test_seq 3 10 >sequence &&
189 git mv sequence values &&
192 test_write_lines A B C D E F G H I J >sequence &&
200 git switch upstream &&
202 GIT_TRACE2_PERF="$(pwd)/trace.output" &&
203 export GIT_TRACE2_PERF &&
205 git replay --onto HEAD upstream~1..topic >out &&
206 git update-ref --stdin <out &&
207 git checkout topic &&
209 git ls-files >tracked &&
210 test_line_count = 2 tracked &&
211 test_path_is_file values &&
212 test_path_is_file sequence &&
214 grep region_enter.*diffcore_rename trace.output >calls &&
215 test_line_count = 2 calls
220 # In the following testcase:
221 # Base: olddir/{valuesZ_1, valuesY_1, valuesX_1}
222 # Upstream: rename olddir/valuesZ_1 -> dirA/valuesZ_2
223 # rename olddir/valuesY_1 -> dirA/valuesY_2
224 # rename olddir/valuesX_1 -> dirB/valuesX_2
225 # Topic_1: rename olddir/valuesZ_1 -> dirA/valuesZ_3
226 # rename olddir/valuesY_1 -> dirA/valuesY_3
227 # Topic_2: add olddir/newfile
228 # Expected Pick1: dirA/{valuesZ, valuesY}, dirB/valuesX
229 # Expected Pick2: dirA/{valuesZ, valuesY}, dirB/{valuesX, newfile}
231 # This testcase presents no problems for git traditionally, but having both
232 # sides do the same renames in effect "use it up" but if the renames remain
233 # cached, the directory rename could put newfile in the wrong directory.
235 test_expect_success
'rename same file identically, then add file to old dir' '
236 git init rename-rename-1to1-then-add-file-to-old-dir &&
238 cd rename-rename-1to1-then-add-file-to-old-dir &&
241 test_seq 3 8 >olddir/valuesZ &&
242 test_seq 3 8 >olddir/valuesY &&
243 test_seq 3 8 >olddir/valuesX &&
245 git commit -m orig &&
247 git branch upstream &&
250 git switch upstream &&
251 test_seq 1 8 >olddir/valuesZ &&
252 test_seq 1 8 >olddir/valuesY &&
253 test_seq 1 8 >olddir/valuesX &&
256 git mv olddir/valuesZ olddir/valuesY dirA &&
257 git mv olddir/ dirB/ &&
258 git commit -m "Renamed (and modified) values*" &&
262 test_seq 3 10 >olddir/valuesZ &&
263 test_seq 3 10 >olddir/valuesY &&
266 git mv olddir/valuesZ olddir/valuesY dirA &&
270 git add olddir/newfile &&
277 git switch upstream &&
278 git config merge.directoryRenames true &&
280 GIT_TRACE2_PERF="$(pwd)/trace.output" &&
281 export GIT_TRACE2_PERF &&
283 git replay --onto HEAD upstream~1..topic >out &&
284 git update-ref --stdin <out &&
285 git checkout topic &&
287 git ls-files >tracked &&
288 test_line_count = 4 tracked &&
289 test_path_is_file dirA/valuesZ &&
290 test_path_is_file dirA/valuesY &&
291 test_path_is_file dirB/valuesX &&
292 test_path_is_file dirB/newfile &&
294 grep region_enter.*diffcore_rename trace.output >calls &&
295 test_line_count = 3 calls
300 # In the following testcase, upstream renames a directory, and the topic branch
301 # first adds a file to the directory, then later renames the directory
305 # Upstream: rename olddir/ -> newdir/
306 # Topic_1: add olddir/newfile
307 # Topic_2: rename olddir/ -> otherdir/
309 # Here we are just concerned that cached renames might prevent us from seeing
310 # the rename conflict, and we want to ensure that we do get a conflict.
312 # While at it, though, we do test that we only try to detect renames 2
313 # times and not three. (The first merge needs to detect renames on the
314 # upstream side. Traditionally, the second merge would need to detect
315 # renames on both sides of history, but our caching of upstream renames
316 # should avoid the need to re-detect upstream renames.)
318 test_expect_success
'cached dir rename does not prevent noticing later conflict' '
319 git init dir-rename-cache-not-occluding-later-conflict &&
321 cd dir-rename-cache-not-occluding-later-conflict &&
324 test_seq 3 10 >olddir/a &&
325 test_seq 3 10 >olddir/b &&
327 git commit -m orig &&
329 git branch upstream &&
332 git switch upstream &&
333 test_seq 3 10 >olddir/a &&
334 test_seq 3 10 >olddir/b &&
336 git mv olddir newdir &&
337 git commit -m "Dir renamed" &&
342 git add olddir/newfile &&
345 test_seq 1 8 >olddir/a &&
346 test_seq 1 8 >olddir/b &&
348 git mv olddir otherdir &&
355 git switch upstream &&
356 git config merge.directoryRenames true &&
358 GIT_TRACE2_PERF="$(pwd)/trace.output" &&
359 export GIT_TRACE2_PERF &&
361 test_must_fail git replay --onto HEAD upstream~1..topic >output &&
363 grep region_enter.*diffcore_rename trace.output >calls &&
364 test_line_count = 2 calls
368 # Helper for the next two tests
369 test_setup_upstream_rename
() {
374 test_seq
3 8 >somefile
&&
375 test_seq
3 8 >relevant-rename
&&
376 git add somefile relevant-rename
&&
378 test_write_lines a b c d e f g
>olddir
/a
&&
379 test_write_lines z y x w v u t
>olddir
/b
&&
381 git commit
-m orig
&&
383 git branch upstream
&&
386 git switch upstream
&&
387 test_seq
1 8 >somefile
&&
388 test_seq
1 8 >relevant-rename
&&
389 git add somefile relevant-rename
&&
390 git
mv relevant-rename renamed
&&
394 git
mv olddir newdir
&&
395 git commit
-m "Dir renamed"
400 # In the following testcase, upstream renames a file in the toplevel directory
401 # as well as its only directory:
402 # Base: relevant-rename_1
406 # Upstream: rename relevant-rename_1 -> renamed_2
407 # rename olddir/ -> newdir/
408 # Topic_1: relevant-rename_3
409 # Topic_2: olddir/newfile_1
410 # Topic_3: olddir/newfile_2
412 # In this testcase, since the first commit being picked only modifies a
413 # file in the toplevel directory, the directory rename is irrelevant for
414 # that first merge. However, we need to notice the directory rename for
415 # the merge that picks the second commit, and we don't want the third
416 # commit to mess up its location either. We want to make sure that
417 # olddir/newfile doesn't exist in the result and that newdir/newfile does.
419 # We also test that we only do rename detection twice. We never need
420 # rename detection on the topic side of history, but we do need it twice on
421 # the upstream side of history. For the first topic commit, we only need
423 # relevant-rename -> renamed
424 # rename, because olddir is unmodified by Topic_1. For Topic_2, however,
425 # the new file being added to olddir means files that were previously
426 # irrelevant for rename detection are now relevant, forcing us to repeat
427 # rename detection for the paths we don't already have cached. Topic_3 also
428 # tweaks olddir/newfile, but the renames in olddir/ will have been cached
429 # from the second rename detection run.
431 test_expect_success
'dir rename unneeded, then add new file to old dir' '
432 test_setup_upstream_rename dir-rename-unneeded-until-new-file &&
434 cd dir-rename-unneeded-until-new-file &&
438 test_seq 3 10 >relevant-rename &&
439 git add relevant-rename &&
442 echo foo >olddir/newfile &&
443 git add olddir/newfile &&
446 echo bar >>olddir/newfile &&
447 git add olddir/newfile &&
454 git switch upstream &&
455 git config merge.directoryRenames true &&
457 GIT_TRACE2_PERF="$(pwd)/trace.output" &&
458 export GIT_TRACE2_PERF &&
460 git replay --onto HEAD upstream~1..topic >out &&
461 git update-ref --stdin <out &&
462 git checkout topic &&
464 grep region_enter.*diffcore_rename trace.output >calls &&
465 test_line_count = 2 calls &&
467 git ls-files >tracked &&
468 test_line_count = 5 tracked &&
469 test_path_is_missing olddir/newfile &&
470 test_path_is_file newdir/newfile
475 # The following testcase is *very* similar to the last one, but instead of
476 # adding a new olddir/newfile, it renames somefile -> olddir/newfile:
477 # Base: relevant-rename_1
481 # Upstream: rename relevant-rename_1 -> renamed_2
482 # rename olddir/ -> newdir/
483 # Topic_1: relevant-rename_3
484 # Topic_2: rename somefile -> olddir/newfile_2
485 # Topic_3: modify olddir/newfile_3
487 # In this testcase, since the first commit being picked only modifies a
488 # file in the toplevel directory, the directory rename is irrelevant for
489 # that first merge. However, we need to notice the directory rename for
490 # the merge that picks the second commit, and we don't want the third
491 # commit to mess up its location either. We want to make sure that
492 # neither somefile or olddir/newfile exists in the result and that
493 # newdir/newfile does.
495 # This testcase needs one more call to rename detection than the last
496 # testcase, because of the somefile -> olddir/newfile rename in Topic_2.
497 test_expect_success
'dir rename unneeded, then rename existing file into old dir' '
498 test_setup_upstream_rename dir-rename-unneeded-until-file-moved-inside &&
500 cd dir-rename-unneeded-until-file-moved-inside &&
504 test_seq 3 10 >relevant-rename &&
505 git add relevant-rename &&
508 test_seq 1 10 >somefile &&
510 git mv somefile olddir/newfile &&
513 test_seq 1 12 >olddir/newfile &&
514 git add olddir/newfile &&
521 git switch upstream &&
522 git config merge.directoryRenames true &&
524 GIT_TRACE2_PERF="$(pwd)/trace.output" &&
525 export GIT_TRACE2_PERF &&
527 git replay --onto HEAD upstream~1..topic >out &&
528 git update-ref --stdin <out &&
529 git checkout topic &&
531 grep region_enter.*diffcore_rename trace.output >calls &&
532 test_line_count = 3 calls &&
534 test_path_is_missing somefile &&
535 test_path_is_missing olddir/newfile &&
536 test_path_is_file newdir/newfile &&
537 git ls-files >tracked &&
538 test_line_count = 4 tracked
542 # Helper for the next two tests
543 test_setup_topic_rename
() {
548 test_seq
3 8 >somefile
&&
550 test_seq
3 8 >olddir
/a
&&
552 git add olddir somefile
&&
553 git commit
-m orig
&&
555 git branch upstream
&&
559 test_seq
1 8 >somefile
&&
560 test_seq
1 8 >olddir
/a
&&
561 git add somefile olddir
/a
&&
562 git
mv olddir newdir
&&
563 git commit
-m "Dir renamed" &&
565 test_seq
1 10 >somefile
&&
568 >olddir
/unrelated-file
&&
570 git commit
-m "Unrelated file in recreated old dir"
575 # In the following testcase, the first commit on the topic branch renames
576 # a directory, while the second recreates the old directory and places a
581 # Upstream: olddir/newfile
582 # Topic_1: somefile_2
583 # rename olddir/ -> newdir/
584 # Topic_2: olddir/unrelated-file
586 # Note that the first pick should merge:
589 # Upstream: olddir/newfile
590 # Topic_1: rename olddir/ -> newdir/
591 # For which the expected result (assuming merge.directoryRenames=true) is
594 # newdir/{a, b, newfile}
596 # While the second pick does the following three-way merge:
597 # Base (Topic_1): somefile
599 # Upstream (Result from 1): same files as base, but adds newdir/newfile
600 # Topic_2: same files as base, but adds olddir/unrelated-file
602 # The second merge is pretty trivial; upstream adds newdir/newfile, and
603 # topic_2 adds olddir/unrelated-file. We're just testing that we don't
604 # accidentally cache directory renames somehow and rename
605 # olddir/unrelated-file to newdir/unrelated-file.
607 # This testcase should only need one call to diffcore_rename_extended().
608 test_expect_success
'caching renames only on upstream side, part 1' '
609 test_setup_topic_rename cache-renames-only-upstream-add-file &&
611 cd cache-renames-only-upstream-add-file &&
613 git switch upstream &&
616 git add olddir/newfile &&
617 git commit -m "Add newfile" &&
623 git switch upstream &&
625 git config merge.directoryRenames true &&
627 GIT_TRACE2_PERF="$(pwd)/trace.output" &&
628 export GIT_TRACE2_PERF &&
630 git replay --onto HEAD upstream~1..topic >out &&
631 git update-ref --stdin <out &&
632 git checkout topic &&
634 grep region_enter.*diffcore_rename trace.output >calls &&
635 test_line_count = 1 calls &&
637 git ls-files >tracked &&
638 test_line_count = 5 tracked &&
639 test_path_is_missing newdir/unrelated-file &&
640 test_path_is_file olddir/unrelated-file &&
641 test_path_is_file newdir/newfile &&
642 test_path_is_file newdir/b &&
643 test_path_is_file newdir/a &&
644 test_path_is_file somefile
649 # The following testcase is *very* similar to the last one, but instead of
650 # adding a new olddir/newfile, it renames somefile -> olddir/newfile:
654 # Upstream: somefile_1 -> olddir/newfile
655 # Topic_1: rename olddir/ -> newdir/
657 # Topic_2: olddir/unrelated-file
660 # Much like the previous test, this case is actually trivial and we are just
661 # making sure there isn't some spurious directory rename caching going on
662 # for the wrong side of history.
665 # This testcase should only need two calls to diffcore_rename_extended(),
666 # both for the first merge, one for each side of history.
668 test_expect_success
'caching renames only on upstream side, part 2' '
669 test_setup_topic_rename cache-renames-only-upstream-rename-file &&
671 cd cache-renames-only-upstream-rename-file &&
673 git switch upstream &&
675 git mv somefile olddir/newfile &&
676 git commit -m "Add newfile" &&
682 git switch upstream &&
684 git config merge.directoryRenames true &&
686 GIT_TRACE2_PERF="$(pwd)/trace.output" &&
687 export GIT_TRACE2_PERF &&
689 git replay --onto HEAD upstream~1..topic >out &&
690 git update-ref --stdin <out &&
691 git checkout topic &&
693 grep region_enter.*diffcore_rename trace.output >calls &&
694 test_line_count = 2 calls &&
696 git ls-files >tracked &&
697 test_line_count = 4 tracked &&
698 test_path_is_missing newdir/unrelated-file &&
699 test_path_is_file olddir/unrelated-file &&
700 test_path_is_file newdir/newfile &&
701 test_path_is_file newdir/b &&
702 test_path_is_file newdir/a
707 # The following testcase just creates two simple renames (slightly modified
708 # on both sides but without conflicting changes), and a directory full of
709 # files that are otherwise uninteresting. The setup is as follows:
711 # base: unrelated/<BUNCH OF FILES>
714 # upstream: modify: numbers
716 # topic: add: unrelated/foo
719 # rename: numbers -> sequence
720 # rename: values -> progression
722 # This is a trivial rename case, but we're curious what happens with a very
723 # low renameLimit interacting with the restart optimization trying to notice
724 # that unrelated/ looks like a trivial merge candidate.
726 test_expect_success
'avoid assuming we detected renames' '
727 git init redo-weirdness &&
732 for i in $(test_seq 1 10)
734 >unrelated/$i || exit 1
736 test_seq 2 10 >numbers &&
737 test_seq 12 20 >values &&
738 git add numbers values unrelated/ &&
739 git commit -m orig &&
741 git branch upstream &&
744 git switch upstream &&
745 test_seq 1 10 >numbers &&
746 test_seq 11 20 >values &&
748 git commit -m "Some tweaks" &&
753 test_seq 2 12 >numbers &&
754 test_seq 12 22 >values &&
755 git add numbers values unrelated/ &&
756 git mv numbers sequence &&
757 git mv values progression &&
764 git switch --detach topic^0 &&
766 test_must_fail git -c merge.renameLimit=1 rebase upstream &&
768 git ls-files -u >actual &&
769 test_line_count = 2 actual