Git 2.45
[git/gitster.git] / t / t6429-merge-sequence-rename-caching.sh
blob0f39ed0d08a34230c7c42f307992c20cc8b06d02
1 #!/bin/sh
3 test_description="remember regular & dir renames in sequence of merges"
5 . ./test-lib.sh
8 # NOTE 1: this testfile tends to not only rename files, but modify on both
9 # sides; without modifying on both sides, optimizations can kick in
10 # which make rename detection irrelevant or trivial. We want to make
11 # sure that we are triggering rename caching rather than rename
12 # bypassing.
14 # NOTE 2: this testfile uses 'test-tool fast-rebase' instead of either
15 # cherry-pick or rebase. sequencer.c is only superficially
16 # integrated with merge-ort; it calls merge_switch_to_result()
17 # after EACH merge, which updates the index and working copy AND
18 # throws away the cached results (because merge_switch_to_result()
19 # is only supposed to be called at the end of the sequence).
20 # Integrating them more deeply is a big task, so for now the tests
21 # use 'test-tool fast-rebase'.
26 # In the following simple testcase:
27 # Base: numbers_1, values_1
28 # Upstream: numbers_2, values_2
29 # Topic_1: sequence_3
30 # Topic_2: scruples_3
31 # or, in english, rename numbers -> sequence in the first commit, and rename
32 # values -> scruples in the second commit.
34 # This shouldn't be a challenge, it's just verifying that cached renames isn't
35 # preventing us from finding new renames.
37 test_expect_success 'caching renames does not preclude finding new ones' '
38 git init caching-renames-and-new-renames &&
40 cd caching-renames-and-new-renames &&
42 test_seq 2 10 >numbers &&
43 test_seq 2 10 >values &&
44 git add numbers values &&
45 git commit -m orig &&
47 git branch upstream &&
48 git branch topic &&
50 git switch upstream &&
51 test_seq 1 10 >numbers &&
52 test_seq 1 10 >values &&
53 git add numbers values &&
54 git commit -m "Tweaked both files" &&
56 git switch topic &&
58 test_seq 2 12 >numbers &&
59 git add numbers &&
60 git mv numbers sequence &&
61 git commit -m A &&
63 test_seq 2 12 >values &&
64 git add values &&
65 git mv values scruples &&
66 git commit -m B &&
69 # Actual testing
72 git switch upstream &&
74 git replay --onto HEAD upstream~1..topic >out &&
75 git update-ref --stdin <out &&
76 git checkout topic &&
78 git ls-files >tracked-files &&
79 test_line_count = 2 tracked-files &&
80 test_seq 1 12 >expect &&
81 test_cmp expect sequence &&
82 test_cmp expect scruples
87 # In the following testcase:
88 # Base: numbers_1
89 # Upstream: rename numbers_1 -> sequence_2
90 # Topic_1: numbers_3
91 # Topic_2: numbers_1
92 # or, in english, the first commit on the topic branch modifies numbers by
93 # shrinking it (dramatically) and the second commit on topic reverts its
94 # parent.
96 # Can git apply both patches?
98 # Traditional cherry-pick/rebase will fail to apply the second commit, the
99 # one that reverted its parent, because despite detecting the rename from
100 # 'numbers' to 'sequence' for the first commit, it fails to detect that
101 # rename when picking the second commit. That's "reasonable" given the
102 # dramatic change in size of the file, but remembering the rename and
103 # reusing it is reasonable too.
105 # We do test here that we expect rename detection to only be run once total
106 # (the topic side of history doesn't need renames, and with caching we
107 # should be able to only run rename detection on the upstream side one
108 # time.)
109 test_expect_success 'cherry-pick both a commit and its immediate revert' '
110 git init pick-commit-and-its-immediate-revert &&
112 cd pick-commit-and-its-immediate-revert &&
114 test_seq 11 30 >numbers &&
115 git add numbers &&
116 git commit -m orig &&
118 git branch upstream &&
119 git branch topic &&
121 git switch upstream &&
122 test_seq 1 30 >numbers &&
123 git add numbers &&
124 git mv numbers sequence &&
125 git commit -m "Renamed (and modified) numbers -> sequence" &&
127 git switch topic &&
129 test_seq 11 13 >numbers &&
130 git add numbers &&
131 git commit -m A &&
133 git revert HEAD &&
136 # Actual testing
139 git switch upstream &&
141 GIT_TRACE2_PERF="$(pwd)/trace.output" &&
142 export GIT_TRACE2_PERF &&
144 git replay --onto HEAD upstream~1..topic >out &&
145 git update-ref --stdin <out &&
146 git checkout topic &&
148 grep region_enter.*diffcore_rename trace.output >calls &&
149 test_line_count = 1 calls
154 # In the following testcase:
155 # Base: sequence_1
156 # Upstream: rename sequence_1 -> values_2
157 # Topic_1: rename sequence_1 -> values_3
158 # Topic_2: add unrelated sequence_4
159 # or, in english, both sides rename sequence -> values, and then the second
160 # commit on the topic branch adds an unrelated file called sequence.
162 # This testcase presents no problems for git traditionally, but having both
163 # sides do the same rename in effect "uses it up" and if it remains cached,
164 # could cause a spurious rename/add conflict.
166 test_expect_success 'rename same file identically, then reintroduce it' '
167 git init rename-rename-1to1-then-add-old-filename &&
169 cd rename-rename-1to1-then-add-old-filename &&
171 test_seq 3 8 >sequence &&
172 git add sequence &&
173 git commit -m orig &&
175 git branch upstream &&
176 git branch topic &&
178 git switch upstream &&
179 test_seq 1 8 >sequence &&
180 git add sequence &&
181 git mv sequence values &&
182 git commit -m "Renamed (and modified) sequence -> values" &&
184 git switch topic &&
186 test_seq 3 10 >sequence &&
187 git add sequence &&
188 git mv sequence values &&
189 git commit -m A &&
191 test_write_lines A B C D E F G H I J >sequence &&
192 git add sequence &&
193 git commit -m B &&
196 # Actual testing
199 git switch upstream &&
201 GIT_TRACE2_PERF="$(pwd)/trace.output" &&
202 export GIT_TRACE2_PERF &&
204 git replay --onto HEAD upstream~1..topic >out &&
205 git update-ref --stdin <out &&
206 git checkout topic &&
208 git ls-files >tracked &&
209 test_line_count = 2 tracked &&
210 test_path_is_file values &&
211 test_path_is_file sequence &&
213 grep region_enter.*diffcore_rename trace.output >calls &&
214 test_line_count = 2 calls
219 # In the following testcase:
220 # Base: olddir/{valuesZ_1, valuesY_1, valuesX_1}
221 # Upstream: rename olddir/valuesZ_1 -> dirA/valuesZ_2
222 # rename olddir/valuesY_1 -> dirA/valuesY_2
223 # rename olddir/valuesX_1 -> dirB/valuesX_2
224 # Topic_1: rename olddir/valuesZ_1 -> dirA/valuesZ_3
225 # rename olddir/valuesY_1 -> dirA/valuesY_3
226 # Topic_2: add olddir/newfile
227 # Expected Pick1: dirA/{valuesZ, valuesY}, dirB/valuesX
228 # Expected Pick2: dirA/{valuesZ, valuesY}, dirB/{valuesX, newfile}
230 # This testcase presents no problems for git traditionally, but having both
231 # sides do the same renames in effect "use it up" but if the renames remain
232 # cached, the directory rename could put newfile in the wrong directory.
234 test_expect_success 'rename same file identically, then add file to old dir' '
235 git init rename-rename-1to1-then-add-file-to-old-dir &&
237 cd rename-rename-1to1-then-add-file-to-old-dir &&
239 mkdir olddir/ &&
240 test_seq 3 8 >olddir/valuesZ &&
241 test_seq 3 8 >olddir/valuesY &&
242 test_seq 3 8 >olddir/valuesX &&
243 git add olddir &&
244 git commit -m orig &&
246 git branch upstream &&
247 git branch topic &&
249 git switch upstream &&
250 test_seq 1 8 >olddir/valuesZ &&
251 test_seq 1 8 >olddir/valuesY &&
252 test_seq 1 8 >olddir/valuesX &&
253 git add olddir &&
254 mkdir dirA &&
255 git mv olddir/valuesZ olddir/valuesY dirA &&
256 git mv olddir/ dirB/ &&
257 git commit -m "Renamed (and modified) values*" &&
259 git switch topic &&
261 test_seq 3 10 >olddir/valuesZ &&
262 test_seq 3 10 >olddir/valuesY &&
263 git add olddir &&
264 mkdir dirA &&
265 git mv olddir/valuesZ olddir/valuesY dirA &&
266 git commit -m A &&
268 >olddir/newfile &&
269 git add olddir/newfile &&
270 git commit -m B &&
273 # Actual testing
276 git switch upstream &&
277 git config merge.directoryRenames true &&
279 GIT_TRACE2_PERF="$(pwd)/trace.output" &&
280 export GIT_TRACE2_PERF &&
282 git replay --onto HEAD upstream~1..topic >out &&
283 git update-ref --stdin <out &&
284 git checkout topic &&
286 git ls-files >tracked &&
287 test_line_count = 4 tracked &&
288 test_path_is_file dirA/valuesZ &&
289 test_path_is_file dirA/valuesY &&
290 test_path_is_file dirB/valuesX &&
291 test_path_is_file dirB/newfile &&
293 grep region_enter.*diffcore_rename trace.output >calls &&
294 test_line_count = 3 calls
299 # In the following testcase, upstream renames a directory, and the topic branch
300 # first adds a file to the directory, then later renames the directory
301 # differently:
302 # Base: olddir/a
303 # olddir/b
304 # Upstream: rename olddir/ -> newdir/
305 # Topic_1: add olddir/newfile
306 # Topic_2: rename olddir/ -> otherdir/
308 # Here we are just concerned that cached renames might prevent us from seeing
309 # the rename conflict, and we want to ensure that we do get a conflict.
311 # While at it, though, we do test that we only try to detect renames 2
312 # times and not three. (The first merge needs to detect renames on the
313 # upstream side. Traditionally, the second merge would need to detect
314 # renames on both sides of history, but our caching of upstream renames
315 # should avoid the need to re-detect upstream renames.)
317 test_expect_success 'cached dir rename does not prevent noticing later conflict' '
318 git init dir-rename-cache-not-occluding-later-conflict &&
320 cd dir-rename-cache-not-occluding-later-conflict &&
322 mkdir olddir &&
323 test_seq 3 10 >olddir/a &&
324 test_seq 3 10 >olddir/b &&
325 git add olddir &&
326 git commit -m orig &&
328 git branch upstream &&
329 git branch topic &&
331 git switch upstream &&
332 test_seq 3 10 >olddir/a &&
333 test_seq 3 10 >olddir/b &&
334 git add olddir &&
335 git mv olddir newdir &&
336 git commit -m "Dir renamed" &&
338 git switch topic &&
340 >olddir/newfile &&
341 git add olddir/newfile &&
342 git commit -m A &&
344 test_seq 1 8 >olddir/a &&
345 test_seq 1 8 >olddir/b &&
346 git add olddir &&
347 git mv olddir otherdir &&
348 git commit -m B &&
351 # Actual testing
354 git switch upstream &&
355 git config merge.directoryRenames true &&
357 GIT_TRACE2_PERF="$(pwd)/trace.output" &&
358 export GIT_TRACE2_PERF &&
360 test_must_fail git replay --onto HEAD upstream~1..topic >output &&
362 grep region_enter.*diffcore_rename trace.output >calls &&
363 test_line_count = 2 calls
367 # Helper for the next two tests
368 test_setup_upstream_rename () {
369 git init $1 &&
371 cd $1 &&
373 test_seq 3 8 >somefile &&
374 test_seq 3 8 >relevant-rename &&
375 git add somefile relevant-rename &&
376 mkdir olddir &&
377 test_write_lines a b c d e f g >olddir/a &&
378 test_write_lines z y x w v u t >olddir/b &&
379 git add olddir &&
380 git commit -m orig &&
382 git branch upstream &&
383 git branch topic &&
385 git switch upstream &&
386 test_seq 1 8 >somefile &&
387 test_seq 1 8 >relevant-rename &&
388 git add somefile relevant-rename &&
389 git mv relevant-rename renamed &&
390 echo h >>olddir/a &&
391 echo s >>olddir/b &&
392 git add olddir &&
393 git mv olddir newdir &&
394 git commit -m "Dir renamed"
399 # In the following testcase, upstream renames a file in the toplevel directory
400 # as well as its only directory:
401 # Base: relevant-rename_1
402 # somefile
403 # olddir/a
404 # olddir/b
405 # Upstream: rename relevant-rename_1 -> renamed_2
406 # rename olddir/ -> newdir/
407 # Topic_1: relevant-rename_3
408 # Topic_2: olddir/newfile_1
409 # Topic_3: olddir/newfile_2
411 # In this testcase, since the first commit being picked only modifies a
412 # file in the toplevel directory, the directory rename is irrelevant for
413 # that first merge. However, we need to notice the directory rename for
414 # the merge that picks the second commit, and we don't want the third
415 # commit to mess up its location either. We want to make sure that
416 # olddir/newfile doesn't exist in the result and that newdir/newfile does.
418 # We also test that we only do rename detection twice. We never need
419 # rename detection on the topic side of history, but we do need it twice on
420 # the upstream side of history. For the first topic commit, we only need
421 # the
422 # relevant-rename -> renamed
423 # rename, because olddir is unmodified by Topic_1. For Topic_2, however,
424 # the new file being added to olddir means files that were previously
425 # irrelevant for rename detection are now relevant, forcing us to repeat
426 # rename detection for the paths we don't already have cached. Topic_3 also
427 # tweaks olddir/newfile, but the renames in olddir/ will have been cached
428 # from the second rename detection run.
430 test_expect_success 'dir rename unneeded, then add new file to old dir' '
431 test_setup_upstream_rename dir-rename-unneeded-until-new-file &&
433 cd dir-rename-unneeded-until-new-file &&
435 git switch topic &&
437 test_seq 3 10 >relevant-rename &&
438 git add relevant-rename &&
439 git commit -m A &&
441 echo foo >olddir/newfile &&
442 git add olddir/newfile &&
443 git commit -m B &&
445 echo bar >>olddir/newfile &&
446 git add olddir/newfile &&
447 git commit -m C &&
450 # Actual testing
453 git switch upstream &&
454 git config merge.directoryRenames true &&
456 GIT_TRACE2_PERF="$(pwd)/trace.output" &&
457 export GIT_TRACE2_PERF &&
459 git replay --onto HEAD upstream~1..topic >out &&
460 git update-ref --stdin <out &&
461 git checkout topic &&
463 grep region_enter.*diffcore_rename trace.output >calls &&
464 test_line_count = 2 calls &&
466 git ls-files >tracked &&
467 test_line_count = 5 tracked &&
468 test_path_is_missing olddir/newfile &&
469 test_path_is_file newdir/newfile
474 # The following testcase is *very* similar to the last one, but instead of
475 # adding a new olddir/newfile, it renames somefile -> olddir/newfile:
476 # Base: relevant-rename_1
477 # somefile_1
478 # olddir/a
479 # olddir/b
480 # Upstream: rename relevant-rename_1 -> renamed_2
481 # rename olddir/ -> newdir/
482 # Topic_1: relevant-rename_3
483 # Topic_2: rename somefile -> olddir/newfile_2
484 # Topic_3: modify olddir/newfile_3
486 # In this testcase, since the first commit being picked only modifies a
487 # file in the toplevel directory, the directory rename is irrelevant for
488 # that first merge. However, we need to notice the directory rename for
489 # the merge that picks the second commit, and we don't want the third
490 # commit to mess up its location either. We want to make sure that
491 # neither somefile or olddir/newfile exists in the result and that
492 # newdir/newfile does.
494 # This testcase needs one more call to rename detection than the last
495 # testcase, because of the somefile -> olddir/newfile rename in Topic_2.
496 test_expect_success 'dir rename unneeded, then rename existing file into old dir' '
497 test_setup_upstream_rename dir-rename-unneeded-until-file-moved-inside &&
499 cd dir-rename-unneeded-until-file-moved-inside &&
501 git switch topic &&
503 test_seq 3 10 >relevant-rename &&
504 git add relevant-rename &&
505 git commit -m A &&
507 test_seq 1 10 >somefile &&
508 git add somefile &&
509 git mv somefile olddir/newfile &&
510 git commit -m B &&
512 test_seq 1 12 >olddir/newfile &&
513 git add olddir/newfile &&
514 git commit -m C &&
517 # Actual testing
520 git switch upstream &&
521 git config merge.directoryRenames true &&
523 GIT_TRACE2_PERF="$(pwd)/trace.output" &&
524 export GIT_TRACE2_PERF &&
526 git replay --onto HEAD upstream~1..topic >out &&
527 git update-ref --stdin <out &&
528 git checkout topic &&
530 grep region_enter.*diffcore_rename trace.output >calls &&
531 test_line_count = 3 calls &&
533 test_path_is_missing somefile &&
534 test_path_is_missing olddir/newfile &&
535 test_path_is_file newdir/newfile &&
536 git ls-files >tracked &&
537 test_line_count = 4 tracked
541 # Helper for the next two tests
542 test_setup_topic_rename () {
543 git init $1 &&
545 cd $1 &&
547 test_seq 3 8 >somefile &&
548 mkdir olddir &&
549 test_seq 3 8 >olddir/a &&
550 echo b >olddir/b &&
551 git add olddir somefile &&
552 git commit -m orig &&
554 git branch upstream &&
555 git branch topic &&
557 git switch topic &&
558 test_seq 1 8 >somefile &&
559 test_seq 1 8 >olddir/a &&
560 git add somefile olddir/a &&
561 git mv olddir newdir &&
562 git commit -m "Dir renamed" &&
564 test_seq 1 10 >somefile &&
565 git add somefile &&
566 mkdir olddir &&
567 >olddir/unrelated-file &&
568 git add olddir &&
569 git commit -m "Unrelated file in recreated old dir"
574 # In the following testcase, the first commit on the topic branch renames
575 # a directory, while the second recreates the old directory and places a
576 # file into it:
577 # Base: somefile
578 # olddir/a
579 # olddir/b
580 # Upstream: olddir/newfile
581 # Topic_1: somefile_2
582 # rename olddir/ -> newdir/
583 # Topic_2: olddir/unrelated-file
585 # Note that the first pick should merge:
586 # Base: somefile
587 # olddir/{a,b}
588 # Upstream: olddir/newfile
589 # Topic_1: rename olddir/ -> newdir/
590 # For which the expected result (assuming merge.directoryRenames=true) is
591 # clearly:
592 # Result: somefile
593 # newdir/{a, b, newfile}
595 # While the second pick does the following three-way merge:
596 # Base (Topic_1): somefile
597 # newdir/{a,b}
598 # Upstream (Result from 1): same files as base, but adds newdir/newfile
599 # Topic_2: same files as base, but adds olddir/unrelated-file
601 # The second merge is pretty trivial; upstream adds newdir/newfile, and
602 # topic_2 adds olddir/unrelated-file. We're just testing that we don't
603 # accidentally cache directory renames somehow and rename
604 # olddir/unrelated-file to newdir/unrelated-file.
606 # This testcase should only need one call to diffcore_rename_extended().
607 test_expect_success 'caching renames only on upstream side, part 1' '
608 test_setup_topic_rename cache-renames-only-upstream-add-file &&
610 cd cache-renames-only-upstream-add-file &&
612 git switch upstream &&
614 >olddir/newfile &&
615 git add olddir/newfile &&
616 git commit -m "Add newfile" &&
619 # Actual testing
622 git switch upstream &&
624 git config merge.directoryRenames true &&
626 GIT_TRACE2_PERF="$(pwd)/trace.output" &&
627 export GIT_TRACE2_PERF &&
629 git replay --onto HEAD upstream~1..topic >out &&
630 git update-ref --stdin <out &&
631 git checkout topic &&
633 grep region_enter.*diffcore_rename trace.output >calls &&
634 test_line_count = 1 calls &&
636 git ls-files >tracked &&
637 test_line_count = 5 tracked &&
638 test_path_is_missing newdir/unrelated-file &&
639 test_path_is_file olddir/unrelated-file &&
640 test_path_is_file newdir/newfile &&
641 test_path_is_file newdir/b &&
642 test_path_is_file newdir/a &&
643 test_path_is_file somefile
648 # The following testcase is *very* similar to the last one, but instead of
649 # adding a new olddir/newfile, it renames somefile -> olddir/newfile:
650 # Base: somefile
651 # olddir/a
652 # olddir/b
653 # Upstream: somefile_1 -> olddir/newfile
654 # Topic_1: rename olddir/ -> newdir/
655 # somefile_2
656 # Topic_2: olddir/unrelated-file
657 # somefile_3
659 # Much like the previous test, this case is actually trivial and we are just
660 # making sure there isn't some spurious directory rename caching going on
661 # for the wrong side of history.
664 # This testcase should only need two calls to diffcore_rename_extended(),
665 # both for the first merge, one for each side of history.
667 test_expect_success 'caching renames only on upstream side, part 2' '
668 test_setup_topic_rename cache-renames-only-upstream-rename-file &&
670 cd cache-renames-only-upstream-rename-file &&
672 git switch upstream &&
674 git mv somefile olddir/newfile &&
675 git commit -m "Add newfile" &&
678 # Actual testing
681 git switch upstream &&
683 git config merge.directoryRenames true &&
685 GIT_TRACE2_PERF="$(pwd)/trace.output" &&
686 export GIT_TRACE2_PERF &&
688 git replay --onto HEAD upstream~1..topic >out &&
689 git update-ref --stdin <out &&
690 git checkout topic &&
692 grep region_enter.*diffcore_rename trace.output >calls &&
693 test_line_count = 2 calls &&
695 git ls-files >tracked &&
696 test_line_count = 4 tracked &&
697 test_path_is_missing newdir/unrelated-file &&
698 test_path_is_file olddir/unrelated-file &&
699 test_path_is_file newdir/newfile &&
700 test_path_is_file newdir/b &&
701 test_path_is_file newdir/a
706 # The following testcase just creates two simple renames (slightly modified
707 # on both sides but without conflicting changes), and a directory full of
708 # files that are otherwise uninteresting. The setup is as follows:
710 # base: unrelated/<BUNCH OF FILES>
711 # numbers
712 # values
713 # upstream: modify: numbers
714 # modify: values
715 # topic: add: unrelated/foo
716 # modify: numbers
717 # modify: values
718 # rename: numbers -> sequence
719 # rename: values -> progression
721 # This is a trivial rename case, but we're curious what happens with a very
722 # low renameLimit interacting with the restart optimization trying to notice
723 # that unrelated/ looks like a trivial merge candidate.
725 test_expect_success 'avoid assuming we detected renames' '
726 git init redo-weirdness &&
728 cd redo-weirdness &&
730 mkdir unrelated &&
731 for i in $(test_seq 1 10)
733 >unrelated/$i || exit 1
734 done &&
735 test_seq 2 10 >numbers &&
736 test_seq 12 20 >values &&
737 git add numbers values unrelated/ &&
738 git commit -m orig &&
740 git branch upstream &&
741 git branch topic &&
743 git switch upstream &&
744 test_seq 1 10 >numbers &&
745 test_seq 11 20 >values &&
746 git add numbers &&
747 git commit -m "Some tweaks" &&
749 git switch topic &&
751 >unrelated/foo &&
752 test_seq 2 12 >numbers &&
753 test_seq 12 22 >values &&
754 git add numbers values unrelated/ &&
755 git mv numbers sequence &&
756 git mv values progression &&
757 git commit -m A &&
760 # Actual testing
763 git switch --detach topic^0 &&
765 test_must_fail git -c merge.renameLimit=1 rebase upstream &&
767 git ls-files -u >actual &&
768 test_line_count = 2 actual
772 test_done