Merge branch 'ds/reprepare-alternates-when-repreparing-packfiles'
[alt-git.git] / t / t6429-merge-sequence-rename-caching.sh
blobd02fa16614e0622585a7732942ee9d5b515e6afa
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 test-tool fast-rebase --onto HEAD upstream~1 topic &&
75 #git cherry-pick upstream~1..topic
77 git ls-files >tracked-files &&
78 test_line_count = 2 tracked-files &&
79 test_seq 1 12 >expect &&
80 test_cmp expect sequence &&
81 test_cmp expect scruples
86 # In the following testcase:
87 # Base: numbers_1
88 # Upstream: rename numbers_1 -> sequence_2
89 # Topic_1: numbers_3
90 # Topic_2: numbers_1
91 # or, in english, the first commit on the topic branch modifies numbers by
92 # shrinking it (dramatically) and the second commit on topic reverts its
93 # parent.
95 # Can git apply both patches?
97 # Traditional cherry-pick/rebase will fail to apply the second commit, the
98 # one that reverted its parent, because despite detecting the rename from
99 # 'numbers' to 'sequence' for the first commit, it fails to detect that
100 # rename when picking the second commit. That's "reasonable" given the
101 # dramatic change in size of the file, but remembering the rename and
102 # reusing it is reasonable too.
104 # We do test here that we expect rename detection to only be run once total
105 # (the topic side of history doesn't need renames, and with caching we
106 # should be able to only run rename detection on the upstream side one
107 # time.)
108 test_expect_success 'cherry-pick both a commit and its immediate revert' '
109 git init pick-commit-and-its-immediate-revert &&
111 cd pick-commit-and-its-immediate-revert &&
113 test_seq 11 30 >numbers &&
114 git add numbers &&
115 git commit -m orig &&
117 git branch upstream &&
118 git branch topic &&
120 git switch upstream &&
121 test_seq 1 30 >numbers &&
122 git add numbers &&
123 git mv numbers sequence &&
124 git commit -m "Renamed (and modified) numbers -> sequence" &&
126 git switch topic &&
128 test_seq 11 13 >numbers &&
129 git add numbers &&
130 git commit -m A &&
132 git revert HEAD &&
135 # Actual testing
138 git switch upstream &&
140 GIT_TRACE2_PERF="$(pwd)/trace.output" &&
141 export GIT_TRACE2_PERF &&
143 test-tool fast-rebase --onto HEAD upstream~1 topic &&
144 #git cherry-pick upstream~1..topic &&
146 grep region_enter.*diffcore_rename trace.output >calls &&
147 test_line_count = 1 calls
152 # In the following testcase:
153 # Base: sequence_1
154 # Upstream: rename sequence_1 -> values_2
155 # Topic_1: rename sequence_1 -> values_3
156 # Topic_2: add unrelated sequence_4
157 # or, in english, both sides rename sequence -> values, and then the second
158 # commit on the topic branch adds an unrelated file called sequence.
160 # This testcase presents no problems for git traditionally, but having both
161 # sides do the same rename in effect "uses it up" and if it remains cached,
162 # could cause a spurious rename/add conflict.
164 test_expect_success 'rename same file identically, then reintroduce it' '
165 git init rename-rename-1to1-then-add-old-filename &&
167 cd rename-rename-1to1-then-add-old-filename &&
169 test_seq 3 8 >sequence &&
170 git add sequence &&
171 git commit -m orig &&
173 git branch upstream &&
174 git branch topic &&
176 git switch upstream &&
177 test_seq 1 8 >sequence &&
178 git add sequence &&
179 git mv sequence values &&
180 git commit -m "Renamed (and modified) sequence -> values" &&
182 git switch topic &&
184 test_seq 3 10 >sequence &&
185 git add sequence &&
186 git mv sequence values &&
187 git commit -m A &&
189 test_write_lines A B C D E F G H I J >sequence &&
190 git add sequence &&
191 git commit -m B &&
194 # Actual testing
197 git switch upstream &&
199 GIT_TRACE2_PERF="$(pwd)/trace.output" &&
200 export GIT_TRACE2_PERF &&
202 test-tool fast-rebase --onto HEAD upstream~1 topic &&
203 #git cherry-pick upstream~1..topic &&
205 git ls-files >tracked &&
206 test_line_count = 2 tracked &&
207 test_path_is_file values &&
208 test_path_is_file sequence &&
210 grep region_enter.*diffcore_rename trace.output >calls &&
211 test_line_count = 2 calls
216 # In the following testcase:
217 # Base: olddir/{valuesZ_1, valuesY_1, valuesX_1}
218 # Upstream: rename olddir/valuesZ_1 -> dirA/valuesZ_2
219 # rename olddir/valuesY_1 -> dirA/valuesY_2
220 # rename olddir/valuesX_1 -> dirB/valuesX_2
221 # Topic_1: rename olddir/valuesZ_1 -> dirA/valuesZ_3
222 # rename olddir/valuesY_1 -> dirA/valuesY_3
223 # Topic_2: add olddir/newfile
224 # Expected Pick1: dirA/{valuesZ, valuesY}, dirB/valuesX
225 # Expected Pick2: dirA/{valuesZ, valuesY}, dirB/{valuesX, newfile}
227 # This testcase presents no problems for git traditionally, but having both
228 # sides do the same renames in effect "use it up" but if the renames remain
229 # cached, the directory rename could put newfile in the wrong directory.
231 test_expect_success 'rename same file identically, then add file to old dir' '
232 git init rename-rename-1to1-then-add-file-to-old-dir &&
234 cd rename-rename-1to1-then-add-file-to-old-dir &&
236 mkdir olddir/ &&
237 test_seq 3 8 >olddir/valuesZ &&
238 test_seq 3 8 >olddir/valuesY &&
239 test_seq 3 8 >olddir/valuesX &&
240 git add olddir &&
241 git commit -m orig &&
243 git branch upstream &&
244 git branch topic &&
246 git switch upstream &&
247 test_seq 1 8 >olddir/valuesZ &&
248 test_seq 1 8 >olddir/valuesY &&
249 test_seq 1 8 >olddir/valuesX &&
250 git add olddir &&
251 mkdir dirA &&
252 git mv olddir/valuesZ olddir/valuesY dirA &&
253 git mv olddir/ dirB/ &&
254 git commit -m "Renamed (and modified) values*" &&
256 git switch topic &&
258 test_seq 3 10 >olddir/valuesZ &&
259 test_seq 3 10 >olddir/valuesY &&
260 git add olddir &&
261 mkdir dirA &&
262 git mv olddir/valuesZ olddir/valuesY dirA &&
263 git commit -m A &&
265 >olddir/newfile &&
266 git add olddir/newfile &&
267 git commit -m B &&
270 # Actual testing
273 git switch upstream &&
274 git config merge.directoryRenames true &&
276 GIT_TRACE2_PERF="$(pwd)/trace.output" &&
277 export GIT_TRACE2_PERF &&
279 test-tool fast-rebase --onto HEAD upstream~1 topic &&
280 #git cherry-pick upstream~1..topic &&
282 git ls-files >tracked &&
283 test_line_count = 4 tracked &&
284 test_path_is_file dirA/valuesZ &&
285 test_path_is_file dirA/valuesY &&
286 test_path_is_file dirB/valuesX &&
287 test_path_is_file dirB/newfile &&
289 grep region_enter.*diffcore_rename trace.output >calls &&
290 test_line_count = 3 calls
295 # In the following testcase, upstream renames a directory, and the topic branch
296 # first adds a file to the directory, then later renames the directory
297 # differently:
298 # Base: olddir/a
299 # olddir/b
300 # Upstream: rename olddir/ -> newdir/
301 # Topic_1: add olddir/newfile
302 # Topic_2: rename olddir/ -> otherdir/
304 # Here we are just concerned that cached renames might prevent us from seeing
305 # the rename conflict, and we want to ensure that we do get a conflict.
307 # While at it, though, we do test that we only try to detect renames 2
308 # times and not three. (The first merge needs to detect renames on the
309 # upstream side. Traditionally, the second merge would need to detect
310 # renames on both sides of history, but our caching of upstream renames
311 # should avoid the need to re-detect upstream renames.)
313 test_expect_success 'cached dir rename does not prevent noticing later conflict' '
314 git init dir-rename-cache-not-occluding-later-conflict &&
316 cd dir-rename-cache-not-occluding-later-conflict &&
318 mkdir olddir &&
319 test_seq 3 10 >olddir/a &&
320 test_seq 3 10 >olddir/b &&
321 git add olddir &&
322 git commit -m orig &&
324 git branch upstream &&
325 git branch topic &&
327 git switch upstream &&
328 test_seq 3 10 >olddir/a &&
329 test_seq 3 10 >olddir/b &&
330 git add olddir &&
331 git mv olddir newdir &&
332 git commit -m "Dir renamed" &&
334 git switch topic &&
336 >olddir/newfile &&
337 git add olddir/newfile &&
338 git commit -m A &&
340 test_seq 1 8 >olddir/a &&
341 test_seq 1 8 >olddir/b &&
342 git add olddir &&
343 git mv olddir otherdir &&
344 git commit -m B &&
347 # Actual testing
350 git switch upstream &&
351 git config merge.directoryRenames true &&
353 GIT_TRACE2_PERF="$(pwd)/trace.output" &&
354 export GIT_TRACE2_PERF &&
356 test_must_fail test-tool fast-rebase --onto HEAD upstream~1 topic >output &&
357 #git cherry-pick upstream..topic &&
359 grep CONFLICT..rename/rename output &&
361 grep region_enter.*diffcore_rename trace.output >calls &&
362 test_line_count = 2 calls
366 # Helper for the next two tests
367 test_setup_upstream_rename () {
368 git init $1 &&
370 cd $1 &&
372 test_seq 3 8 >somefile &&
373 test_seq 3 8 >relevant-rename &&
374 git add somefile relevant-rename &&
375 mkdir olddir &&
376 test_write_lines a b c d e f g >olddir/a &&
377 test_write_lines z y x w v u t >olddir/b &&
378 git add olddir &&
379 git commit -m orig &&
381 git branch upstream &&
382 git branch topic &&
384 git switch upstream &&
385 test_seq 1 8 >somefile &&
386 test_seq 1 8 >relevant-rename &&
387 git add somefile relevant-rename &&
388 git mv relevant-rename renamed &&
389 echo h >>olddir/a &&
390 echo s >>olddir/b &&
391 git add olddir &&
392 git mv olddir newdir &&
393 git commit -m "Dir renamed"
398 # In the following testcase, upstream renames a file in the toplevel directory
399 # as well as its only directory:
400 # Base: relevant-rename_1
401 # somefile
402 # olddir/a
403 # olddir/b
404 # Upstream: rename relevant-rename_1 -> renamed_2
405 # rename olddir/ -> newdir/
406 # Topic_1: relevant-rename_3
407 # Topic_2: olddir/newfile_1
408 # Topic_3: olddir/newfile_2
410 # In this testcase, since the first commit being picked only modifies a
411 # file in the toplevel directory, the directory rename is irrelevant for
412 # that first merge. However, we need to notice the directory rename for
413 # the merge that picks the second commit, and we don't want the third
414 # commit to mess up its location either. We want to make sure that
415 # olddir/newfile doesn't exist in the result and that newdir/newfile does.
417 # We also test that we only do rename detection twice. We never need
418 # rename detection on the topic side of history, but we do need it twice on
419 # the upstream side of history. For the first topic commit, we only need
420 # the
421 # relevant-rename -> renamed
422 # rename, because olddir is unmodified by Topic_1. For Topic_2, however,
423 # the new file being added to olddir means files that were previously
424 # irrelevant for rename detection are now relevant, forcing us to repeat
425 # rename detection for the paths we don't already have cached. Topic_3 also
426 # tweaks olddir/newfile, but the renames in olddir/ will have been cached
427 # from the second rename detection run.
429 test_expect_success 'dir rename unneeded, then add new file to old dir' '
430 test_setup_upstream_rename dir-rename-unneeded-until-new-file &&
432 cd dir-rename-unneeded-until-new-file &&
434 git switch topic &&
436 test_seq 3 10 >relevant-rename &&
437 git add relevant-rename &&
438 git commit -m A &&
440 echo foo >olddir/newfile &&
441 git add olddir/newfile &&
442 git commit -m B &&
444 echo bar >>olddir/newfile &&
445 git add olddir/newfile &&
446 git commit -m C &&
449 # Actual testing
452 git switch upstream &&
453 git config merge.directoryRenames true &&
455 GIT_TRACE2_PERF="$(pwd)/trace.output" &&
456 export GIT_TRACE2_PERF &&
458 test-tool fast-rebase --onto HEAD upstream~1 topic &&
459 #git cherry-pick upstream..topic &&
461 grep region_enter.*diffcore_rename trace.output >calls &&
462 test_line_count = 2 calls &&
464 git ls-files >tracked &&
465 test_line_count = 5 tracked &&
466 test_path_is_missing olddir/newfile &&
467 test_path_is_file newdir/newfile
472 # The following testcase is *very* similar to the last one, but instead of
473 # adding a new olddir/newfile, it renames somefile -> olddir/newfile:
474 # Base: relevant-rename_1
475 # somefile_1
476 # olddir/a
477 # olddir/b
478 # Upstream: rename relevant-rename_1 -> renamed_2
479 # rename olddir/ -> newdir/
480 # Topic_1: relevant-rename_3
481 # Topic_2: rename somefile -> olddir/newfile_2
482 # Topic_3: modify olddir/newfile_3
484 # In this testcase, since the first commit being picked only modifies a
485 # file in the toplevel directory, the directory rename is irrelevant for
486 # that first merge. However, we need to notice the directory rename for
487 # the merge that picks the second commit, and we don't want the third
488 # commit to mess up its location either. We want to make sure that
489 # neither somefile or olddir/newfile exists in the result and that
490 # newdir/newfile does.
492 # This testcase needs one more call to rename detection than the last
493 # testcase, because of the somefile -> olddir/newfile rename in Topic_2.
494 test_expect_success 'dir rename unneeded, then rename existing file into old dir' '
495 test_setup_upstream_rename dir-rename-unneeded-until-file-moved-inside &&
497 cd dir-rename-unneeded-until-file-moved-inside &&
499 git switch topic &&
501 test_seq 3 10 >relevant-rename &&
502 git add relevant-rename &&
503 git commit -m A &&
505 test_seq 1 10 >somefile &&
506 git add somefile &&
507 git mv somefile olddir/newfile &&
508 git commit -m B &&
510 test_seq 1 12 >olddir/newfile &&
511 git add olddir/newfile &&
512 git commit -m C &&
515 # Actual testing
518 git switch upstream &&
519 git config merge.directoryRenames true &&
521 GIT_TRACE2_PERF="$(pwd)/trace.output" &&
522 export GIT_TRACE2_PERF &&
524 test-tool fast-rebase --onto HEAD upstream~1 topic &&
525 #git cherry-pick upstream..topic &&
527 grep region_enter.*diffcore_rename trace.output >calls &&
528 test_line_count = 3 calls &&
530 test_path_is_missing somefile &&
531 test_path_is_missing olddir/newfile &&
532 test_path_is_file newdir/newfile &&
533 git ls-files >tracked &&
534 test_line_count = 4 tracked
538 # Helper for the next two tests
539 test_setup_topic_rename () {
540 git init $1 &&
542 cd $1 &&
544 test_seq 3 8 >somefile &&
545 mkdir olddir &&
546 test_seq 3 8 >olddir/a &&
547 echo b >olddir/b &&
548 git add olddir somefile &&
549 git commit -m orig &&
551 git branch upstream &&
552 git branch topic &&
554 git switch topic &&
555 test_seq 1 8 >somefile &&
556 test_seq 1 8 >olddir/a &&
557 git add somefile olddir/a &&
558 git mv olddir newdir &&
559 git commit -m "Dir renamed" &&
561 test_seq 1 10 >somefile &&
562 git add somefile &&
563 mkdir olddir &&
564 >olddir/unrelated-file &&
565 git add olddir &&
566 git commit -m "Unrelated file in recreated old dir"
571 # In the following testcase, the first commit on the topic branch renames
572 # a directory, while the second recreates the old directory and places a
573 # file into it:
574 # Base: somefile
575 # olddir/a
576 # olddir/b
577 # Upstream: olddir/newfile
578 # Topic_1: somefile_2
579 # rename olddir/ -> newdir/
580 # Topic_2: olddir/unrelated-file
582 # Note that the first pick should merge:
583 # Base: somefile
584 # olddir/{a,b}
585 # Upstream: olddir/newfile
586 # Topic_1: rename olddir/ -> newdir/
587 # For which the expected result (assuming merge.directoryRenames=true) is
588 # clearly:
589 # Result: somefile
590 # newdir/{a, b, newfile}
592 # While the second pick does the following three-way merge:
593 # Base (Topic_1): somefile
594 # newdir/{a,b}
595 # Upstream (Result from 1): same files as base, but adds newdir/newfile
596 # Topic_2: same files as base, but adds olddir/unrelated-file
598 # The second merge is pretty trivial; upstream adds newdir/newfile, and
599 # topic_2 adds olddir/unrelated-file. We're just testing that we don't
600 # accidentally cache directory renames somehow and rename
601 # olddir/unrelated-file to newdir/unrelated-file.
603 # This testcase should only need one call to diffcore_rename_extended().
604 test_expect_success 'caching renames only on upstream side, part 1' '
605 test_setup_topic_rename cache-renames-only-upstream-add-file &&
607 cd cache-renames-only-upstream-add-file &&
609 git switch upstream &&
611 >olddir/newfile &&
612 git add olddir/newfile &&
613 git commit -m "Add newfile" &&
616 # Actual testing
619 git switch upstream &&
621 git config merge.directoryRenames true &&
623 GIT_TRACE2_PERF="$(pwd)/trace.output" &&
624 export GIT_TRACE2_PERF &&
626 test-tool fast-rebase --onto HEAD upstream~1 topic &&
627 #git cherry-pick upstream..topic &&
629 grep region_enter.*diffcore_rename trace.output >calls &&
630 test_line_count = 1 calls &&
632 git ls-files >tracked &&
633 test_line_count = 5 tracked &&
634 test_path_is_missing newdir/unrelated-file &&
635 test_path_is_file olddir/unrelated-file &&
636 test_path_is_file newdir/newfile &&
637 test_path_is_file newdir/b &&
638 test_path_is_file newdir/a &&
639 test_path_is_file somefile
644 # The following testcase is *very* similar to the last one, but instead of
645 # adding a new olddir/newfile, it renames somefile -> olddir/newfile:
646 # Base: somefile
647 # olddir/a
648 # olddir/b
649 # Upstream: somefile_1 -> olddir/newfile
650 # Topic_1: rename olddir/ -> newdir/
651 # somefile_2
652 # Topic_2: olddir/unrelated-file
653 # somefile_3
655 # Much like the previous test, this case is actually trivial and we are just
656 # making sure there isn't some spurious directory rename caching going on
657 # for the wrong side of history.
660 # This testcase should only need two calls to diffcore_rename_extended(),
661 # both for the first merge, one for each side of history.
663 test_expect_success 'caching renames only on upstream side, part 2' '
664 test_setup_topic_rename cache-renames-only-upstream-rename-file &&
666 cd cache-renames-only-upstream-rename-file &&
668 git switch upstream &&
670 git mv somefile olddir/newfile &&
671 git commit -m "Add newfile" &&
674 # Actual testing
677 git switch upstream &&
679 git config merge.directoryRenames true &&
681 GIT_TRACE2_PERF="$(pwd)/trace.output" &&
682 export GIT_TRACE2_PERF &&
684 test-tool fast-rebase --onto HEAD upstream~1 topic &&
685 #git cherry-pick upstream..topic &&
687 grep region_enter.*diffcore_rename trace.output >calls &&
688 test_line_count = 2 calls &&
690 git ls-files >tracked &&
691 test_line_count = 4 tracked &&
692 test_path_is_missing newdir/unrelated-file &&
693 test_path_is_file olddir/unrelated-file &&
694 test_path_is_file newdir/newfile &&
695 test_path_is_file newdir/b &&
696 test_path_is_file newdir/a
701 # The following testcase just creates two simple renames (slightly modified
702 # on both sides but without conflicting changes), and a directory full of
703 # files that are otherwise uninteresting. The setup is as follows:
705 # base: unrelated/<BUNCH OF FILES>
706 # numbers
707 # values
708 # upstream: modify: numbers
709 # modify: values
710 # topic: add: unrelated/foo
711 # modify: numbers
712 # modify: values
713 # rename: numbers -> sequence
714 # rename: values -> progression
716 # This is a trivial rename case, but we're curious what happens with a very
717 # low renameLimit interacting with the restart optimization trying to notice
718 # that unrelated/ looks like a trivial merge candidate.
720 test_expect_success 'avoid assuming we detected renames' '
721 git init redo-weirdness &&
723 cd redo-weirdness &&
725 mkdir unrelated &&
726 for i in $(test_seq 1 10)
728 >unrelated/$i || exit 1
729 done &&
730 test_seq 2 10 >numbers &&
731 test_seq 12 20 >values &&
732 git add numbers values unrelated/ &&
733 git commit -m orig &&
735 git branch upstream &&
736 git branch topic &&
738 git switch upstream &&
739 test_seq 1 10 >numbers &&
740 test_seq 11 20 >values &&
741 git add numbers &&
742 git commit -m "Some tweaks" &&
744 git switch topic &&
746 >unrelated/foo &&
747 test_seq 2 12 >numbers &&
748 test_seq 12 22 >values &&
749 git add numbers values unrelated/ &&
750 git mv numbers sequence &&
751 git mv values progression &&
752 git commit -m A &&
755 # Actual testing
758 git switch --detach topic^0 &&
760 test_must_fail git -c merge.renameLimit=1 rebase upstream &&
762 git ls-files -u >actual &&
763 test_line_count = 2 actual
767 test_done