1#!/bin/sh 2 3test_description="remember regular & dir renames in sequence of merges" 4 5. ./test-lib.sh 6 7# 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. 13# 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'. 22# 23 24 25# 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. 33# 34# This shouldn't be a challenge, it's just verifying that cached renames isn't 35# preventing us from finding new renames. 36# 37test_expect_success 'caching renames does not preclude finding new ones' ' 38 test_create_repo caching-renames-and-new-renames && 39 ( 40 cd caching-renames-and-new-renames && 41 42 test_seq 2 10 >numbers && 43 test_seq 2 10 >values && 44 git add numbers values && 45 git commit -m orig && 46 47 git branch upstream && 48 git branch topic && 49 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" && 55 56 git switch topic && 57 58 test_seq 2 12 >numbers && 59 git add numbers && 60 git mv numbers sequence && 61 git commit -m A && 62 63 test_seq 2 12 >values && 64 git add values && 65 git mv values scruples && 66 git commit -m B && 67 68 # 69 # Actual testing 70 # 71 72 git switch upstream && 73 74 test-tool fast-rebase --onto HEAD upstream~1 topic && 75 #git cherry-pick upstream~1..topic 76 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 82 ) 83' 84 85# 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. 94# 95# Can git apply both patches? 96# 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. 103# 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.) 108test_expect_success 'cherry-pick both a commit and its immediate revert' ' 109 test_create_repo pick-commit-and-its-immediate-revert && 110 ( 111 cd pick-commit-and-its-immediate-revert && 112 113 test_seq 11 30 >numbers && 114 git add numbers && 115 git commit -m orig && 116 117 git branch upstream && 118 git branch topic && 119 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" && 125 126 git switch topic && 127 128 test_seq 11 13 >numbers && 129 git add numbers && 130 git commit -m A && 131 132 git revert HEAD && 133 134 # 135 # Actual testing 136 # 137 138 git switch upstream && 139 140 GIT_TRACE2_PERF="$(pwd)/trace.output" && 141 export GIT_TRACE2_PERF && 142 143 test-tool fast-rebase --onto HEAD upstream~1 topic && 144 #git cherry-pick upstream~1..topic && 145 146 grep region_enter.*diffcore_rename trace.output >calls && 147 test_line_count = 1 calls 148 ) 149' 150 151# 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. 159# 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. 163# 164test_expect_success 'rename same file identically, then reintroduce it' ' 165 test_create_repo rename-rename-1to1-then-add-old-filename && 166 ( 167 cd rename-rename-1to1-then-add-old-filename && 168 169 test_seq 3 8 >sequence && 170 git add sequence && 171 git commit -m orig && 172 173 git branch upstream && 174 git branch topic && 175 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" && 181 182 git switch topic && 183 184 test_seq 3 10 >sequence && 185 git add sequence && 186 git mv sequence values && 187 git commit -m A && 188 189 test_write_lines A B C D E F G H I J >sequence && 190 git add sequence && 191 git commit -m B && 192 193 # 194 # Actual testing 195 # 196 197 git switch upstream && 198 199 GIT_TRACE2_PERF="$(pwd)/trace.output" && 200 export GIT_TRACE2_PERF && 201 202 test-tool fast-rebase --onto HEAD upstream~1 topic && 203 #git cherry-pick upstream~1..topic && 204 205 git ls-files >tracked && 206 test_line_count = 2 tracked && 207 test_path_is_file values && 208 test_path_is_file sequence && 209 210 grep region_enter.*diffcore_rename trace.output >calls && 211 test_line_count = 2 calls 212 ) 213' 214 215# 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} 226# 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. 230# 231test_expect_success 'rename same file identically, then add file to old dir' ' 232 test_create_repo rename-rename-1to1-then-add-file-to-old-dir && 233 ( 234 cd rename-rename-1to1-then-add-file-to-old-dir && 235 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 && 242 243 git branch upstream && 244 git branch topic && 245 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*" && 255 256 git switch topic && 257 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 && 264 265 >olddir/newfile && 266 git add olddir/newfile && 267 git commit -m B && 268 269 # 270 # Actual testing 271 # 272 273 git switch upstream && 274 git config merge.directoryRenames true && 275 276 GIT_TRACE2_PERF="$(pwd)/trace.output" && 277 export GIT_TRACE2_PERF && 278 279 test-tool fast-rebase --onto HEAD upstream~1 topic && 280 #git cherry-pick upstream~1..topic && 281 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 && 288 289 grep region_enter.*diffcore_rename trace.output >calls && 290 test_line_count = 3 calls 291 ) 292' 293 294# 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/ 303# 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. 306# 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.) 312# 313test_expect_success 'cached dir rename does not prevent noticing later conflict' ' 314 test_create_repo dir-rename-cache-not-occluding-later-conflict && 315 ( 316 cd dir-rename-cache-not-occluding-later-conflict && 317 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 && 323 324 git branch upstream && 325 git branch topic && 326 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" && 333 334 git switch topic && 335 336 >olddir/newfile && 337 git add olddir/newfile && 338 git commit -m A && 339 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 && 345 346 # 347 # Actual testing 348 # 349 350 git switch upstream && 351 git config merge.directoryRenames true && 352 353 GIT_TRACE2_PERF="$(pwd)/trace.output" && 354 export GIT_TRACE2_PERF && 355 356 test_must_fail test-tool fast-rebase --onto HEAD upstream~1 topic >output && 357 #git cherry-pick upstream..topic && 358 359 grep CONFLICT..rename/rename output && 360 361 grep region_enter.*diffcore_rename trace.output >calls && 362 test_line_count = 2 calls 363 ) 364' 365 366# Helper for the next two tests 367test_setup_upstream_rename () { 368 test_create_repo $1 && 369 ( 370 cd $1 && 371 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 && 380 381 git branch upstream && 382 git branch topic && 383 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" 394 ) 395} 396 397# 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 409# 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. 416# 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. 428# 429test_expect_success 'dir rename unneeded, then add new file to old dir' ' 430 test_setup_upstream_rename dir-rename-unneeded-until-new-file && 431 ( 432 cd dir-rename-unneeded-until-new-file && 433 434 git switch topic && 435 436 test_seq 3 10 >relevant-rename && 437 git add relevant-rename && 438 git commit -m A && 439 440 echo foo >olddir/newfile && 441 git add olddir/newfile && 442 git commit -m B && 443 444 echo bar >>olddir/newfile && 445 git add olddir/newfile && 446 git commit -m C && 447 448 # 449 # Actual testing 450 # 451 452 git switch upstream && 453 git config merge.directoryRenames true && 454 455 GIT_TRACE2_PERF="$(pwd)/trace.output" && 456 export GIT_TRACE2_PERF && 457 458 test-tool fast-rebase --onto HEAD upstream~1 topic && 459 #git cherry-pick upstream..topic && 460 461 grep region_enter.*diffcore_rename trace.output >calls && 462 test_line_count = 2 calls && 463 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 468 ) 469' 470 471# 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 483# 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. 491# 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. 494test_expect_success 'dir rename unneeded, then rename existing file into old dir' ' 495 test_setup_upstream_rename dir-rename-unneeded-until-file-moved-inside && 496 ( 497 cd dir-rename-unneeded-until-file-moved-inside && 498 499 git switch topic && 500 501 test_seq 3 10 >relevant-rename && 502 git add relevant-rename && 503 git commit -m A && 504 505 test_seq 1 10 >somefile && 506 git add somefile && 507 git mv somefile olddir/newfile && 508 git commit -m B && 509 510 test_seq 1 12 >olddir/newfile && 511 git add olddir/newfile && 512 git commit -m C && 513 514 # 515 # Actual testing 516 # 517 518 git switch upstream && 519 git config merge.directoryRenames true && 520 521 GIT_TRACE2_PERF="$(pwd)/trace.output" && 522 export GIT_TRACE2_PERF && 523 524 test-tool fast-rebase --onto HEAD upstream~1 topic && 525 #git cherry-pick upstream..topic && 526 527 grep region_enter.*diffcore_rename trace.output >calls && 528 test_line_count = 3 calls && 529 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 535 ) 536' 537 538# Helper for the next two tests 539test_setup_topic_rename () { 540 test_create_repo $1 && 541 ( 542 cd $1 && 543 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 && 550 551 git branch upstream && 552 git branch topic && 553 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" && 560 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" 567 ) 568} 569 570# 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 581# 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} 591# 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 597# 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. 602# 603# This testcase should only need one call to diffcore_rename_extended(). 604test_expect_success 'caching renames only on upstream side, part 1' ' 605 test_setup_topic_rename cache-renames-only-upstream-add-file && 606 ( 607 cd cache-renames-only-upstream-add-file && 608 609 git switch upstream && 610 611 >olddir/newfile && 612 git add olddir/newfile && 613 git commit -m "Add newfile" && 614 615 # 616 # Actual testing 617 # 618 619 git switch upstream && 620 621 git config merge.directoryRenames true && 622 623 GIT_TRACE2_PERF="$(pwd)/trace.output" && 624 export GIT_TRACE2_PERF && 625 626 test-tool fast-rebase --onto HEAD upstream~1 topic && 627 #git cherry-pick upstream..topic && 628 629 grep region_enter.*diffcore_rename trace.output >calls && 630 test_line_count = 1 calls && 631 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 640 ) 641' 642 643# 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 654# 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. 658# 659# 660# This testcase should only need two calls to diffcore_rename_extended(), 661# both for the first merge, one for each side of history. 662# 663test_expect_success 'caching renames only on upstream side, part 2' ' 664 test_setup_topic_rename cache-renames-only-upstream-rename-file && 665 ( 666 cd cache-renames-only-upstream-rename-file && 667 668 git switch upstream && 669 670 git mv somefile olddir/newfile && 671 git commit -m "Add newfile" && 672 673 # 674 # Actual testing 675 # 676 677 git switch upstream && 678 679 git config merge.directoryRenames true && 680 681 GIT_TRACE2_PERF="$(pwd)/trace.output" && 682 export GIT_TRACE2_PERF && 683 684 test-tool fast-rebase --onto HEAD upstream~1 topic && 685 #git cherry-pick upstream..topic && 686 687 grep region_enter.*diffcore_rename trace.output >calls && 688 test_line_count = 2 calls && 689 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 697 ) 698' 699 700test_done 701