| 1 | #!/bin/sh |
| 2 | |
| 3 | test_description='add -i basic tests' |
| 4 | GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main |
| 5 | export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME |
| 6 | |
| 7 | . ./test-lib.sh |
| 8 | . "$TEST_DIRECTORY"/lib-terminal.sh |
| 9 | |
| 10 | SP=" " |
| 11 | |
| 12 | diff_cmp () { |
| 13 | for x |
| 14 | do |
| 15 | sed -e '/^index/s/[0-9a-f]*[1-9a-f][0-9a-f]*\.\./1234567../' \ |
| 16 | -e '/^index/s/\.\.[0-9a-f]*[1-9a-f][0-9a-f]*/..9abcdef/' \ |
| 17 | -e '/^index/s/ 00*\.\./ 0000000../' \ |
| 18 | -e '/^index/s/\.\.00*$/..0000000/' \ |
| 19 | -e '/^index/s/\.\.00* /..0000000 /' \ |
| 20 | "$x" >"$x.filtered" |
| 21 | done |
| 22 | test_cmp "$1.filtered" "$2.filtered" |
| 23 | } |
| 24 | |
| 25 | # This function uses a trick to manipulate the interactive add to use color: |
| 26 | # the `want_color()` function special-cases the situation where a pager was |
| 27 | # spawned and Git now wants to output colored text: to detect that situation, |
| 28 | # the environment variable `GIT_PAGER_IN_USE` is set. However, color is |
| 29 | # suppressed despite that environment variable if the `TERM` variable |
| 30 | # indicates a dumb terminal, so we set that variable, too. |
| 31 | |
| 32 | force_color () { |
| 33 | # The first element of $@ may be a shell function, as a result POSIX |
| 34 | # does not guarantee that "one-shot assignment" will not persist after |
| 35 | # the function call. Thus, we prevent these variables from escaping |
| 36 | # this function's context with this subshell. |
| 37 | ( |
| 38 | GIT_PAGER_IN_USE=true && |
| 39 | TERM=vt100 && |
| 40 | export GIT_PAGER_IN_USE TERM && |
| 41 | "$@" |
| 42 | ) |
| 43 | } |
| 44 | |
| 45 | test_expect_success 'unknown command' ' |
| 46 | test_when_finished "git reset --hard; rm -f command" && |
| 47 | echo W >command && |
| 48 | git add -N command && |
| 49 | git diff command >expect && |
| 50 | cat >>expect <<-EOF && |
| 51 | (1/1) Stage addition [y,n,q,a,d,e,p,P,?]? Unknown command ${SQ}W${SQ} (use ${SQ}?${SQ} for help) |
| 52 | (1/1) Stage addition [y,n,q,a,d,e,p,P,?]?$SP |
| 53 | EOF |
| 54 | git add -p -- command <command >actual 2>&1 && |
| 55 | test_cmp expect actual |
| 56 | ' |
| 57 | |
| 58 | test_expect_success 'setup (initial)' ' |
| 59 | echo content >file && |
| 60 | git add file && |
| 61 | echo more >>file && |
| 62 | echo lines >>file |
| 63 | ' |
| 64 | test_expect_success 'status works (initial)' ' |
| 65 | git add -i </dev/null >output && |
| 66 | test_grep "+1/-0 *+2/-0 file" output |
| 67 | ' |
| 68 | |
| 69 | test_expect_success 'setup expected' ' |
| 70 | cat >expected <<-\EOF |
| 71 | new file mode 100644 |
| 72 | index 0000000..d95f3ad |
| 73 | --- /dev/null |
| 74 | +++ b/file |
| 75 | @@ -0,0 +1 @@ |
| 76 | +content |
| 77 | EOF |
| 78 | ' |
| 79 | |
| 80 | test_expect_success 'diff works (initial)' ' |
| 81 | test_write_lines d 1 | git add -i >output && |
| 82 | sed -ne "/new file/,/content/p" <output >diff && |
| 83 | diff_cmp expected diff |
| 84 | ' |
| 85 | test_expect_success 'revert works (initial)' ' |
| 86 | git add file && |
| 87 | test_write_lines r 1 | git add -i && |
| 88 | git ls-files >output && |
| 89 | test_grep ! . output |
| 90 | ' |
| 91 | |
| 92 | test_expect_success 'add untracked (multiple)' ' |
| 93 | test_when_finished "git reset && rm [1-9]" && |
| 94 | touch $(test_seq 9) && |
| 95 | test_write_lines a "2-5 8-" | git add -i -- [1-9] && |
| 96 | test_write_lines 2 3 4 5 8 9 >expected && |
| 97 | git ls-files [1-9] >output && |
| 98 | test_cmp expected output |
| 99 | ' |
| 100 | |
| 101 | test_expect_success 'setup (commit)' ' |
| 102 | echo baseline >file && |
| 103 | git add file && |
| 104 | git commit -m commit && |
| 105 | echo content >>file && |
| 106 | git add file && |
| 107 | echo more >>file && |
| 108 | echo lines >>file |
| 109 | ' |
| 110 | test_expect_success 'status works (commit)' ' |
| 111 | git add -i </dev/null >output && |
| 112 | test_grep "+1/-0 *+2/-0 file" output |
| 113 | ' |
| 114 | |
| 115 | test_expect_success 'update can stage deletions' ' |
| 116 | >to-delete && |
| 117 | git add to-delete && |
| 118 | rm to-delete && |
| 119 | test_write_lines u t "" | git add -i && |
| 120 | git ls-files to-delete >output && |
| 121 | test_must_be_empty output |
| 122 | ' |
| 123 | |
| 124 | test_expect_success 'setup expected' ' |
| 125 | cat >expected <<-\EOF |
| 126 | index 180b47c..b6f2c08 100644 |
| 127 | --- a/file |
| 128 | +++ b/file |
| 129 | @@ -1 +1,2 @@ |
| 130 | baseline |
| 131 | +content |
| 132 | EOF |
| 133 | ' |
| 134 | |
| 135 | test_expect_success 'diff works (commit)' ' |
| 136 | test_write_lines d 1 | git add -i >output && |
| 137 | sed -ne "/^index/,/content/p" <output >diff && |
| 138 | diff_cmp expected diff |
| 139 | ' |
| 140 | test_expect_success 'revert works (commit)' ' |
| 141 | git add file && |
| 142 | test_write_lines r 1 | git add -i && |
| 143 | git add -i </dev/null >output && |
| 144 | test_grep "unchanged *+3/-0 file" output |
| 145 | ' |
| 146 | |
| 147 | test_expect_success 'reject multi-key input' ' |
| 148 | saved=$(git hash-object -w file) && |
| 149 | test_when_finished "git cat-file blob $saved >file" && |
| 150 | echo an extra line >>file && |
| 151 | test_write_lines aa | git add -p >actual && |
| 152 | test_grep "is expected, got ${SQ}aa${SQ}" actual |
| 153 | ' |
| 154 | |
| 155 | test_expect_success 'setup expected' ' |
| 156 | cat >expected <<-\EOF |
| 157 | EOF |
| 158 | ' |
| 159 | |
| 160 | test_expect_success 'dummy edit works' ' |
| 161 | test_set_editor : && |
| 162 | test_write_lines e a | git add -p && |
| 163 | git diff > diff && |
| 164 | diff_cmp expected diff |
| 165 | ' |
| 166 | |
| 167 | test_expect_success 'setup patch' ' |
| 168 | cat >patch <<-\EOF |
| 169 | @@ -1,1 +1,4 @@ |
| 170 | this |
| 171 | +patch |
| 172 | -does not |
| 173 | apply |
| 174 | EOF |
| 175 | ' |
| 176 | |
| 177 | test_expect_success 'setup fake editor' ' |
| 178 | write_script "fake_editor.sh" <<-\EOF && |
| 179 | mv -f "$1" oldpatch && |
| 180 | mv -f patch "$1" |
| 181 | EOF |
| 182 | test_set_editor "$(pwd)/fake_editor.sh" |
| 183 | ' |
| 184 | |
| 185 | test_expect_success 'bad edit rejected' ' |
| 186 | git reset && |
| 187 | test_write_lines e n d | git add -p >output && |
| 188 | test_grep "hunk does not apply" output |
| 189 | ' |
| 190 | |
| 191 | test_expect_success 'setup patch' ' |
| 192 | cat >patch <<-\EOF |
| 193 | this patch |
| 194 | is garbage |
| 195 | EOF |
| 196 | ' |
| 197 | |
| 198 | test_expect_success 'garbage edit rejected' ' |
| 199 | git reset && |
| 200 | test_write_lines e n d | git add -p >output && |
| 201 | test_grep "hunk does not apply" output |
| 202 | ' |
| 203 | |
| 204 | test_expect_success 'setup patch' ' |
| 205 | cat >patch <<-\EOF |
| 206 | @@ -1,0 +1,0 @@ |
| 207 | baseline |
| 208 | +content |
| 209 | +newcontent |
| 210 | +lines |
| 211 | EOF |
| 212 | ' |
| 213 | |
| 214 | test_expect_success 'setup expected' ' |
| 215 | cat >expected <<-\EOF |
| 216 | diff --git a/file b/file |
| 217 | index b5dd6c9..f910ae9 100644 |
| 218 | --- a/file |
| 219 | +++ b/file |
| 220 | @@ -1,4 +1,4 @@ |
| 221 | baseline |
| 222 | content |
| 223 | -newcontent |
| 224 | +more |
| 225 | lines |
| 226 | EOF |
| 227 | ' |
| 228 | |
| 229 | test_expect_success 'real edit works' ' |
| 230 | test_write_lines e n d | git add -p && |
| 231 | git diff >output && |
| 232 | diff_cmp expected output |
| 233 | ' |
| 234 | |
| 235 | test_expect_success 'setup file' ' |
| 236 | test_write_lines a "" b "" c >file && |
| 237 | git add file && |
| 238 | test_write_lines a "" d "" c >file |
| 239 | ' |
| 240 | |
| 241 | test_expect_success 'setup patch' ' |
| 242 | NULL="" && |
| 243 | cat >patch <<-EOF |
| 244 | @@ -1,4 +1,4 @@ |
| 245 | a |
| 246 | $NULL |
| 247 | -b |
| 248 | +f |
| 249 | $SP |
| 250 | c |
| 251 | EOF |
| 252 | ' |
| 253 | |
| 254 | test_expect_success 'setup expected' ' |
| 255 | cat >expected <<-EOF |
| 256 | diff --git a/file b/file |
| 257 | index b5dd6c9..f910ae9 100644 |
| 258 | --- a/file |
| 259 | +++ b/file |
| 260 | @@ -1,5 +1,5 @@ |
| 261 | a |
| 262 | $SP |
| 263 | -f |
| 264 | +d |
| 265 | $SP |
| 266 | c |
| 267 | EOF |
| 268 | ' |
| 269 | |
| 270 | test_expect_success 'edit can strip spaces from empty context lines' ' |
| 271 | test_write_lines e n q | git add -p 2>error && |
| 272 | test_must_be_empty error && |
| 273 | git diff >output && |
| 274 | diff_cmp expected output |
| 275 | ' |
| 276 | |
| 277 | test_expect_success 'skip files similarly as commit -a' ' |
| 278 | git reset && |
| 279 | echo file >.gitignore && |
| 280 | echo changed >file && |
| 281 | echo y | git add -p file && |
| 282 | git diff >output && |
| 283 | git reset && |
| 284 | git commit -am commit && |
| 285 | git diff >expected && |
| 286 | diff_cmp expected output && |
| 287 | git reset --hard HEAD^ |
| 288 | ' |
| 289 | rm -f .gitignore |
| 290 | |
| 291 | test_expect_success FILEMODE 'patch does not affect mode' ' |
| 292 | git reset --hard && |
| 293 | echo content >>file && |
| 294 | chmod +x file && |
| 295 | printf "n\\ny\\n" | git add -p && |
| 296 | git show :file | grep content && |
| 297 | git diff file | grep "new mode" |
| 298 | ' |
| 299 | |
| 300 | test_expect_success FILEMODE 'stage mode but not hunk' ' |
| 301 | git reset --hard && |
| 302 | echo content >>file && |
| 303 | chmod +x file && |
| 304 | printf "y\\nn\\n" | git add -p && |
| 305 | git diff --cached file | grep "new mode" && |
| 306 | git diff file | grep "+content" |
| 307 | ' |
| 308 | |
| 309 | |
| 310 | test_expect_success FILEMODE 'stage mode and hunk' ' |
| 311 | git reset --hard && |
| 312 | echo content >>file && |
| 313 | chmod +x file && |
| 314 | printf "y\\ny\\n" | git add -p && |
| 315 | git diff --cached file >out && |
| 316 | test_grep "new mode" out && |
| 317 | test_grep "+content" out && |
| 318 | git diff file >out && |
| 319 | test_must_be_empty out |
| 320 | ' |
| 321 | |
| 322 | # end of tests disabled when filemode is not usable |
| 323 | |
| 324 | test_expect_success 'different prompts for mode change/deleted' ' |
| 325 | git reset --hard && |
| 326 | >file && |
| 327 | >deleted && |
| 328 | git add --chmod=+x file deleted && |
| 329 | echo changed >file && |
| 330 | rm deleted && |
| 331 | test_write_lines n n n | |
| 332 | git -c core.filemode=true add -p >actual && |
| 333 | sed -n "s/^\(([0-9/]*) Stage .*?\).*/\1/p" actual >actual.filtered && |
| 334 | cat >expect <<-\EOF && |
| 335 | (1/1) Stage deletion [y,n,q,a,d,p,P,?]? |
| 336 | (1/2) Stage mode change [y,n,q,a,d,k,K,j,J,g,/,p,P,?]? |
| 337 | (2/2) Stage this hunk [y,n,q,a,d,K,J,g,/,e,p,P,?]? |
| 338 | EOF |
| 339 | test_cmp expect actual.filtered |
| 340 | ' |
| 341 | |
| 342 | test_expect_success 'correct message when there is nothing to do' ' |
| 343 | git reset --hard && |
| 344 | git add -p >out && |
| 345 | test_grep "No changes" out && |
| 346 | printf "\\0123" >binary && |
| 347 | git add binary && |
| 348 | printf "\\0abc" >binary && |
| 349 | git add -p >out && |
| 350 | test_grep "Only binary files changed" out |
| 351 | ' |
| 352 | |
| 353 | test_expect_success 'setup again' ' |
| 354 | git reset --hard && |
| 355 | test_chmod +x file && |
| 356 | echo content >>file && |
| 357 | test_write_lines A B C D>file2 && |
| 358 | git add file2 |
| 359 | ' |
| 360 | |
| 361 | # Write the patch file with a new line at the top and bottom |
| 362 | test_expect_success 'setup patch' ' |
| 363 | cat >patch <<-\EOF |
| 364 | index 180b47c..b6f2c08 100644 |
| 365 | --- a/file |
| 366 | +++ b/file |
| 367 | @@ -1,2 +1,4 @@ |
| 368 | +firstline |
| 369 | baseline |
| 370 | content |
| 371 | +lastline |
| 372 | \ No newline at end of file |
| 373 | diff --git a/file2 b/file2 |
| 374 | index 8422d40..35b930a 100644 |
| 375 | --- a/file2 |
| 376 | +++ b/file2 |
| 377 | @@ -1,4 +1,5 @@ |
| 378 | -A |
| 379 | +Z |
| 380 | B |
| 381 | +Y |
| 382 | C |
| 383 | -D |
| 384 | +X |
| 385 | EOF |
| 386 | ' |
| 387 | |
| 388 | # Expected output, diff is similar to the patch but w/ diff at the top |
| 389 | test_expect_success 'setup expected' ' |
| 390 | echo diff --git a/file b/file >expected && |
| 391 | sed -e "/^index 180b47c/s/ 100644/ 100755/" \ |
| 392 | -e /1,5/s//1,4/ \ |
| 393 | -e /Y/d patch >>expected && |
| 394 | cat >expected-output <<-\EOF |
| 395 | --- a/file |
| 396 | +++ b/file |
| 397 | @@ -1,2 +1,4 @@ |
| 398 | +firstline |
| 399 | baseline |
| 400 | content |
| 401 | +lastline |
| 402 | \ No newline at end of file |
| 403 | @@ -1,2 +1,3 @@ |
| 404 | +firstline |
| 405 | baseline |
| 406 | content |
| 407 | @@ -1,2 +2,3 @@ |
| 408 | baseline |
| 409 | content |
| 410 | +lastline |
| 411 | \ No newline at end of file |
| 412 | --- a/file2 |
| 413 | +++ b/file2 |
| 414 | @@ -1,4 +1,5 @@ |
| 415 | -A |
| 416 | +Z |
| 417 | B |
| 418 | +Y |
| 419 | C |
| 420 | -D |
| 421 | +X |
| 422 | @@ -1,2 +1,2 @@ |
| 423 | -A |
| 424 | +Z |
| 425 | B |
| 426 | @@ -2,2 +2,3 @@ |
| 427 | B |
| 428 | +Y |
| 429 | C |
| 430 | @@ -3,2 +4,2 @@ |
| 431 | C |
| 432 | -D |
| 433 | +X |
| 434 | EOF |
| 435 | ' |
| 436 | |
| 437 | # Test splitting the first patch, then adding both |
| 438 | test_expect_success 'add first line works' ' |
| 439 | git commit -am "clear local changes" && |
| 440 | git apply patch && |
| 441 | test_write_lines s y y s y n y | git add -p 2>error >raw-output && |
| 442 | sed -n -e "s/^([1-9]\/[1-9]) Stage this hunk[^@]*\(@@ .*\)/\1/" \ |
| 443 | -e "/^[-+@ \\\\]"/p raw-output >output && |
| 444 | test_must_be_empty error && |
| 445 | git diff --cached >diff && |
| 446 | diff_cmp expected diff && |
| 447 | test_cmp expected-output output |
| 448 | ' |
| 449 | |
| 450 | test_expect_success 'setup expected' ' |
| 451 | cat >expected <<-\EOF |
| 452 | diff --git a/non-empty b/non-empty |
| 453 | deleted file mode 100644 |
| 454 | index d95f3ad..0000000 |
| 455 | --- a/non-empty |
| 456 | +++ /dev/null |
| 457 | @@ -1 +0,0 @@ |
| 458 | -content |
| 459 | EOF |
| 460 | ' |
| 461 | |
| 462 | test_expect_success 'deleting a non-empty file' ' |
| 463 | git reset --hard && |
| 464 | echo content >non-empty && |
| 465 | git add non-empty && |
| 466 | git commit -m non-empty && |
| 467 | rm non-empty && |
| 468 | echo y | git add -p non-empty && |
| 469 | git diff --cached >diff && |
| 470 | diff_cmp expected diff |
| 471 | ' |
| 472 | |
| 473 | test_expect_success 'setup expected' ' |
| 474 | cat >expected <<-\EOF |
| 475 | diff --git a/empty b/empty |
| 476 | deleted file mode 100644 |
| 477 | index e69de29..0000000 |
| 478 | EOF |
| 479 | ' |
| 480 | |
| 481 | test_expect_success 'deleting an empty file' ' |
| 482 | git reset --hard && |
| 483 | > empty && |
| 484 | git add empty && |
| 485 | git commit -m empty && |
| 486 | rm empty && |
| 487 | echo y | git add -p empty && |
| 488 | git diff --cached >diff && |
| 489 | diff_cmp expected diff |
| 490 | ' |
| 491 | |
| 492 | test_expect_success 'adding an empty file' ' |
| 493 | git init added && |
| 494 | ( |
| 495 | cd added && |
| 496 | test_commit initial && |
| 497 | >empty && |
| 498 | git add empty && |
| 499 | test_tick && |
| 500 | git commit -m empty && |
| 501 | git tag added-file && |
| 502 | git reset --hard HEAD^ && |
| 503 | test_path_is_missing empty && |
| 504 | |
| 505 | echo y | git checkout -p added-file -- >actual && |
| 506 | test_path_is_file empty && |
| 507 | test_grep "Apply addition to index and worktree" actual |
| 508 | ) |
| 509 | ' |
| 510 | |
| 511 | test_expect_success 'split hunk setup' ' |
| 512 | git reset --hard && |
| 513 | test_write_lines 10 20 30 40 50 60 >test && |
| 514 | git add test && |
| 515 | test_tick && |
| 516 | git commit -m test && |
| 517 | |
| 518 | test_write_lines 10 15 20 21 22 23 24 30 40 50 60 >test |
| 519 | ' |
| 520 | |
| 521 | test_expect_success 'goto hunk 1 with "g 1"' ' |
| 522 | test_when_finished "git reset" && |
| 523 | tr _ " " >expect <<-EOF && |
| 524 | (2/2) Stage this hunk [y,n,q,a,d,K,J,g,/,e,p,P,?]? + 1: -1,2 +1,3 +15 |
| 525 | _ 2: -2,4 +3,8 +21 |
| 526 | go to which hunk? @@ -1,2 +1,3 @@ |
| 527 | _10 |
| 528 | +15 |
| 529 | _20 |
| 530 | (1/2) Stage this hunk (was: y) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_ |
| 531 | EOF |
| 532 | test_write_lines s y g 1 | git add -p >actual && |
| 533 | tail -n 7 <actual >actual.trimmed && |
| 534 | test_cmp expect actual.trimmed |
| 535 | ' |
| 536 | |
| 537 | test_expect_success 'goto hunk 1 with "g1"' ' |
| 538 | test_when_finished "git reset" && |
| 539 | tr _ " " >expect <<-EOF && |
| 540 | _10 |
| 541 | +15 |
| 542 | _20 |
| 543 | (1/2) Stage this hunk (was: y) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_ |
| 544 | EOF |
| 545 | test_write_lines s y g1 | git add -p >actual && |
| 546 | tail -n 4 <actual >actual.trimmed && |
| 547 | test_cmp expect actual.trimmed |
| 548 | ' |
| 549 | |
| 550 | test_expect_success 'navigate to hunk via regex /pattern' ' |
| 551 | test_when_finished "git reset" && |
| 552 | tr _ " " >expect <<-EOF && |
| 553 | (2/2) Stage this hunk [y,n,q,a,d,K,J,g,/,e,p,P,?]? @@ -1,2 +1,3 @@ |
| 554 | _10 |
| 555 | +15 |
| 556 | _20 |
| 557 | (1/2) Stage this hunk (was: y) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_ |
| 558 | EOF |
| 559 | test_write_lines s y /1,2 | git add -p >actual && |
| 560 | tail -n 5 <actual >actual.trimmed && |
| 561 | test_cmp expect actual.trimmed |
| 562 | ' |
| 563 | |
| 564 | test_expect_success 'navigate to hunk via regex / pattern' ' |
| 565 | test_when_finished "git reset" && |
| 566 | tr _ " " >expect <<-EOF && |
| 567 | _10 |
| 568 | +15 |
| 569 | _20 |
| 570 | (1/2) Stage this hunk (was: y) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_ |
| 571 | EOF |
| 572 | test_write_lines s y / 1,2 | git add -p >actual && |
| 573 | tail -n 4 <actual >actual.trimmed && |
| 574 | test_cmp expect actual.trimmed |
| 575 | ' |
| 576 | |
| 577 | test_expect_success 'print again the hunk' ' |
| 578 | test_when_finished "git reset" && |
| 579 | tr _ " " >expect <<-EOF && |
| 580 | +15 |
| 581 | 20 |
| 582 | (1/2) Stage this hunk (was: y) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? @@ -1,2 +1,3 @@ |
| 583 | 10 |
| 584 | +15 |
| 585 | 20 |
| 586 | (1/2) Stage this hunk (was: y) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_ |
| 587 | EOF |
| 588 | test_write_lines s y g 1 p | git add -p >actual && |
| 589 | tail -n 7 <actual >actual.trimmed && |
| 590 | test_cmp expect actual.trimmed |
| 591 | ' |
| 592 | |
| 593 | test_expect_success TTY 'print again the hunk (PAGER)' ' |
| 594 | test_when_finished "git reset" && |
| 595 | cat >expect <<-EOF && |
| 596 | <GREEN>+<RESET><GREEN>15<RESET> |
| 597 | 20<RESET> |
| 598 | <BOLD;BLUE>(1/2) Stage this hunk (was: y) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? <RESET>PAGER <CYAN>@@ -1,2 +1,3 @@<RESET> |
| 599 | PAGER 10<RESET> |
| 600 | PAGER <GREEN>+<RESET><GREEN>15<RESET> |
| 601 | PAGER 20<RESET> |
| 602 | <BOLD;BLUE>(1/2) Stage this hunk (was: y) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? <RESET> |
| 603 | EOF |
| 604 | test_write_lines s y g 1 P | |
| 605 | ( |
| 606 | GIT_PAGER="sed s/^/PAGER\ /" && |
| 607 | export GIT_PAGER && |
| 608 | test_terminal git add -p >actual |
| 609 | ) && |
| 610 | tail -n 7 <actual | test_decode_color >actual.trimmed && |
| 611 | test_cmp expect actual.trimmed |
| 612 | ' |
| 613 | |
| 614 | test_expect_success TTY 'P handles SIGPIPE when writing to pager' ' |
| 615 | test_when_finished "rm -f huge_file; git reset" && |
| 616 | printf "\n%2500000s" Y >huge_file && |
| 617 | git add -N huge_file && |
| 618 | test_write_lines P q | ( |
| 619 | GIT_PAGER="head -n 1" && |
| 620 | export GIT_PAGER && |
| 621 | test_terminal git add -p |
| 622 | ) |
| 623 | ' |
| 624 | |
| 625 | test_expect_success 'split hunk "add -p (edit)"' ' |
| 626 | # Split, say Edit and do nothing. Then: |
| 627 | # |
| 628 | # 1. Broken version results in a patch that does not apply and |
| 629 | # only takes [y/n] (edit again) so the first q is discarded |
| 630 | # and then n attempts to discard the edit. Repeat q enough |
| 631 | # times to get out. |
| 632 | # |
| 633 | # 2. Correct version applies the (not)edited version, and asks |
| 634 | # about the next hunk, against which we say q and program |
| 635 | # exits. |
| 636 | printf "%s\n" s e q n q q | |
| 637 | EDITOR=: git add -p && |
| 638 | git diff >actual && |
| 639 | test_grep ! "^+15" actual |
| 640 | ' |
| 641 | |
| 642 | test_expect_success 'split hunk "add -p (no, yes, edit)"' ' |
| 643 | test_write_lines 5 10 20 21 30 31 40 50 60 >test && |
| 644 | git reset && |
| 645 | # test sequence is s(plit), n(o), y(es), e(dit) |
| 646 | # q n q q is there to make sure we exit at the end. |
| 647 | printf "%s\n" s n y e q n q q | |
| 648 | EDITOR=: git add -p 2>error && |
| 649 | test_must_be_empty error && |
| 650 | git diff >actual && |
| 651 | test_grep ! "^+31" actual |
| 652 | ' |
| 653 | |
| 654 | test_expect_success 'split hunk with incomplete line at end' ' |
| 655 | git reset --hard && |
| 656 | printf "missing LF" >>test && |
| 657 | git add test && |
| 658 | test_write_lines before 10 20 30 40 50 60 70 >test && |
| 659 | git grep --cached missing && |
| 660 | test_write_lines s n y q | git add -p && |
| 661 | test_must_fail git grep --cached missing && |
| 662 | git grep before && |
| 663 | test_must_fail git grep --cached before |
| 664 | ' |
| 665 | |
| 666 | test_expect_success 'edit, adding lines to the first hunk' ' |
| 667 | test_write_lines 10 11 20 30 40 50 51 60 >test && |
| 668 | git reset && |
| 669 | tr _ " " >patch <<-EOF && |
| 670 | @@ -1,5 +1,6 @@ |
| 671 | _10 |
| 672 | +11 |
| 673 | +12 |
| 674 | _20 |
| 675 | +21 |
| 676 | +22 |
| 677 | _30 |
| 678 | EOF |
| 679 | # test sequence is s(plit), e(dit), n(o) |
| 680 | # q n q q is there to make sure we exit at the end. |
| 681 | printf "%s\n" s e n q n q q | |
| 682 | EDITOR=./fake_editor.sh git add -p 2>error && |
| 683 | test_must_be_empty error && |
| 684 | git diff --cached >actual && |
| 685 | test_grep "^+22" actual |
| 686 | ' |
| 687 | |
| 688 | test_expect_success 'patch mode ignores unmerged entries' ' |
| 689 | git reset --hard && |
| 690 | test_commit conflict && |
| 691 | test_commit non-conflict && |
| 692 | git checkout -b side && |
| 693 | test_commit side conflict.t && |
| 694 | git checkout main && |
| 695 | test_commit main conflict.t && |
| 696 | test_must_fail git merge side && |
| 697 | echo changed >non-conflict.t && |
| 698 | echo y | git add -p >output && |
| 699 | test_grep ! a/conflict.t output && |
| 700 | cat >expected <<-\EOF && |
| 701 | * Unmerged path conflict.t |
| 702 | diff --git a/non-conflict.t b/non-conflict.t |
| 703 | index f766221..5ea2ed4 100644 |
| 704 | --- a/non-conflict.t |
| 705 | +++ b/non-conflict.t |
| 706 | @@ -1 +1 @@ |
| 707 | -non-conflict |
| 708 | +changed |
| 709 | EOF |
| 710 | git diff --cached >diff && |
| 711 | diff_cmp expected diff |
| 712 | ' |
| 713 | |
| 714 | test_expect_success 'index is refreshed after applying patch' ' |
| 715 | git reset --hard && |
| 716 | echo content >test && |
| 717 | printf y | git add -p && |
| 718 | git diff-files --exit-code |
| 719 | ' |
| 720 | |
| 721 | test_expect_success 'diffs can be colorized' ' |
| 722 | git reset --hard && |
| 723 | |
| 724 | echo content >test && |
| 725 | printf y >y && |
| 726 | force_color git add -p >output 2>&1 <y && |
| 727 | git diff-files --exit-code && |
| 728 | |
| 729 | # We do not want to depend on the exact coloring scheme |
| 730 | # git uses for diffs, so just check that we saw some kind of color. |
| 731 | test_grep "$(printf "\\033")" output |
| 732 | ' |
| 733 | |
| 734 | test_expect_success 'colors can be overridden' ' |
| 735 | git reset --hard && |
| 736 | test_when_finished "git rm -f color-test" && |
| 737 | test_write_lines context old more-context >color-test && |
| 738 | git add color-test && |
| 739 | test_write_lines context new more-context another-one >color-test && |
| 740 | |
| 741 | echo trigger an error message >input && |
| 742 | force_color git \ |
| 743 | -c color.interactive.error=blue \ |
| 744 | add -i 2>err.raw <input && |
| 745 | test_decode_color <err.raw >err && |
| 746 | test_grep "<BLUE>Huh (trigger)?<RESET>" err && |
| 747 | |
| 748 | test_write_lines help quit >input && |
| 749 | force_color git \ |
| 750 | -c color.interactive.header=red \ |
| 751 | -c color.interactive.help=green \ |
| 752 | -c color.interactive.prompt=yellow \ |
| 753 | add -i >actual.raw <input && |
| 754 | test_decode_color <actual.raw >actual && |
| 755 | cat >expect <<-\EOF && |
| 756 | <RED> staged unstaged path<RESET> |
| 757 | 1: +3/-0 +2/-1 color-test |
| 758 | |
| 759 | <RED>*** Commands ***<RESET> |
| 760 | 1: <YELLOW>s<RESET>tatus 2: <YELLOW>u<RESET>pdate 3: <YELLOW>r<RESET>evert 4: <YELLOW>a<RESET>dd untracked |
| 761 | 5: <YELLOW>p<RESET>atch 6: <YELLOW>d<RESET>iff 7: <YELLOW>q<RESET>uit 8: <YELLOW>h<RESET>elp |
| 762 | <YELLOW>What now<RESET>> <GREEN>status - show paths with changes<RESET> |
| 763 | <GREEN>update - add working tree state to the staged set of changes<RESET> |
| 764 | <GREEN>revert - revert staged set of changes back to the HEAD version<RESET> |
| 765 | <GREEN>patch - pick hunks and update selectively<RESET> |
| 766 | <GREEN>diff - view diff between HEAD and index<RESET> |
| 767 | <GREEN>add untracked - add contents of untracked files to the staged set of changes<RESET> |
| 768 | <RED>*** Commands ***<RESET> |
| 769 | 1: <YELLOW>s<RESET>tatus 2: <YELLOW>u<RESET>pdate 3: <YELLOW>r<RESET>evert 4: <YELLOW>a<RESET>dd untracked |
| 770 | 5: <YELLOW>p<RESET>atch 6: <YELLOW>d<RESET>iff 7: <YELLOW>q<RESET>uit 8: <YELLOW>h<RESET>elp |
| 771 | <YELLOW>What now<RESET>> Bye. |
| 772 | EOF |
| 773 | test_cmp expect actual && |
| 774 | |
| 775 | : exercise recolor_hunk by editing and then look at the hunk again && |
| 776 | test_write_lines s e K q >input && |
| 777 | force_color git \ |
| 778 | -c color.interactive.prompt=yellow \ |
| 779 | -c color.diff.meta=italic \ |
| 780 | -c color.diff.frag=magenta \ |
| 781 | -c color.diff.context=cyan \ |
| 782 | -c color.diff.old=bold \ |
| 783 | -c color.diff.new=blue \ |
| 784 | -c core.editor=touch \ |
| 785 | add -p >actual.raw <input && |
| 786 | test_decode_color <actual.raw >actual.decoded && |
| 787 | sed "s/index [0-9a-f]*\\.\\.[0-9a-f]* 100644/<INDEX-LINE>/" <actual.decoded >actual && |
| 788 | cat >expect <<-\EOF && |
| 789 | <ITALIC>diff --git a/color-test b/color-test<RESET> |
| 790 | <ITALIC><INDEX-LINE><RESET> |
| 791 | <ITALIC>--- a/color-test<RESET> |
| 792 | <ITALIC>+++ b/color-test<RESET> |
| 793 | <MAGENTA>@@ -1,3 +1,4 @@<RESET> |
| 794 | <CYAN> context<RESET> |
| 795 | <BOLD>-old<RESET> |
| 796 | <BLUE>+<RESET><BLUE>new<RESET> |
| 797 | <CYAN> more-context<RESET> |
| 798 | <BLUE>+<RESET><BLUE>another-one<RESET> |
| 799 | <YELLOW>(1/1) Stage this hunk [y,n,q,a,d,s,e,p,P,?]? <RESET><BOLD>Split into 2 hunks.<RESET> |
| 800 | <MAGENTA>@@ -1,3 +1,3 @@<RESET> |
| 801 | <CYAN> context<RESET> |
| 802 | <BOLD>-old<RESET> |
| 803 | <BLUE>+<RESET><BLUE>new<RESET> |
| 804 | <CYAN> more-context<RESET> |
| 805 | <YELLOW>(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? <RESET><MAGENTA>@@ -3 +3,2 @@<RESET> |
| 806 | <CYAN> more-context<RESET> |
| 807 | <BLUE>+<RESET><BLUE>another-one<RESET> |
| 808 | <YELLOW>(2/2) Stage this hunk [y,n,q,a,d,K,J,g,/,e,p,P,?]? <RESET><MAGENTA>@@ -1,3 +1,3 @@<RESET> |
| 809 | <CYAN> context<RESET> |
| 810 | <BOLD>-old<RESET> |
| 811 | <BLUE>+new<RESET> |
| 812 | <CYAN> more-context<RESET> |
| 813 | <YELLOW>(1/2) Stage this hunk (was: y) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? <RESET> |
| 814 | EOF |
| 815 | test_cmp expect actual |
| 816 | ' |
| 817 | |
| 818 | test_expect_success 'brackets appear without color' ' |
| 819 | git reset --hard && |
| 820 | test_when_finished "git rm -f bracket-test" && |
| 821 | test_write_lines context old more-context >bracket-test && |
| 822 | git add bracket-test && |
| 823 | test_write_lines context new more-context another-one >bracket-test && |
| 824 | |
| 825 | test_write_lines quit >input && |
| 826 | git add -i >actual <input && |
| 827 | |
| 828 | sed "s/^|//" >expect <<-\EOF && |
| 829 | | staged unstaged path |
| 830 | | 1: +3/-0 +2/-1 bracket-test |
| 831 | | |
| 832 | |*** Commands *** |
| 833 | | 1: [s]tatus 2: [u]pdate 3: [r]evert 4: [a]dd untracked |
| 834 | | 5: [p]atch 6: [d]iff 7: [q]uit 8: [h]elp |
| 835 | |What now> Bye. |
| 836 | EOF |
| 837 | |
| 838 | test_cmp expect actual |
| 839 | ' |
| 840 | |
| 841 | test_expect_success 'colors can be skipped with color.ui=false' ' |
| 842 | git reset --hard && |
| 843 | test_when_finished "git rm -f color-test" && |
| 844 | test_write_lines context old more-context >color-test && |
| 845 | git add color-test && |
| 846 | test_write_lines context new more-context another-one >color-test && |
| 847 | |
| 848 | test_write_lines help quit >input && |
| 849 | force_color git \ |
| 850 | -c color.ui=false \ |
| 851 | add -i >actual.raw <input && |
| 852 | test_decode_color <actual.raw >actual && |
| 853 | test_cmp actual.raw actual |
| 854 | ' |
| 855 | |
| 856 | test_expect_success 'colorized diffs respect diff.wsErrorHighlight' ' |
| 857 | git reset --hard && |
| 858 | |
| 859 | echo "old " >test && |
| 860 | git add test && |
| 861 | echo "new " >test && |
| 862 | |
| 863 | printf y >y && |
| 864 | force_color git -c diff.wsErrorHighlight=all add -p >output.raw 2>&1 <y && |
| 865 | test_decode_color <output.raw >output && |
| 866 | test_grep "old<" output |
| 867 | ' |
| 868 | |
| 869 | test_expect_success 'diff color respects color.diff' ' |
| 870 | git reset --hard && |
| 871 | |
| 872 | echo old >test && |
| 873 | git add test && |
| 874 | echo new >test && |
| 875 | |
| 876 | printf n >n && |
| 877 | force_color git \ |
| 878 | -c color.interactive=auto \ |
| 879 | -c color.interactive.prompt=blue \ |
| 880 | -c color.diff=false \ |
| 881 | -c color.diff.old=red \ |
| 882 | add -p >output.raw 2>&1 <n && |
| 883 | test_decode_color <output.raw >output && |
| 884 | test_grep "BLUE.*Stage this hunk" output && |
| 885 | test_grep ! "RED" output |
| 886 | ' |
| 887 | |
| 888 | test_expect_success 're-coloring diff without color.interactive' ' |
| 889 | git reset --hard && |
| 890 | |
| 891 | test_write_lines 1 2 3 >test && |
| 892 | git add test && |
| 893 | test_write_lines one 2 three >test && |
| 894 | |
| 895 | test_write_lines s n n | |
| 896 | force_color git \ |
| 897 | -c color.interactive=false \ |
| 898 | -c color.interactive.prompt=blue \ |
| 899 | -c color.diff=true \ |
| 900 | -c color.diff.frag="bold magenta" \ |
| 901 | add -p >output.raw 2>&1 && |
| 902 | test_decode_color <output.raw >output && |
| 903 | test_grep "<BOLD;MAGENTA>@@" output && |
| 904 | test_grep ! "BLUE" output |
| 905 | ' |
| 906 | |
| 907 | test_expect_success 'diffFilter filters diff' ' |
| 908 | git reset --hard && |
| 909 | |
| 910 | echo content >test && |
| 911 | test_config interactive.diffFilter "sed s/^/foo:/" && |
| 912 | printf y >y && |
| 913 | force_color git add -p >output 2>&1 <y && |
| 914 | |
| 915 | # avoid depending on the exact coloring or content of the prompts, |
| 916 | # and just make sure we saw our diff prefixed |
| 917 | test_grep foo:.*content output |
| 918 | ' |
| 919 | |
| 920 | test_expect_success 'detect bogus diffFilter output' ' |
| 921 | git reset --hard && |
| 922 | |
| 923 | echo content >test && |
| 924 | test_config interactive.diffFilter "sed 6d" && |
| 925 | printf y >y && |
| 926 | force_color test_must_fail git add -p <y >output 2>&1 && |
| 927 | test_grep "mismatched output" output |
| 928 | ' |
| 929 | |
| 930 | test_expect_success 'handle iffy colored hunk headers' ' |
| 931 | git reset --hard && |
| 932 | |
| 933 | echo content >test && |
| 934 | printf n >n && |
| 935 | force_color git -c interactive.diffFilter="sed s/.*@@.*/XX/" \ |
| 936 | add -p >output 2>&1 <n && |
| 937 | test_grep "^XX$" output |
| 938 | ' |
| 939 | |
| 940 | test_expect_success 'handle very large filtered diff' ' |
| 941 | git reset --hard && |
| 942 | # The specific number here is not important, but it must |
| 943 | # be large enough that the output of "git diff --color" |
| 944 | # fills up the pipe buffer. 10,000 results in ~200k of |
| 945 | # colored output. |
| 946 | test_seq 10000 >test && |
| 947 | test_config interactive.diffFilter cat && |
| 948 | printf y >y && |
| 949 | force_color git add -p >output 2>&1 <y && |
| 950 | git diff-files --exit-code -- test |
| 951 | ' |
| 952 | |
| 953 | test_expect_success 'diff.algorithm is passed to `git diff-files`' ' |
| 954 | git reset --hard && |
| 955 | |
| 956 | >file && |
| 957 | git add file && |
| 958 | echo changed >file && |
| 959 | test_must_fail git -c diff.algorithm=bogus add -p 2>err && |
| 960 | test_grep "error: option diff-algorithm accepts " err |
| 961 | ' |
| 962 | |
| 963 | test_expect_success 'patch-mode via -i prompts for files' ' |
| 964 | git reset --hard && |
| 965 | |
| 966 | echo one >file && |
| 967 | echo two >test && |
| 968 | git add -i <<-\EOF && |
| 969 | patch |
| 970 | test |
| 971 | |
| 972 | y |
| 973 | quit |
| 974 | EOF |
| 975 | |
| 976 | echo test >expect && |
| 977 | git diff --cached --name-only >actual && |
| 978 | diff_cmp expect actual |
| 979 | ' |
| 980 | |
| 981 | test_expect_success 'add -p handles globs' ' |
| 982 | git reset --hard && |
| 983 | |
| 984 | mkdir -p subdir && |
| 985 | echo base >one.c && |
| 986 | echo base >subdir/two.c && |
| 987 | git add "*.c" && |
| 988 | git commit -m base && |
| 989 | |
| 990 | echo change >one.c && |
| 991 | echo change >subdir/two.c && |
| 992 | git add -p "*.c" <<-\EOF && |
| 993 | y |
| 994 | y |
| 995 | EOF |
| 996 | |
| 997 | cat >expect <<-\EOF && |
| 998 | one.c |
| 999 | subdir/two.c |
| 1000 | EOF |
| 1001 | git diff --cached --name-only >actual && |
| 1002 | test_cmp expect actual |
| 1003 | ' |
| 1004 | |
| 1005 | test_expect_success 'add -p handles relative paths' ' |
| 1006 | git reset --hard && |
| 1007 | |
| 1008 | echo base >relpath.c && |
| 1009 | git add "*.c" && |
| 1010 | git commit -m relpath && |
| 1011 | |
| 1012 | echo change >relpath.c && |
| 1013 | mkdir -p subdir && |
| 1014 | git -C subdir add -p .. 2>error <<-\EOF && |
| 1015 | y |
| 1016 | EOF |
| 1017 | |
| 1018 | test_must_be_empty error && |
| 1019 | |
| 1020 | cat >expect <<-\EOF && |
| 1021 | relpath.c |
| 1022 | EOF |
| 1023 | git diff --cached --name-only >actual && |
| 1024 | test_cmp expect actual |
| 1025 | ' |
| 1026 | |
| 1027 | test_expect_success 'add -p does not expand argument lists' ' |
| 1028 | git reset --hard && |
| 1029 | |
| 1030 | echo content >not-changed && |
| 1031 | git add not-changed && |
| 1032 | git commit -m "add not-changed file" && |
| 1033 | |
| 1034 | echo change >file && |
| 1035 | GIT_TRACE=$(pwd)/trace.out git add -p . <<-\EOF && |
| 1036 | y |
| 1037 | EOF |
| 1038 | |
| 1039 | # we know that "file" must be mentioned since we actually |
| 1040 | # update it, but we want to be sure that our "." pathspec |
| 1041 | # was not expanded into the argument list of any command. |
| 1042 | # So look only for "not-changed". |
| 1043 | test_grep ! -E "^trace: (built-in|exec|run_command): .*not-changed" trace.out |
| 1044 | ' |
| 1045 | |
| 1046 | test_expect_success 'hunk-editing handles custom comment char' ' |
| 1047 | git reset --hard && |
| 1048 | echo change >>file && |
| 1049 | test_config core.commentChar "\$" && |
| 1050 | echo e | GIT_EDITOR=true git add -p && |
| 1051 | git diff --exit-code |
| 1052 | ' |
| 1053 | |
| 1054 | test_expect_success 'add -p works even with color.ui=always' ' |
| 1055 | git reset --hard && |
| 1056 | echo change >>file && |
| 1057 | test_config color.ui always && |
| 1058 | echo y | git add -p && |
| 1059 | echo file >expect && |
| 1060 | git diff --cached --name-only >actual && |
| 1061 | test_cmp expect actual |
| 1062 | ' |
| 1063 | |
| 1064 | test_expect_success 'setup different kinds of dirty submodules' ' |
| 1065 | test_create_repo for-submodules && |
| 1066 | ( |
| 1067 | cd for-submodules && |
| 1068 | test_commit initial && |
| 1069 | test_create_repo dirty-head && |
| 1070 | ( |
| 1071 | cd dirty-head && |
| 1072 | test_commit initial |
| 1073 | ) && |
| 1074 | cp -R dirty-head dirty-otherwise && |
| 1075 | cp -R dirty-head dirty-both-ways && |
| 1076 | git add dirty-head && |
| 1077 | git add dirty-otherwise dirty-both-ways && |
| 1078 | git commit -m initial && |
| 1079 | |
| 1080 | cd dirty-head && |
| 1081 | test_commit updated && |
| 1082 | cd ../dirty-both-ways && |
| 1083 | test_commit updated && |
| 1084 | echo dirty >>initial && |
| 1085 | : >untracked && |
| 1086 | cd ../dirty-otherwise && |
| 1087 | echo dirty >>initial && |
| 1088 | : >untracked |
| 1089 | ) && |
| 1090 | git -C for-submodules diff-files --name-only >actual && |
| 1091 | cat >expected <<-\EOF && |
| 1092 | dirty-both-ways |
| 1093 | dirty-head |
| 1094 | EOF |
| 1095 | test_cmp expected actual && |
| 1096 | git -C for-submodules diff-files --name-only --ignore-submodules=none >actual && |
| 1097 | cat >expected <<-\EOF && |
| 1098 | dirty-both-ways |
| 1099 | dirty-head |
| 1100 | dirty-otherwise |
| 1101 | EOF |
| 1102 | test_cmp expected actual && |
| 1103 | git -C for-submodules diff-files --name-only --ignore-submodules=dirty >actual && |
| 1104 | cat >expected <<-\EOF && |
| 1105 | dirty-both-ways |
| 1106 | dirty-head |
| 1107 | EOF |
| 1108 | test_cmp expected actual |
| 1109 | ' |
| 1110 | |
| 1111 | test_expect_success 'status ignores dirty submodules (except HEAD)' ' |
| 1112 | git -C for-submodules add -i </dev/null >output && |
| 1113 | test_grep dirty-head output && |
| 1114 | test_grep dirty-both-ways output && |
| 1115 | test_grep ! dirty-otherwise output |
| 1116 | ' |
| 1117 | |
| 1118 | test_expect_success 'handle submodules' ' |
| 1119 | echo 123 >>for-submodules/dirty-otherwise/initial.t && |
| 1120 | |
| 1121 | force_color git -C for-submodules add -p dirty-otherwise >output 2>&1 && |
| 1122 | test_grep "No changes" output && |
| 1123 | |
| 1124 | force_color git -C for-submodules add -p dirty-head >output 2>&1 <y && |
| 1125 | git -C for-submodules ls-files --stage dirty-head >actual && |
| 1126 | rev="$(git -C for-submodules/dirty-head rev-parse HEAD)" && |
| 1127 | test_grep "$rev" actual |
| 1128 | ' |
| 1129 | |
| 1130 | test_expect_success 'set up pathological context' ' |
| 1131 | git reset --hard && |
| 1132 | test_write_lines a a a a a a a a a a a >a && |
| 1133 | git add a && |
| 1134 | git commit -m a && |
| 1135 | test_write_lines c b a a a a a a a b a a a a >a && |
| 1136 | test_write_lines a a a a a a a b a a a a >expected-1 && |
| 1137 | test_write_lines b a a a a a a a b a a a a >expected-2 && |
| 1138 | # check editing can cope with missing header and deleted context lines |
| 1139 | # as well as changes to other lines |
| 1140 | test_write_lines +b " a" >patch |
| 1141 | ' |
| 1142 | |
| 1143 | test_expect_success 'add -p works with pathological context lines' ' |
| 1144 | git reset && |
| 1145 | printf "%s\n" n y | |
| 1146 | git add -p && |
| 1147 | git cat-file blob :a >actual && |
| 1148 | test_cmp expected-1 actual |
| 1149 | ' |
| 1150 | |
| 1151 | test_expect_success 'add -p patch editing works with pathological context lines' ' |
| 1152 | git reset && |
| 1153 | # n q q below is in case edit fails |
| 1154 | printf "%s\n" e y n q q | |
| 1155 | git add -p && |
| 1156 | git cat-file blob :a >actual && |
| 1157 | test_cmp expected-2 actual |
| 1158 | ' |
| 1159 | |
| 1160 | test_expect_success 'checkout -p works with pathological context lines' ' |
| 1161 | test_write_lines a a a a a a >a && |
| 1162 | git add a && |
| 1163 | test_write_lines a b a b a b a b a b a >a && |
| 1164 | test_write_lines s n n y q | git checkout -p && |
| 1165 | test_write_lines a b a b a a b a b a >expect && |
| 1166 | test_cmp expect a |
| 1167 | ' |
| 1168 | |
| 1169 | # This should be called from a subshell as it sets a temporary editor |
| 1170 | setup_new_file() { |
| 1171 | write_script new-file-editor.sh <<-\EOF && |
| 1172 | sed /^#/d "$1" >patch && |
| 1173 | sed /^+c/d patch >"$1" |
| 1174 | EOF |
| 1175 | test_set_editor "$(pwd)/new-file-editor.sh" && |
| 1176 | test_write_lines a b c d e f >new-file && |
| 1177 | test_write_lines a b d e f >new-file-expect && |
| 1178 | test_write_lines "@@ -0,0 +1,6 @@" +a +b +c +d +e +f >patch-expect |
| 1179 | } |
| 1180 | |
| 1181 | test_expect_success 'add -N followed by add -p patch editing' ' |
| 1182 | git reset --hard && |
| 1183 | ( |
| 1184 | setup_new_file && |
| 1185 | git add -N new-file && |
| 1186 | test_write_lines e n q | git add -p && |
| 1187 | git cat-file blob :new-file >actual && |
| 1188 | test_cmp new-file-expect actual && |
| 1189 | test_cmp patch-expect patch |
| 1190 | ) |
| 1191 | ' |
| 1192 | |
| 1193 | test_expect_success 'checkout -p patch editing of added file' ' |
| 1194 | git reset --hard && |
| 1195 | ( |
| 1196 | setup_new_file && |
| 1197 | git add new-file && |
| 1198 | git commit -m "add new file" && |
| 1199 | git rm new-file && |
| 1200 | git commit -m "remove new file" && |
| 1201 | test_write_lines e n q | git checkout -p HEAD^ && |
| 1202 | test_cmp new-file-expect new-file && |
| 1203 | test_cmp patch-expect patch |
| 1204 | ) |
| 1205 | ' |
| 1206 | |
| 1207 | test_expect_success 'show help from add--helper' ' |
| 1208 | git reset --hard && |
| 1209 | cat >expect <<-EOF && |
| 1210 | |
| 1211 | <BOLD>*** Commands ***<RESET> |
| 1212 | 1: <BOLD;BLUE>s<RESET>tatus 2: <BOLD;BLUE>u<RESET>pdate 3: <BOLD;BLUE>r<RESET>evert 4: <BOLD;BLUE>a<RESET>dd untracked |
| 1213 | 5: <BOLD;BLUE>p<RESET>atch 6: <BOLD;BLUE>d<RESET>iff 7: <BOLD;BLUE>q<RESET>uit 8: <BOLD;BLUE>h<RESET>elp |
| 1214 | <BOLD;BLUE>What now<RESET>> <BOLD;RED>status - show paths with changes<RESET> |
| 1215 | <BOLD;RED>update - add working tree state to the staged set of changes<RESET> |
| 1216 | <BOLD;RED>revert - revert staged set of changes back to the HEAD version<RESET> |
| 1217 | <BOLD;RED>patch - pick hunks and update selectively<RESET> |
| 1218 | <BOLD;RED>diff - view diff between HEAD and index<RESET> |
| 1219 | <BOLD;RED>add untracked - add contents of untracked files to the staged set of changes<RESET> |
| 1220 | <BOLD>*** Commands ***<RESET> |
| 1221 | 1: <BOLD;BLUE>s<RESET>tatus 2: <BOLD;BLUE>u<RESET>pdate 3: <BOLD;BLUE>r<RESET>evert 4: <BOLD;BLUE>a<RESET>dd untracked |
| 1222 | 5: <BOLD;BLUE>p<RESET>atch 6: <BOLD;BLUE>d<RESET>iff 7: <BOLD;BLUE>q<RESET>uit 8: <BOLD;BLUE>h<RESET>elp |
| 1223 | <BOLD;BLUE>What now<RESET>>$SP |
| 1224 | Bye. |
| 1225 | EOF |
| 1226 | test_write_lines h | force_color git add -i >actual.colored && |
| 1227 | test_decode_color <actual.colored >actual && |
| 1228 | test_cmp expect actual |
| 1229 | ' |
| 1230 | |
| 1231 | test_expect_success 'reset -p with unmerged files' ' |
| 1232 | test_when_finished "git checkout --force main" && |
| 1233 | test_commit one conflict && |
| 1234 | git checkout -B side HEAD^ && |
| 1235 | test_commit two conflict && |
| 1236 | test_must_fail git merge one && |
| 1237 | |
| 1238 | # this is a noop with only an unmerged entry |
| 1239 | git reset -p && |
| 1240 | |
| 1241 | # add files that sort before and after unmerged entry |
| 1242 | echo a >a && |
| 1243 | echo z >z && |
| 1244 | git add a z && |
| 1245 | |
| 1246 | # confirm that we can reset those files |
| 1247 | printf "%s\n" y y | git reset -p && |
| 1248 | git diff-index --cached --diff-filter=u HEAD >staged && |
| 1249 | test_must_be_empty staged |
| 1250 | ' |
| 1251 | |
| 1252 | test_expect_success 'hunk splitting works with diff.suppressBlankEmpty' ' |
| 1253 | test_config diff.suppressBlankEmpty true && |
| 1254 | write_script fake-editor.sh <<-\EOF && |
| 1255 | tr F G <"$1" >"$1.tmp" && |
| 1256 | mv "$1.tmp" "$1" |
| 1257 | EOF |
| 1258 | |
| 1259 | test_write_lines a b "" c d "" e f "" >file && |
| 1260 | git add file && |
| 1261 | test_write_lines A b "" c D "" e F "" >file && |
| 1262 | ( |
| 1263 | test_set_editor "$(pwd)/fake-editor.sh" && |
| 1264 | test_write_lines s n y e q | git add -p file |
| 1265 | ) && |
| 1266 | git cat-file blob :file >actual && |
| 1267 | test_write_lines a b "" c D "" e G "" >expect && |
| 1268 | test_cmp expect actual |
| 1269 | ' |
| 1270 | |
| 1271 | test_expect_success 'add -p respects diff.context' ' |
| 1272 | test_write_lines a b c d e f g h i j k l m >file && |
| 1273 | git add file && |
| 1274 | test_write_lines a b c d e f G h i j k l m >file && |
| 1275 | echo y | git -c diff.context=5 add -p >actual && |
| 1276 | test_grep "@@ -2,11 +2,11 @@" actual |
| 1277 | ' |
| 1278 | |
| 1279 | test_expect_success 'add -p respects diff.interHunkContext' ' |
| 1280 | test_write_lines a b c d e f g h i j k l m n o p q r s >file && |
| 1281 | git add file && |
| 1282 | test_write_lines a b c d E f g i i j k l m N o p q r s >file && |
| 1283 | echo y | git -c diff.interhunkcontext=2 add -p >actual && |
| 1284 | test_grep "@@ -2,16 +2,16 @@" actual |
| 1285 | ' |
| 1286 | |
| 1287 | test_expect_success 'add -p rejects negative diff.context' ' |
| 1288 | test_config diff.context -1 && |
| 1289 | test_must_fail git add -p 2>output && |
| 1290 | test_grep "diff.context cannot be negative" output |
| 1291 | ' |
| 1292 | |
| 1293 | for cmd in add checkout restore 'commit -m file' |
| 1294 | do |
| 1295 | test_expect_success "${cmd%% *} accepts -U and --inter-hunk-context" ' |
| 1296 | test_write_lines a b c d e f g h i j k l m n o p q r s t u v >file && |
| 1297 | git add file && |
| 1298 | test_write_lines a b c d e F g h i j k l m n o p Q r s t u v >file && |
| 1299 | echo y | git -c diff.context=5 -c diff.interhunkcontext=1 \ |
| 1300 | $cmd -p -U 4 --inter-hunk-context 2 >actual && |
| 1301 | test_grep "@@ -2,20 +2,20 @@" actual |
| 1302 | ' |
| 1303 | done |
| 1304 | |
| 1305 | test_expect_success 'reset accepts -U and --inter-hunk-context' ' |
| 1306 | test_write_lines a b c d e f g h i j k l m n o p q r s t u v >file && |
| 1307 | git commit -m file file && |
| 1308 | test_write_lines a b c d e F g h i j k l m n o p Q r s t u v >file && |
| 1309 | git add file && |
| 1310 | echo y | git -c diff.context=5 -c diff.interhunkcontext=1 \ |
| 1311 | reset -p -U 4 --inter-hunk-context 2 >actual && |
| 1312 | test_grep "@@ -2,20 +2,20 @@" actual |
| 1313 | ' |
| 1314 | |
| 1315 | test_expect_success 'stash accepts -U and --inter-hunk-context' ' |
| 1316 | test_write_lines a b c d e F g h i j k l m n o p Q r s t u v >file && |
| 1317 | git commit -m file file && |
| 1318 | test_write_lines a b c d e f g h i j k l m n o p q r s t u v >file && |
| 1319 | echo y | git -c diff.context=5 -c diff.interhunkcontext=1 \ |
| 1320 | stash -p -U 4 --inter-hunk-context 2 >actual && |
| 1321 | test_grep "@@ -2,20 +2,20 @@" actual |
| 1322 | ' |
| 1323 | |
| 1324 | test_expect_success 'set up base for -p color tests' ' |
| 1325 | echo commit >file && |
| 1326 | git commit -am "commit state" && |
| 1327 | git tag patch-base |
| 1328 | ' |
| 1329 | |
| 1330 | for cmd in add checkout commit reset restore "stash save" "stash push" |
| 1331 | do |
| 1332 | test_expect_success "$cmd rejects invalid context options" ' |
| 1333 | test_must_fail git $cmd -p -U -3 2>actual && |
| 1334 | cat actual | echo && |
| 1335 | test_grep -e ".--unified. cannot be negative" actual && |
| 1336 | |
| 1337 | test_must_fail git $cmd -p --inter-hunk-context -3 2>actual && |
| 1338 | test_grep -e ".--inter-hunk-context. cannot be negative" actual && |
| 1339 | |
| 1340 | test_must_fail git $cmd -U 7 2>actual && |
| 1341 | test_grep -E ".--unified. requires .(--interactive/)?--patch." actual && |
| 1342 | |
| 1343 | test_must_fail git $cmd --inter-hunk-context 2 2>actual && |
| 1344 | test_grep -E ".--inter-hunk-context. requires .(--interactive/)?--patch." actual |
| 1345 | ' |
| 1346 | |
| 1347 | test_expect_success "$cmd falls back to color.ui" ' |
| 1348 | git reset --hard patch-base && |
| 1349 | echo working-tree >file && |
| 1350 | test_write_lines y | |
| 1351 | force_color git -c color.ui=false $cmd -p >output.raw 2>&1 && |
| 1352 | test_decode_color <output.raw >output && |
| 1353 | test_cmp output.raw output |
| 1354 | ' |
| 1355 | done |
| 1356 | |
| 1357 | test_expect_success 'splitting previous hunk marks split hunks as undecided' ' |
| 1358 | test_write_lines a " " b c d e f g h i j k >file && |
| 1359 | git add file && |
| 1360 | test_write_lines x " " b y d e f g h i j x >file && |
| 1361 | test_write_lines n K s n y q | git add -p file && |
| 1362 | git cat-file blob :file >actual && |
| 1363 | test_write_lines a " " b y d e f g h i j k >expect && |
| 1364 | test_cmp expect actual |
| 1365 | ' |
| 1366 | |
| 1367 | test_expect_success 'splitting edited hunk' ' |
| 1368 | # Before the first hunk is edited it can be split into two |
| 1369 | # hunks, after editing it can be split into three hunks. |
| 1370 | |
| 1371 | write_script fake-editor.sh <<-\EOF && |
| 1372 | sed "s/^ c/-c/" "$1" >"$1.tmp" && |
| 1373 | mv "$1.tmp" "$1" |
| 1374 | EOF |
| 1375 | |
| 1376 | test_write_lines a b c d e f g h i j k l m n >file && |
| 1377 | git add file && |
| 1378 | test_write_lines A b c d E f g h i j k l M n >file && |
| 1379 | ( |
| 1380 | test_set_editor "$(pwd)/fake-editor.sh" && |
| 1381 | test_write_lines e K s j y n y q | git add -p file |
| 1382 | ) && |
| 1383 | git cat-file blob :file >actual && |
| 1384 | test_write_lines a b d e f g h i j k l M n >expect && |
| 1385 | test_cmp expect actual |
| 1386 | ' |
| 1387 | |
| 1388 | test_expect_success 'options J, K roll over' ' |
| 1389 | test_write_lines a b c d e f g h i >file && |
| 1390 | git add file && |
| 1391 | test_write_lines X b c d e f g h X >file && |
| 1392 | test_write_lines J J K q | git add -p >out && |
| 1393 | test_write_lines 1 2 1 2 >expect && |
| 1394 | sed -n -e "s-/.*--" -e "s/^(//p" <out >actual && |
| 1395 | test_cmp expect actual |
| 1396 | ' |
| 1397 | |
| 1398 | test_expect_success 'options y, n, a, d, j, k, e roll over to next undecided (1)' ' |
| 1399 | test_write_lines a b c d e f g h i j k l m n o p q >file && |
| 1400 | git add file && |
| 1401 | test_write_lines X b c d e f g h X j k l m n o p X >file && |
| 1402 | test_set_editor : && |
| 1403 | test_write_lines g3 y g3 n g3 a g3 d g3 j g3 e k q | git add -p >out && |
| 1404 | test_write_lines 1 3 1 3 1 3 1 3 1 3 1 3 1 2 >expect && |
| 1405 | sed -n -e "s-/.*--" -e "s/^(//p" <out >actual && |
| 1406 | test_cmp expect actual |
| 1407 | ' |
| 1408 | |
| 1409 | test_expect_success 'options y, n, a, d, j, k, e roll over to next undecided (2)' ' |
| 1410 | test_write_lines a b c d e f g h i j k l m n o p q >file && |
| 1411 | git add file && |
| 1412 | test_write_lines X b c d e f g h X j k l m n o p X >file && |
| 1413 | test_set_editor : && |
| 1414 | test_write_lines y g3 y g3 n g3 a g3 d g3 j g3 e g1 k q | git add -p >out && |
| 1415 | test_write_lines 1 2 3 2 3 2 3 2 3 2 3 2 3 2 1 2 >expect && |
| 1416 | sed -n -e "s-/.*--" -e "s/^(//p" <out >actual && |
| 1417 | test_cmp expect actual |
| 1418 | ' |
| 1419 | |
| 1420 | test_expect_success 'invalid option s is rejected' ' |
| 1421 | test_write_lines a b c d e f g h i j k >file && |
| 1422 | git add file && |
| 1423 | test_write_lines X b X d e f g h i j X >file && |
| 1424 | test_write_lines j s q | git add -p >out && |
| 1425 | sed -ne "s/ @@.*//" -e "s/ \$//" -e "/^(/p" <out >actual && |
| 1426 | cat >expect <<-EOF && |
| 1427 | (1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,s,e,p,P,?]? |
| 1428 | (2/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? Sorry, cannot split this hunk |
| 1429 | (2/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? |
| 1430 | EOF |
| 1431 | test_cmp expect actual |
| 1432 | ' |
| 1433 | |
| 1434 | test_expect_success 'EOF quits' ' |
| 1435 | echo a >file && |
| 1436 | echo a >file2 && |
| 1437 | git add file file2 && |
| 1438 | echo X >file && |
| 1439 | echo X >file2 && |
| 1440 | git add -p </dev/null >out && |
| 1441 | test_grep file out && |
| 1442 | test_grep ! file2 out |
| 1443 | ' |
| 1444 | for cmd in add checkout reset "stash save" "stash push" |
| 1445 | do |
| 1446 | test_expect_success "$cmd rejects invalid --no-auto-advance options" ' |
| 1447 | test_must_fail git $cmd --no-auto-advance 2>actual && |
| 1448 | test_grep -E "requires .*--(interactive|patch)" actual |
| 1449 | ' |
| 1450 | done |
| 1451 | |
| 1452 | test_expect_success 'manual advance (">") moves to next file with --no-auto-advance' ' |
| 1453 | git reset --hard && |
| 1454 | echo line1 >first-file && |
| 1455 | echo line2 >second-file && |
| 1456 | git add -A && |
| 1457 | git commit -m initial >/dev/null 2>&1 && |
| 1458 | echo change_first >>first-file && |
| 1459 | echo change_second >>second-file && |
| 1460 | |
| 1461 | printf ">\nq\n" | git add -p --no-auto-advance >output.test 2>&1 && |
| 1462 | test_grep -E "(a|b)/second-file" output.test |
| 1463 | ' |
| 1464 | |
| 1465 | test_expect_success 'select n on a hunk, go to another file, come back and change to y stages' ' |
| 1466 | git reset --hard && |
| 1467 | echo one >f1 && |
| 1468 | echo one >f2 && |
| 1469 | git add -A && |
| 1470 | git commit -m initial >/dev/null 2>&1 && |
| 1471 | echo change1 >>f1 && |
| 1472 | echo change2 >>f2 && |
| 1473 | |
| 1474 | printf "n\n>\n<\ny\nq\n" | git add -p --no-auto-advance >output.staged 2>&1 && |
| 1475 | git diff --cached --name-only >staged && |
| 1476 | test_grep -E "(a/f1)" output.staged |
| 1477 | ' |
| 1478 | |
| 1479 | test_expect_success 'select y on a hunk, go to another file, come back and change to n does not stage' ' |
| 1480 | git reset --hard && |
| 1481 | echo one >f1 && |
| 1482 | echo one >f2 && |
| 1483 | git add -A && |
| 1484 | git commit -m initial >/dev/null 2>&1 && |
| 1485 | echo change1 >>f1 && |
| 1486 | echo change2 >>f2 && |
| 1487 | |
| 1488 | printf "y\n>\n<\nn\nq\n" | git add -p --no-auto-advance >output.unstaged 2>&1 && |
| 1489 | git diff --cached --name-only >staged && |
| 1490 | test_must_be_empty staged |
| 1491 | ' |
| 1492 | |
| 1493 | test_expect_success 'deciding all hunks in a file does not auto advance' ' |
| 1494 | git reset --hard && |
| 1495 | echo line >stay && |
| 1496 | echo line >other && |
| 1497 | git add -A && |
| 1498 | git commit -m initial >/dev/null 2>&1 && |
| 1499 | echo change >>stay && |
| 1500 | echo change >>other && |
| 1501 | test_write_lines y | git add -p --no-auto-advance >raw-output 2>&1 && |
| 1502 | test_grep "(1/1) Stage this hunk (was: y)" raw-output && |
| 1503 | test_grep ! "diff --git a/stay b/stay" raw-output |
| 1504 | ' |
| 1505 | test_expect_success 'HUNKS SUMMARY does not show in help text when there are undecided hunks' ' |
| 1506 | git reset --hard && |
| 1507 | test_write_lines 1 2 3 4 5 6 7 8 9 >f && |
| 1508 | git add f && |
| 1509 | git commit -m initial >/dev/null 2>&1 && |
| 1510 | test_write_lines 1 X 3 4 Y 6 7 Z 9 >f && |
| 1511 | test_write_lines s y n | git add -p --no-auto-advance >raw-nostat 2>&1 && |
| 1512 | test_grep ! "HUNKS SUMMARY - Hunks: " raw-nostat |
| 1513 | ' |
| 1514 | |
| 1515 | test_expect_success 'help text shows HUNK SUMMARY when all hunks have been decided' ' |
| 1516 | git reset --hard && |
| 1517 | test_write_lines 1 2 3 4 5 6 7 8 9 >f2 && |
| 1518 | git add f2 && |
| 1519 | git commit -m initial >/dev/null 2>&1 && |
| 1520 | test_write_lines 1 X 3 4 Y 6 7 Z 9 >f2 && |
| 1521 | printf "s\ny\nn\ny\n?\n" | git add -p --no-auto-advance >raw-stat 2>&1 && |
| 1522 | test_grep "HUNKS SUMMARY - Hunks: 3, USE: 2, SKIP: 1" raw-stat |
| 1523 | ' |
| 1524 | |
| 1525 | test_expect_success 'selective staging across multiple files with --no-advance' ' |
| 1526 | git reset --hard && |
| 1527 | test_write_lines 1 2 3 4 5 6 7 8 9 >a.file && |
| 1528 | test_write_lines 1 2 3 4 5 6 7 8 9 >b.file && |
| 1529 | test_write_lines 1 2 3 4 5 6 7 8 9 >c.file && |
| 1530 | git add -A && |
| 1531 | git commit -m initial >/dev/null 2>&1 && |
| 1532 | test_write_lines 1 A2 3 4 A5 6 7 8 9 >a.file && |
| 1533 | test_write_lines 1 2 B3 4 5 6 7 B8 9 >b.file && |
| 1534 | test_write_lines C1 2 3 4 5 C6 7 8 9 >c.file && |
| 1535 | printf "s\ny\nn\n>\ns\nn\ny\n>\ns\ny\ny\nq\n" | git add -p --no-auto-advance >output.index 2>&1 && |
| 1536 | git diff --cached >staged.diff && |
| 1537 | test_grep "+A2" staged.diff && |
| 1538 | test_grep ! "+A5" staged.diff && |
| 1539 | test_grep "+B8" staged.diff && |
| 1540 | test_grep ! "+B3" staged.diff && |
| 1541 | test_grep "+C1" staged.diff && |
| 1542 | test_grep "+C6" staged.diff |
| 1543 | ' |
| 1544 | |
| 1545 | test_done |