| 1 | #!/bin/sh |
| 2 | |
| 3 | test_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 replay instead of either cherry-pick or rebase. |
| 15 | # sequencer.c is only superficially integrated with merge-ort; it |
| 16 | # calls merge_switch_to_result() after EACH merge, which updates the |
| 17 | # index and working copy AND throws away the cached results (because |
| 18 | # merge_switch_to_result() is only supposed to be called at the end |
| 19 | # of the sequence). Integrating them more deeply is a big task, so |
| 20 | # for now the tests use 'git replay'. |
| 21 | # |
| 22 | |
| 23 | |
| 24 | # |
| 25 | # In the following simple testcase: |
| 26 | # Base: numbers_1, values_1 |
| 27 | # Upstream: numbers_2, values_2 |
| 28 | # Topic_1: sequence_3 |
| 29 | # Topic_2: scruples_3 |
| 30 | # or, in english, rename numbers -> sequence in the first commit, and rename |
| 31 | # values -> scruples in the second commit. |
| 32 | # |
| 33 | # This shouldn't be a challenge, it's just verifying that cached renames isn't |
| 34 | # preventing us from finding new renames. |
| 35 | # |
| 36 | test_expect_success 'caching renames does not preclude finding new ones' ' |
| 37 | git init caching-renames-and-new-renames && |
| 38 | ( |
| 39 | cd caching-renames-and-new-renames && |
| 40 | |
| 41 | test_seq 2 10 >numbers && |
| 42 | test_seq 2 10 >values && |
| 43 | git add numbers values && |
| 44 | git commit -m orig && |
| 45 | |
| 46 | git branch upstream && |
| 47 | git branch topic && |
| 48 | |
| 49 | git switch upstream && |
| 50 | test_seq 1 10 >numbers && |
| 51 | test_seq 1 10 >values && |
| 52 | git add numbers values && |
| 53 | git commit -m "Tweaked both files" && |
| 54 | |
| 55 | git switch topic && |
| 56 | |
| 57 | test_seq 2 12 >numbers && |
| 58 | git add numbers && |
| 59 | git mv numbers sequence && |
| 60 | git commit -m A && |
| 61 | |
| 62 | test_seq 2 12 >values && |
| 63 | git add values && |
| 64 | git mv values scruples && |
| 65 | git commit -m B && |
| 66 | |
| 67 | # |
| 68 | # Actual testing |
| 69 | # |
| 70 | |
| 71 | git switch upstream && |
| 72 | |
| 73 | git replay --onto HEAD upstream~1..topic >out && |
| 74 | git update-ref --stdin <out && |
| 75 | git checkout 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.) |
| 108 | test_expect_success 'cherry-pick both a commit and its immediate revert' ' |
| 109 | git init 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 | git replay --onto HEAD upstream~1..topic >out && |
| 144 | git update-ref --stdin <out && |
| 145 | git checkout topic && |
| 146 | |
| 147 | grep region_enter.*diffcore_rename trace.output >calls && |
| 148 | test_line_count = 1 calls |
| 149 | ) |
| 150 | ' |
| 151 | |
| 152 | # |
| 153 | # In the following testcase: |
| 154 | # Base: sequence_1 |
| 155 | # Upstream: rename sequence_1 -> values_2 |
| 156 | # Topic_1: rename sequence_1 -> values_3 |
| 157 | # Topic_2: add unrelated sequence_4 |
| 158 | # or, in english, both sides rename sequence -> values, and then the second |
| 159 | # commit on the topic branch adds an unrelated file called sequence. |
| 160 | # |
| 161 | # This testcase presents no problems for git traditionally, but having both |
| 162 | # sides do the same rename in effect "uses it up" and if it remains cached, |
| 163 | # could cause a spurious rename/add conflict. |
| 164 | # |
| 165 | test_expect_success 'rename same file identically, then reintroduce it' ' |
| 166 | git init rename-rename-1to1-then-add-old-filename && |
| 167 | ( |
| 168 | cd rename-rename-1to1-then-add-old-filename && |
| 169 | |
| 170 | test_seq 3 8 >sequence && |
| 171 | git add sequence && |
| 172 | git commit -m orig && |
| 173 | |
| 174 | git branch upstream && |
| 175 | git branch topic && |
| 176 | |
| 177 | git switch upstream && |
| 178 | test_seq 1 8 >sequence && |
| 179 | git add sequence && |
| 180 | git mv sequence values && |
| 181 | git commit -m "Renamed (and modified) sequence -> values" && |
| 182 | |
| 183 | git switch topic && |
| 184 | |
| 185 | test_seq 3 10 >sequence && |
| 186 | git add sequence && |
| 187 | git mv sequence values && |
| 188 | git commit -m A && |
| 189 | |
| 190 | test_write_lines A B C D E F G H I J >sequence && |
| 191 | git add sequence && |
| 192 | git commit -m B && |
| 193 | |
| 194 | # |
| 195 | # Actual testing |
| 196 | # |
| 197 | |
| 198 | git switch upstream && |
| 199 | |
| 200 | GIT_TRACE2_PERF="$(pwd)/trace.output" && |
| 201 | export GIT_TRACE2_PERF && |
| 202 | |
| 203 | git replay --onto HEAD upstream~1..topic >out && |
| 204 | git update-ref --stdin <out && |
| 205 | git checkout topic && |
| 206 | |
| 207 | git ls-files >tracked && |
| 208 | test_line_count = 2 tracked && |
| 209 | test_path_is_file values && |
| 210 | test_path_is_file sequence && |
| 211 | |
| 212 | grep region_enter.*diffcore_rename trace.output >calls && |
| 213 | test_line_count = 2 calls |
| 214 | ) |
| 215 | ' |
| 216 | |
| 217 | # |
| 218 | # In the following testcase: |
| 219 | # Base: olddir/{valuesZ_1, valuesY_1, valuesX_1} |
| 220 | # Upstream: rename olddir/valuesZ_1 -> dirA/valuesZ_2 |
| 221 | # rename olddir/valuesY_1 -> dirA/valuesY_2 |
| 222 | # rename olddir/valuesX_1 -> dirB/valuesX_2 |
| 223 | # Topic_1: rename olddir/valuesZ_1 -> dirA/valuesZ_3 |
| 224 | # rename olddir/valuesY_1 -> dirA/valuesY_3 |
| 225 | # Topic_2: add olddir/newfile |
| 226 | # Expected Pick1: dirA/{valuesZ, valuesY}, dirB/valuesX |
| 227 | # Expected Pick2: dirA/{valuesZ, valuesY}, dirB/{valuesX, newfile} |
| 228 | # |
| 229 | # This testcase presents no problems for git traditionally, but having both |
| 230 | # sides do the same renames in effect "use it up" but if the renames remain |
| 231 | # cached, the directory rename could put newfile in the wrong directory. |
| 232 | # |
| 233 | test_expect_success 'rename same file identically, then add file to old dir' ' |
| 234 | git init rename-rename-1to1-then-add-file-to-old-dir && |
| 235 | ( |
| 236 | cd rename-rename-1to1-then-add-file-to-old-dir && |
| 237 | |
| 238 | mkdir olddir/ && |
| 239 | test_seq 3 8 >olddir/valuesZ && |
| 240 | test_seq 3 8 >olddir/valuesY && |
| 241 | test_seq 3 8 >olddir/valuesX && |
| 242 | git add olddir && |
| 243 | git commit -m orig && |
| 244 | |
| 245 | git branch upstream && |
| 246 | git branch topic && |
| 247 | |
| 248 | git switch upstream && |
| 249 | test_seq 1 8 >olddir/valuesZ && |
| 250 | test_seq 1 8 >olddir/valuesY && |
| 251 | test_seq 1 8 >olddir/valuesX && |
| 252 | git add olddir && |
| 253 | mkdir dirA && |
| 254 | git mv olddir/valuesZ olddir/valuesY dirA && |
| 255 | git mv olddir/ dirB/ && |
| 256 | git commit -m "Renamed (and modified) values*" && |
| 257 | |
| 258 | git switch topic && |
| 259 | |
| 260 | test_seq 3 10 >olddir/valuesZ && |
| 261 | test_seq 3 10 >olddir/valuesY && |
| 262 | git add olddir && |
| 263 | mkdir dirA && |
| 264 | git mv olddir/valuesZ olddir/valuesY dirA && |
| 265 | git commit -m A && |
| 266 | |
| 267 | >olddir/newfile && |
| 268 | git add olddir/newfile && |
| 269 | git commit -m B && |
| 270 | |
| 271 | # |
| 272 | # Actual testing |
| 273 | # |
| 274 | |
| 275 | git switch upstream && |
| 276 | git config merge.directoryRenames true && |
| 277 | |
| 278 | GIT_TRACE2_PERF="$(pwd)/trace.output" && |
| 279 | export GIT_TRACE2_PERF && |
| 280 | |
| 281 | git replay --onto HEAD upstream~1..topic >out && |
| 282 | git update-ref --stdin <out && |
| 283 | git checkout topic && |
| 284 | |
| 285 | git ls-files >tracked && |
| 286 | test_line_count = 4 tracked && |
| 287 | test_path_is_file dirA/valuesZ && |
| 288 | test_path_is_file dirA/valuesY && |
| 289 | test_path_is_file dirB/valuesX && |
| 290 | test_path_is_file dirB/newfile && |
| 291 | |
| 292 | grep region_enter.*diffcore_rename trace.output >calls && |
| 293 | test_line_count = 3 calls |
| 294 | ) |
| 295 | ' |
| 296 | |
| 297 | # |
| 298 | # In the following testcase, upstream renames a directory, and the topic branch |
| 299 | # first adds a file to the directory, then later renames the directory |
| 300 | # differently: |
| 301 | # Base: olddir/a |
| 302 | # olddir/b |
| 303 | # Upstream: rename olddir/ -> newdir/ |
| 304 | # Topic_1: add olddir/newfile |
| 305 | # Topic_2: rename olddir/ -> otherdir/ |
| 306 | # |
| 307 | # Here we are just concerned that cached renames might prevent us from seeing |
| 308 | # the rename conflict, and we want to ensure that we do get a conflict. |
| 309 | # |
| 310 | # While at it, though, we do test that we only try to detect renames 2 |
| 311 | # times and not three. (The first merge needs to detect renames on the |
| 312 | # upstream side. Traditionally, the second merge would need to detect |
| 313 | # renames on both sides of history, but our caching of upstream renames |
| 314 | # should avoid the need to re-detect upstream renames.) |
| 315 | # |
| 316 | test_expect_success 'cached dir rename does not prevent noticing later conflict' ' |
| 317 | git init dir-rename-cache-not-occluding-later-conflict && |
| 318 | ( |
| 319 | cd dir-rename-cache-not-occluding-later-conflict && |
| 320 | |
| 321 | mkdir olddir && |
| 322 | test_seq 3 10 >olddir/a && |
| 323 | test_seq 3 10 >olddir/b && |
| 324 | git add olddir && |
| 325 | git commit -m orig && |
| 326 | |
| 327 | git branch upstream && |
| 328 | git branch topic && |
| 329 | |
| 330 | git switch upstream && |
| 331 | test_seq 3 10 >olddir/a && |
| 332 | test_seq 3 10 >olddir/b && |
| 333 | git add olddir && |
| 334 | git mv olddir newdir && |
| 335 | git commit -m "Dir renamed" && |
| 336 | |
| 337 | git switch topic && |
| 338 | |
| 339 | >olddir/newfile && |
| 340 | git add olddir/newfile && |
| 341 | git commit -m A && |
| 342 | |
| 343 | test_seq 1 8 >olddir/a && |
| 344 | test_seq 1 8 >olddir/b && |
| 345 | git add olddir && |
| 346 | git mv olddir otherdir && |
| 347 | git commit -m B && |
| 348 | |
| 349 | # |
| 350 | # Actual testing |
| 351 | # |
| 352 | |
| 353 | git switch upstream && |
| 354 | git config merge.directoryRenames true && |
| 355 | |
| 356 | GIT_TRACE2_PERF="$(pwd)/trace.output" && |
| 357 | export GIT_TRACE2_PERF && |
| 358 | |
| 359 | test_must_fail git replay --onto HEAD upstream~1..topic >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 |
| 367 | test_setup_upstream_rename () { |
| 368 | git init $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 | # |
| 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 && |
| 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 | git replay --onto HEAD upstream~1..topic >out && |
| 459 | git update-ref --stdin <out && |
| 460 | git checkout topic && |
| 461 | |
| 462 | grep region_enter.*diffcore_rename trace.output >calls && |
| 463 | test_line_count = 2 calls && |
| 464 | |
| 465 | git ls-files >tracked && |
| 466 | test_line_count = 5 tracked && |
| 467 | test_path_is_missing olddir/newfile && |
| 468 | test_path_is_file newdir/newfile |
| 469 | ) |
| 470 | ' |
| 471 | |
| 472 | # |
| 473 | # The following testcase is *very* similar to the last one, but instead of |
| 474 | # adding a new olddir/newfile, it renames somefile -> olddir/newfile: |
| 475 | # Base: relevant-rename_1 |
| 476 | # somefile_1 |
| 477 | # olddir/a |
| 478 | # olddir/b |
| 479 | # Upstream: rename relevant-rename_1 -> renamed_2 |
| 480 | # rename olddir/ -> newdir/ |
| 481 | # Topic_1: relevant-rename_3 |
| 482 | # Topic_2: rename somefile -> olddir/newfile_2 |
| 483 | # Topic_3: modify olddir/newfile_3 |
| 484 | # |
| 485 | # In this testcase, since the first commit being picked only modifies a |
| 486 | # file in the toplevel directory, the directory rename is irrelevant for |
| 487 | # that first merge. However, we need to notice the directory rename for |
| 488 | # the merge that picks the second commit, and we don't want the third |
| 489 | # commit to mess up its location either. We want to make sure that |
| 490 | # neither somefile or olddir/newfile exists in the result and that |
| 491 | # newdir/newfile does. |
| 492 | # |
| 493 | # This testcase needs one more call to rename detection than the last |
| 494 | # testcase, because of the somefile -> olddir/newfile rename in Topic_2. |
| 495 | test_expect_success 'dir rename unneeded, then rename existing file into old dir' ' |
| 496 | test_setup_upstream_rename dir-rename-unneeded-until-file-moved-inside && |
| 497 | ( |
| 498 | cd dir-rename-unneeded-until-file-moved-inside && |
| 499 | |
| 500 | git switch topic && |
| 501 | |
| 502 | test_seq 3 10 >relevant-rename && |
| 503 | git add relevant-rename && |
| 504 | git commit -m A && |
| 505 | |
| 506 | test_seq 1 10 >somefile && |
| 507 | git add somefile && |
| 508 | git mv somefile olddir/newfile && |
| 509 | git commit -m B && |
| 510 | |
| 511 | test_seq 1 12 >olddir/newfile && |
| 512 | git add olddir/newfile && |
| 513 | git commit -m C && |
| 514 | |
| 515 | # |
| 516 | # Actual testing |
| 517 | # |
| 518 | |
| 519 | git switch upstream && |
| 520 | git config merge.directoryRenames true && |
| 521 | |
| 522 | GIT_TRACE2_PERF="$(pwd)/trace.output" && |
| 523 | export GIT_TRACE2_PERF && |
| 524 | |
| 525 | git replay --onto HEAD upstream~1..topic >out && |
| 526 | git update-ref --stdin <out && |
| 527 | git checkout topic && |
| 528 | |
| 529 | grep region_enter.*diffcore_rename trace.output >calls && |
| 530 | test_line_count = 3 calls && |
| 531 | |
| 532 | test_path_is_missing somefile && |
| 533 | test_path_is_missing olddir/newfile && |
| 534 | test_path_is_file newdir/newfile && |
| 535 | git ls-files >tracked && |
| 536 | test_line_count = 4 tracked |
| 537 | ) |
| 538 | ' |
| 539 | |
| 540 | # Helper for the next two tests |
| 541 | test_setup_topic_rename () { |
| 542 | git init $1 && |
| 543 | ( |
| 544 | cd $1 && |
| 545 | |
| 546 | test_seq 3 8 >somefile && |
| 547 | mkdir olddir && |
| 548 | test_seq 3 8 >olddir/a && |
| 549 | echo b >olddir/b && |
| 550 | git add olddir somefile && |
| 551 | git commit -m orig && |
| 552 | |
| 553 | git branch upstream && |
| 554 | git branch topic && |
| 555 | |
| 556 | git switch topic && |
| 557 | test_seq 1 8 >somefile && |
| 558 | test_seq 1 8 >olddir/a && |
| 559 | git add somefile olddir/a && |
| 560 | git mv olddir newdir && |
| 561 | git commit -m "Dir renamed" && |
| 562 | |
| 563 | test_seq 1 10 >somefile && |
| 564 | git add somefile && |
| 565 | mkdir olddir && |
| 566 | >olddir/unrelated-file && |
| 567 | git add olddir && |
| 568 | git commit -m "Unrelated file in recreated old dir" |
| 569 | ) |
| 570 | } |
| 571 | |
| 572 | # |
| 573 | # In the following testcase, the first commit on the topic branch renames |
| 574 | # a directory, while the second recreates the old directory and places a |
| 575 | # file into it: |
| 576 | # Base: somefile |
| 577 | # olddir/a |
| 578 | # olddir/b |
| 579 | # Upstream: olddir/newfile |
| 580 | # Topic_1: somefile_2 |
| 581 | # rename olddir/ -> newdir/ |
| 582 | # Topic_2: olddir/unrelated-file |
| 583 | # |
| 584 | # Note that the first pick should merge: |
| 585 | # Base: somefile |
| 586 | # olddir/{a,b} |
| 587 | # Upstream: olddir/newfile |
| 588 | # Topic_1: rename olddir/ -> newdir/ |
| 589 | # For which the expected result (assuming merge.directoryRenames=true) is |
| 590 | # clearly: |
| 591 | # Result: somefile |
| 592 | # newdir/{a, b, newfile} |
| 593 | # |
| 594 | # While the second pick does the following three-way merge: |
| 595 | # Base (Topic_1): somefile |
| 596 | # newdir/{a,b} |
| 597 | # Upstream (Result from 1): same files as base, but adds newdir/newfile |
| 598 | # Topic_2: same files as base, but adds olddir/unrelated-file |
| 599 | # |
| 600 | # The second merge is pretty trivial; upstream adds newdir/newfile, and |
| 601 | # topic_2 adds olddir/unrelated-file. We're just testing that we don't |
| 602 | # accidentally cache directory renames somehow and rename |
| 603 | # olddir/unrelated-file to newdir/unrelated-file. |
| 604 | # |
| 605 | # This testcase should only need one call to diffcore_rename_extended(). |
| 606 | test_expect_success 'caching renames only on upstream side, part 1' ' |
| 607 | test_setup_topic_rename cache-renames-only-upstream-add-file && |
| 608 | ( |
| 609 | cd cache-renames-only-upstream-add-file && |
| 610 | |
| 611 | git switch upstream && |
| 612 | |
| 613 | >olddir/newfile && |
| 614 | git add olddir/newfile && |
| 615 | git commit -m "Add newfile" && |
| 616 | |
| 617 | # |
| 618 | # Actual testing |
| 619 | # |
| 620 | |
| 621 | git switch upstream && |
| 622 | |
| 623 | git config merge.directoryRenames true && |
| 624 | |
| 625 | GIT_TRACE2_PERF="$(pwd)/trace.output" && |
| 626 | export GIT_TRACE2_PERF && |
| 627 | |
| 628 | git replay --onto HEAD upstream~1..topic >out && |
| 629 | git update-ref --stdin <out && |
| 630 | git checkout topic && |
| 631 | |
| 632 | grep region_enter.*diffcore_rename trace.output >calls && |
| 633 | test_line_count = 1 calls && |
| 634 | |
| 635 | git ls-files >tracked && |
| 636 | test_line_count = 5 tracked && |
| 637 | test_path_is_missing newdir/unrelated-file && |
| 638 | test_path_is_file olddir/unrelated-file && |
| 639 | test_path_is_file newdir/newfile && |
| 640 | test_path_is_file newdir/b && |
| 641 | test_path_is_file newdir/a && |
| 642 | test_path_is_file somefile |
| 643 | ) |
| 644 | ' |
| 645 | |
| 646 | # |
| 647 | # The following testcase is *very* similar to the last one, but instead of |
| 648 | # adding a new olddir/newfile, it renames somefile -> olddir/newfile: |
| 649 | # Base: somefile |
| 650 | # olddir/a |
| 651 | # olddir/b |
| 652 | # Upstream: somefile_1 -> olddir/newfile |
| 653 | # Topic_1: rename olddir/ -> newdir/ |
| 654 | # somefile_2 |
| 655 | # Topic_2: olddir/unrelated-file |
| 656 | # somefile_3 |
| 657 | # |
| 658 | # Much like the previous test, this case is actually trivial and we are just |
| 659 | # making sure there isn't some spurious directory rename caching going on |
| 660 | # for the wrong side of history. |
| 661 | # |
| 662 | # |
| 663 | # This testcase should only need two calls to diffcore_rename_extended(), |
| 664 | # both for the first merge, one for each side of history. |
| 665 | # |
| 666 | test_expect_success 'caching renames only on upstream side, part 2' ' |
| 667 | test_setup_topic_rename cache-renames-only-upstream-rename-file && |
| 668 | ( |
| 669 | cd cache-renames-only-upstream-rename-file && |
| 670 | |
| 671 | git switch upstream && |
| 672 | |
| 673 | git mv somefile olddir/newfile && |
| 674 | git commit -m "Add newfile" && |
| 675 | |
| 676 | # |
| 677 | # Actual testing |
| 678 | # |
| 679 | |
| 680 | git switch upstream && |
| 681 | |
| 682 | git config merge.directoryRenames true && |
| 683 | |
| 684 | GIT_TRACE2_PERF="$(pwd)/trace.output" && |
| 685 | export GIT_TRACE2_PERF && |
| 686 | |
| 687 | git replay --onto HEAD upstream~1..topic >out && |
| 688 | git update-ref --stdin <out && |
| 689 | git checkout topic && |
| 690 | |
| 691 | grep region_enter.*diffcore_rename trace.output >calls && |
| 692 | test_line_count = 2 calls && |
| 693 | |
| 694 | git ls-files >tracked && |
| 695 | test_line_count = 4 tracked && |
| 696 | test_path_is_missing newdir/unrelated-file && |
| 697 | test_path_is_file olddir/unrelated-file && |
| 698 | test_path_is_file newdir/newfile && |
| 699 | test_path_is_file newdir/b && |
| 700 | test_path_is_file newdir/a |
| 701 | ) |
| 702 | ' |
| 703 | |
| 704 | # |
| 705 | # The following testcase just creates two simple renames (slightly modified |
| 706 | # on both sides but without conflicting changes), and a directory full of |
| 707 | # files that are otherwise uninteresting. The setup is as follows: |
| 708 | # |
| 709 | # base: unrelated/<BUNCH OF FILES> |
| 710 | # numbers |
| 711 | # values |
| 712 | # upstream: modify: numbers |
| 713 | # modify: values |
| 714 | # topic: add: unrelated/foo |
| 715 | # modify: numbers |
| 716 | # modify: values |
| 717 | # rename: numbers -> sequence |
| 718 | # rename: values -> progression |
| 719 | # |
| 720 | # This is a trivial rename case, but we're curious what happens with a very |
| 721 | # low renameLimit interacting with the restart optimization trying to notice |
| 722 | # that unrelated/ looks like a trivial merge candidate. |
| 723 | # |
| 724 | test_expect_success 'avoid assuming we detected renames' ' |
| 725 | git init redo-weirdness && |
| 726 | ( |
| 727 | cd redo-weirdness && |
| 728 | |
| 729 | mkdir unrelated && |
| 730 | for i in $(test_seq 1 10) |
| 731 | do |
| 732 | >unrelated/$i || exit 1 |
| 733 | done && |
| 734 | test_seq 2 10 >numbers && |
| 735 | test_seq 12 20 >values && |
| 736 | git add numbers values unrelated/ && |
| 737 | git commit -m orig && |
| 738 | |
| 739 | git branch upstream && |
| 740 | git branch topic && |
| 741 | |
| 742 | git switch upstream && |
| 743 | test_seq 1 10 >numbers && |
| 744 | test_seq 11 20 >values && |
| 745 | git add numbers && |
| 746 | git commit -m "Some tweaks" && |
| 747 | |
| 748 | git switch topic && |
| 749 | |
| 750 | >unrelated/foo && |
| 751 | test_seq 2 12 >numbers && |
| 752 | test_seq 12 22 >values && |
| 753 | git add numbers values unrelated/ && |
| 754 | git mv numbers sequence && |
| 755 | git mv values progression && |
| 756 | git commit -m A && |
| 757 | |
| 758 | # |
| 759 | # Actual testing |
| 760 | # |
| 761 | |
| 762 | git switch --detach topic^0 && |
| 763 | |
| 764 | test_must_fail git -c merge.renameLimit=1 rebase upstream && |
| 765 | |
| 766 | git ls-files -u >actual && |
| 767 | test_line_count = 2 actual |
| 768 | ) |
| 769 | ' |
| 770 | |
| 771 | # |
| 772 | # In the following testcase: |
| 773 | # Base: olddir/{valuesX_1, valuesY_1, valuesZ_1} |
| 774 | # other/content |
| 775 | # Upstream: rename olddir/valuesX_1 -> newdir/valuesX_2 |
| 776 | # Topic_1: modify olddir/valuesX_1 -> olddir/valuesX_3 |
| 777 | # Topic_2: modify olddir/valuesY, |
| 778 | # modify other/content |
| 779 | # Expected Pick1: olddir/{valuesY, valuesZ}, newdir/valuesX, other/content |
| 780 | # Expected Pick2: olddir/{valuesY, valuesZ}, newdir/valuesX, other/content |
| 781 | # |
| 782 | # This testcase presents no problems for git traditionally, but the fact that |
| 783 | # olddir/valuesX -> newdir/valuesX |
| 784 | # gets cached after the first pick presents a problem for the second commit to |
| 785 | # be replayed, because it appears to be an irrelevant rename, so the trivial |
| 786 | # directory resolution will resolve newdir/ without recursing into it, giving |
| 787 | # us no way to apply the cached rename to anything. |
| 788 | # |
| 789 | test_expect_success 'rename a file, use it on first pick, but irrelevant on second' ' |
| 790 | git init rename_a_file_use_it_once_irrelevant_on_second && |
| 791 | ( |
| 792 | cd rename_a_file_use_it_once_irrelevant_on_second && |
| 793 | |
| 794 | mkdir olddir/ other/ && |
| 795 | test_seq 3 8 >olddir/valuesX && |
| 796 | test_seq 3 8 >olddir/valuesY && |
| 797 | test_seq 3 8 >olddir/valuesZ && |
| 798 | printf "%s\n" A B C D E F G >other/content && |
| 799 | git add olddir other && |
| 800 | git commit -m orig && |
| 801 | |
| 802 | git branch upstream && |
| 803 | git branch topic && |
| 804 | |
| 805 | git switch upstream && |
| 806 | test_seq 1 8 >olddir/valuesX && |
| 807 | git add olddir && |
| 808 | mkdir newdir && |
| 809 | git mv olddir/valuesX newdir && |
| 810 | git commit -m "Renamed (and modified) olddir/valuesX into newdir/" && |
| 811 | |
| 812 | git switch topic && |
| 813 | |
| 814 | test_seq 3 10 >olddir/valuesX && |
| 815 | git add olddir && |
| 816 | git commit -m A && |
| 817 | |
| 818 | test_seq 1 8 >olddir/valuesY && |
| 819 | printf "%s\n" A B C D E F G H I >other/content && |
| 820 | git add olddir/valuesY other && |
| 821 | git commit -m B && |
| 822 | |
| 823 | # |
| 824 | # Actual testing; mostly we want to verify that we do not hit |
| 825 | # git: merge-ort.c:3032: process_renames: Assertion `newinfo && !newinfo->merged.clean` failed. |
| 826 | # |
| 827 | |
| 828 | git switch upstream && |
| 829 | git config merge.directoryRenames true && |
| 830 | |
| 831 | git replay --onto HEAD upstream~1..topic >out && |
| 832 | |
| 833 | # |
| 834 | # ...but we may as well check that the replay gave us a reasonable result |
| 835 | # |
| 836 | |
| 837 | git update-ref --stdin <out && |
| 838 | git checkout topic && |
| 839 | |
| 840 | git ls-files >tracked && |
| 841 | test_line_count = 4 tracked && |
| 842 | test_path_is_file newdir/valuesX && |
| 843 | test_path_is_file olddir/valuesY && |
| 844 | test_path_is_file olddir/valuesZ && |
| 845 | test_path_is_file other/content |
| 846 | ) |
| 847 | ' |
| 848 | |
| 849 | # |
| 850 | # In the following testcase: |
| 851 | # Base: subdir/file_1 |
| 852 | # Upstream: file_1 (renamed from subdir/file) |
| 853 | # Topic_1: subdir/file_2 (modified subdir/file) |
| 854 | # Topic_2: subdir/file_2, file_2 (added another "file" with same contents) |
| 855 | # Topic_3: file_2 (deleted subdir/file) |
| 856 | # |
| 857 | # |
| 858 | # This testcase presents no problems for git traditionally, but the fact that |
| 859 | # subdir/file -> file |
| 860 | # gets cached after the first pick presents a problem for the third commit |
| 861 | # to be replayed, because file has contents file_2 on all three sides and |
| 862 | # is thus trivially resolved early. The point of renames is to allow us to |
| 863 | # three-way merge contents across multiple filenames, but if the target is |
| 864 | # already resolved, we risk throwing an assertion. Verify that the code |
| 865 | # correctly drops the irrelevant rename in order to avoid hitting that |
| 866 | # assertion. |
| 867 | # |
| 868 | test_expect_success 'cached rename does not assert on trivially clean target' ' |
| 869 | git init cached-rename-trivially-clean-target && |
| 870 | ( |
| 871 | cd cached-rename-trivially-clean-target && |
| 872 | |
| 873 | mkdir subdir && |
| 874 | printf "%s\n" 1 2 3 >subdir/file && |
| 875 | git add subdir/file && |
| 876 | git commit -m orig && |
| 877 | |
| 878 | git branch upstream && |
| 879 | git branch topic && |
| 880 | |
| 881 | git switch upstream && |
| 882 | git mv subdir/file file && |
| 883 | git commit -m "rename subdir/file to file" && |
| 884 | |
| 885 | git switch topic && |
| 886 | |
| 887 | echo 4 >>subdir/file && |
| 888 | git add subdir/file && |
| 889 | git commit -m "modify subdir/file" && |
| 890 | |
| 891 | cp subdir/file file && |
| 892 | git add file && |
| 893 | git commit -m "copy subdir/file to file" && |
| 894 | |
| 895 | git rm subdir/file && |
| 896 | git commit -m "delete subdir/file" && |
| 897 | |
| 898 | git switch upstream && |
| 899 | git replay --onto HEAD upstream..topic && |
| 900 | git checkout topic && |
| 901 | |
| 902 | git ls-files >tracked-files && |
| 903 | test_line_count = 1 tracked-files && |
| 904 | printf "%s\n" 1 2 3 4 >expect && |
| 905 | test_cmp expect file |
| 906 | ) |
| 907 | ' |
| 908 | |
| 909 | test_done |