Git fork
at reftables-rust 540 lines 15 kB view raw
1#!/bin/sh 2 3test_description='git filter-branch' 4GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main 5export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME 6 7. ./test-lib.sh 8. "$TEST_DIRECTORY/lib-gpg.sh" 9 10test_expect_success 'setup' ' 11 test_commit A && 12 GIT_COMMITTER_DATE="@0 +0000" GIT_AUTHOR_DATE="@0 +0000" && 13 test_commit --notick B && 14 git checkout -b branch B && 15 test_commit D && 16 mkdir dir && 17 test_commit dir/D && 18 test_commit E && 19 git checkout main && 20 test_commit C && 21 git checkout branch && 22 git merge C && 23 git tag F && 24 test_commit G && 25 test_commit H 26' 27# * (HEAD, branch) H 28# * G 29# * Merge commit 'C' into branch 30# |\ 31# | * (main) C 32# * | E 33# * | dir/D 34# * | D 35# |/ 36# * B 37# * A 38 39 40H=$(git rev-parse H) 41 42test_expect_success 'rewrite identically' ' 43 git filter-branch branch 44' 45test_expect_success 'result is really identical' ' 46 test $H = $(git rev-parse HEAD) 47' 48 49test_expect_success 'rewrite bare repository identically' ' 50 (git config core.bare true && cd .git && 51 git filter-branch branch > filter-output 2>&1 && 52 ! grep fatal filter-output) 53' 54git config core.bare false 55test_expect_success 'result is really identical' ' 56 test $H = $(git rev-parse HEAD) 57' 58 59TRASHDIR=$(pwd) 60test_expect_success 'correct GIT_DIR while using -d' ' 61 mkdir drepo && 62 ( cd drepo && 63 git init && 64 test_commit drepo && 65 git filter-branch -d "$TRASHDIR/dfoo" \ 66 --index-filter "cp \"$TRASHDIR\"/dfoo/backup-refs \"$TRASHDIR\"" \ 67 ) && 68 grep drepo "$TRASHDIR/backup-refs" 69' 70 71test_expect_success 'tree-filter works with -d' ' 72 git init drepo-tree && 73 ( 74 cd drepo-tree && 75 test_commit one && 76 git filter-branch -d "$TRASHDIR/dfoo" \ 77 --tree-filter "echo changed >one.t" && 78 echo changed >expect && 79 git cat-file blob HEAD:one.t >actual && 80 test_cmp expect actual && 81 test_cmp one.t actual 82 ) 83' 84 85test_expect_success 'Fail if commit filter fails' ' 86 test_must_fail git filter-branch -f --commit-filter "exit 1" HEAD 87' 88 89test_expect_success 'rewrite, renaming a specific file' ' 90 git filter-branch -f --tree-filter "mv D.t doh || :" HEAD 91' 92 93test_expect_success 'test that the file was renamed' ' 94 test D = "$(git show HEAD:doh --)" && 95 ! test -f D.t && 96 test -f doh && 97 test D = "$(cat doh)" 98' 99 100test_expect_success 'rewrite, renaming a specific directory' ' 101 git filter-branch -f --tree-filter "mv dir diroh || :" HEAD 102' 103 104test_expect_success 'test that the directory was renamed' ' 105 test dir/D = "$(git show HEAD:diroh/D.t --)" && 106 ! test -d dir && 107 test -d diroh && 108 ! test -d diroh/dir && 109 test -f diroh/D.t && 110 test dir/D = "$(cat diroh/D.t)" 111' 112 113V=$(git rev-parse HEAD) 114 115test_expect_success 'populate --state-branch' ' 116 git filter-branch --state-branch state -f --tree-filter "touch file || :" HEAD 117' 118 119W=$(git rev-parse HEAD) 120 121test_expect_success 'using --state-branch to skip already rewritten commits' ' 122 test_when_finished git reset --hard $V && 123 git reset --hard $V && 124 git filter-branch --state-branch state -f --tree-filter "touch file || :" HEAD && 125 test_cmp_rev $W HEAD 126' 127 128git tag oldD HEAD~4 129test_expect_success 'rewrite one branch, keeping a side branch' ' 130 git branch modD oldD && 131 git filter-branch -f --tree-filter "mv B.t boh || :" D..modD 132' 133 134test_expect_success 'common ancestor is still common (unchanged)' ' 135 test "$(git merge-base modD D)" = "$(git rev-parse B)" 136' 137 138test_expect_success 'filter subdirectory only' ' 139 mkdir subdir && 140 touch subdir/new && 141 git add subdir/new && 142 test_tick && 143 git commit -m "subdir" && 144 echo H > A.t && 145 test_tick && 146 git commit -m "not subdir" A.t && 147 echo A > subdir/new && 148 test_tick && 149 git commit -m "again subdir" subdir/new && 150 git rm A.t && 151 test_tick && 152 git commit -m "again not subdir" && 153 git branch sub && 154 git branch sub-earlier HEAD~2 && 155 git filter-branch -f --subdirectory-filter subdir \ 156 refs/heads/sub refs/heads/sub-earlier 157' 158 159test_expect_success 'subdirectory filter result looks okay' ' 160 test 2 = $(git rev-list sub | wc -l) && 161 git show sub:new && 162 test_must_fail git show sub:subdir && 163 git show sub-earlier:new && 164 test_must_fail git show sub-earlier:subdir 165' 166 167test_expect_success 'more setup' ' 168 git checkout main && 169 mkdir subdir && 170 echo A > subdir/new && 171 git add subdir/new && 172 test_tick && 173 git commit -m "subdir on main" subdir/new && 174 git rm A.t && 175 test_tick && 176 git commit -m "again subdir on main" && 177 git merge branch 178' 179 180test_expect_success 'use index-filter to move into a subdirectory' ' 181 git branch directorymoved && 182 git filter-branch -f --index-filter \ 183 "git ls-files -s | sed \"s- -&newsubdir/-\" | 184 GIT_INDEX_FILE=\$GIT_INDEX_FILE.new \ 185 git update-index --index-info && 186 mv \"\$GIT_INDEX_FILE.new\" \"\$GIT_INDEX_FILE\"" directorymoved && 187 git diff --exit-code HEAD directorymoved:newsubdir 188' 189 190test_expect_success 'stops when msg filter fails' ' 191 old=$(git rev-parse HEAD) && 192 test_must_fail git filter-branch -f --msg-filter false HEAD && 193 test $old = $(git rev-parse HEAD) && 194 rm -rf .git-rewrite 195' 196 197test_expect_success 'author information is preserved' ' 198 : > i && 199 git add i && 200 test_tick && 201 GIT_AUTHOR_NAME="B V Uips" git commit -m bvuips && 202 git branch preserved-author && 203 (sane_unset GIT_AUTHOR_NAME && 204 git filter-branch -f --msg-filter "cat; \ 205 test \$GIT_COMMIT != $(git rev-parse main) || \ 206 echo Hallo" \ 207 preserved-author) && 208 git rev-list --author="B V Uips" preserved-author >actual && 209 test_line_count = 1 actual 210' 211 212test_expect_success "remove a certain author's commits" ' 213 echo i > i && 214 test_tick && 215 git commit -m i i && 216 git branch removed-author && 217 git filter-branch -f --commit-filter "\ 218 if [ \"\$GIT_AUTHOR_NAME\" = \"B V Uips\" ];\ 219 then\ 220 skip_commit \"\$@\"; 221 else\ 222 git commit-tree \"\$@\";\ 223 fi" removed-author && 224 cnt1=$(git rev-list main | wc -l) && 225 cnt2=$(git rev-list removed-author | wc -l) && 226 test $cnt1 -eq $(($cnt2 + 1)) && 227 git rev-list --author="B V Uips" removed-author >actual && 228 test_line_count = 0 actual 229' 230 231test_expect_success 'barf on invalid name' ' 232 test_must_fail git filter-branch -f main xy-problem && 233 test_must_fail git filter-branch -f HEAD^ 234' 235 236test_expect_success '"map" works in commit filter' ' 237 git filter-branch -f --commit-filter "\ 238 parent=\$(git rev-parse \$GIT_COMMIT^) && 239 mapped=\$(map \$parent) && 240 actual=\$(echo \"\$@\" | sed \"s/^.*-p //\") && 241 test \$mapped = \$actual && 242 git commit-tree \"\$@\";" main~2..main && 243 git rev-parse --verify main 244' 245 246test_expect_success 'Name needing quotes' ' 247 248 git checkout -b rerere A && 249 mkdir foo && 250 name="れれれ" && 251 >foo/$name && 252 git add foo && 253 git commit -m "Adding a file" && 254 git filter-branch --tree-filter "rm -fr foo" && 255 test_must_fail git ls-files --error-unmatch "foo/$name" && 256 test $(git rev-parse --verify rerere) != $(git rev-parse --verify A) 257 258' 259 260test_expect_success 'Subdirectory filter with disappearing trees' ' 261 git reset --hard && 262 git checkout main && 263 264 mkdir foo && 265 touch foo/bar && 266 git add foo && 267 test_tick && 268 git commit -m "Adding foo" && 269 270 git rm -r foo && 271 test_tick && 272 git commit -m "Removing foo" && 273 274 mkdir foo && 275 touch foo/bar && 276 git add foo && 277 test_tick && 278 git commit -m "Re-adding foo" && 279 280 git filter-branch -f --subdirectory-filter foo && 281 git rev-list main >actual && 282 test_line_count = 3 actual 283' 284 285test_expect_success 'Tag name filtering retains tag message' ' 286 git tag -m atag T && 287 git cat-file tag T > expect && 288 git filter-branch -f --tag-name-filter cat && 289 git cat-file tag T > actual && 290 test_cmp expect actual 291' 292 293faux_gpg_tag='object XXXXXX 294type commit 295tag S 296tagger T A Gger <tagger@example.com> 1206026339 -0500 297 298This is a faux gpg signed tag. 299-----BEGIN PGP SIGNATURE----- 300Version: FauxGPG v0.0.0 (FAUX/Linux) 301 302gdsfoewhxu/6l06f1kxyxhKdZkrcbaiOMtkJUA9ITAc1mlamh0ooasxkH1XwMbYQ 303acmwXaWET20H0GeAGP+7vow= 304=agpO 305-----END PGP SIGNATURE----- 306' 307test_expect_success 'Tag name filtering strips gpg signature' ' 308 sha1=$(git rev-parse HEAD) && 309 sha1t=$(echo "$faux_gpg_tag" | sed -e s/XXXXXX/$sha1/ | git mktag) && 310 git update-ref "refs/tags/S" "$sha1t" && 311 echo "$faux_gpg_tag" | sed -e s/XXXXXX/$sha1/ | head -n 6 > expect && 312 git filter-branch -f --tag-name-filter cat && 313 git cat-file tag S > actual && 314 test_cmp expect actual 315' 316 317test_expect_success GPG 'Filtering retains message of gpg signed commit' ' 318 mkdir gpg && 319 touch gpg/foo && 320 git add gpg && 321 test_tick && 322 git commit -S -m "Adding gpg" && 323 324 git log -1 --format="%s" > expect && 325 git filter-branch -f --msg-filter "cat" && 326 git log -1 --format="%s" > actual && 327 test_cmp expect actual 328' 329 330test_expect_success 'Tag name filtering allows slashes in tag names' ' 331 git tag -m tag-with-slash X/1 && 332 git cat-file tag X/1 | sed -e s,X/1,X/2, > expect && 333 git filter-branch -f --tag-name-filter "echo X/2" && 334 git cat-file tag X/2 > actual && 335 test_cmp expect actual 336' 337test_expect_success 'setup --prune-empty comparisons' ' 338 git checkout --orphan main-no-a && 339 git rm -rf . && 340 unset test_tick && 341 test_tick && 342 GIT_COMMITTER_DATE="@0 +0000" GIT_AUTHOR_DATE="@0 +0000" && 343 test_commit --notick B B.t B Bx && 344 git checkout -b branch-no-a Bx && 345 test_commit D D.t D Dx && 346 mkdir dir && 347 test_commit dir/D dir/D.t dir/D dir/Dx && 348 test_commit E E.t E Ex && 349 git checkout main-no-a && 350 test_commit C C.t C Cx && 351 git checkout branch-no-a && 352 git merge Cx -m "Merge tag '\''C'\'' into branch" && 353 git tag Fx && 354 test_commit G G.t G Gx && 355 test_commit H H.t H Hx && 356 git checkout branch 357' 358 359test_expect_success 'Prune empty commits' ' 360 git rev-list HEAD > expect && 361 test_commit to_remove && 362 git filter-branch -f --index-filter "git update-index --remove to_remove.t" --prune-empty HEAD && 363 git rev-list HEAD > actual && 364 test_cmp expect actual 365' 366 367test_expect_success 'prune empty collapsed merges' ' 368 test_config merge.ff false && 369 git rev-list HEAD >expect && 370 test_commit to_remove_2 && 371 git reset --hard HEAD^ && 372 test_merge non-ff to_remove_2 && 373 git filter-branch -f --index-filter "git update-index --remove to_remove_2.t" --prune-empty HEAD && 374 git rev-list HEAD >actual && 375 test_cmp expect actual 376' 377 378test_expect_success 'prune empty works even without index/tree filters' ' 379 git rev-list HEAD >expect && 380 git commit --allow-empty -m empty && 381 git filter-branch -f --prune-empty HEAD && 382 git rev-list HEAD >actual && 383 test_cmp expect actual 384' 385 386test_expect_success '--prune-empty is able to prune root commit' ' 387 git rev-list branch-no-a >expect && 388 git branch testing H && 389 git filter-branch -f --prune-empty --index-filter "git update-index --remove A.t" testing && 390 git rev-list testing >actual && 391 git branch -D testing && 392 test_cmp expect actual 393' 394 395test_expect_success '--prune-empty is able to prune entire branch' ' 396 git branch prune-entire B && 397 git filter-branch -f --prune-empty --index-filter "git update-index --remove A.t B.t" prune-entire && 398 test_must_fail git rev-parse refs/heads/prune-entire && 399 test_must_fail git reflog exists refs/heads/prune-entire 400' 401 402test_expect_success '--remap-to-ancestor with filename filters' ' 403 git checkout main && 404 git reset --hard A && 405 test_commit add-foo foo 1 && 406 git branch moved-foo && 407 test_commit add-bar bar a && 408 git branch invariant && 409 orig_invariant=$(git rev-parse invariant) && 410 git branch moved-bar && 411 test_commit change-foo foo 2 && 412 git filter-branch -f --remap-to-ancestor \ 413 moved-foo moved-bar A..main \ 414 -- -- foo && 415 test $(git rev-parse moved-foo) = $(git rev-parse moved-bar) && 416 test $(git rev-parse moved-foo) = $(git rev-parse main^) && 417 test $orig_invariant = $(git rev-parse invariant) 418' 419 420test_expect_success 'automatic remapping to ancestor with filename filters' ' 421 git checkout main && 422 git reset --hard A && 423 test_commit add-foo2 foo 1 && 424 git branch moved-foo2 && 425 test_commit add-bar2 bar a && 426 git branch invariant2 && 427 orig_invariant=$(git rev-parse invariant2) && 428 git branch moved-bar2 && 429 test_commit change-foo2 foo 2 && 430 git filter-branch -f \ 431 moved-foo2 moved-bar2 A..main \ 432 -- -- foo && 433 test $(git rev-parse moved-foo2) = $(git rev-parse moved-bar2) && 434 test $(git rev-parse moved-foo2) = $(git rev-parse main^) && 435 test $orig_invariant = $(git rev-parse invariant2) 436' 437 438test_expect_success 'setup submodule' ' 439 rm -fr ?* .git && 440 git init && 441 test_commit file && 442 mkdir submod && 443 submodurl="$PWD/submod" && 444 ( cd submod && 445 git init && 446 test_commit file-in-submod ) && 447 git submodule add "$submodurl" && 448 git commit -m "added submodule" && 449 test_commit add-file && 450 ( cd submod && test_commit add-in-submodule ) && 451 git add submod && 452 git commit -m "changed submodule" && 453 git branch original HEAD 454' 455 456orig_head=$(git show-ref --hash --head HEAD) 457 458test_expect_success 'rewrite submodule with another content' ' 459 git filter-branch --tree-filter "test -d submod && { 460 rm -rf submod && 461 git rm -rf --quiet submod && 462 mkdir submod && 463 : > submod/file 464 } || :" HEAD && 465 test $orig_head != $(git show-ref --hash --head HEAD) 466' 467 468test_expect_success 'replace submodule revision' ' 469 invalid=$(test_oid numeric) && 470 git reset --hard original && 471 git filter-branch -f --tree-filter \ 472 "if git ls-files --error-unmatch -- submod > /dev/null 2>&1 473 then git update-index --cacheinfo 160000 $invalid submod 474 fi" HEAD && 475 test $orig_head != $(git show-ref --hash --head HEAD) 476' 477 478test_expect_success 'filter commit message without trailing newline' ' 479 git reset --hard original && 480 commit=$(printf "no newline" | git commit-tree HEAD^{tree}) && 481 git update-ref refs/heads/no-newline $commit && 482 git filter-branch -f refs/heads/no-newline && 483 echo $commit >expect && 484 git rev-parse refs/heads/no-newline >actual && 485 test_cmp expect actual 486' 487 488test_expect_success 'tree-filter deals with object name vs pathname ambiguity' ' 489 test_when_finished "git reset --hard original" && 490 ambiguous=$(git rev-list -1 HEAD) && 491 git filter-branch --tree-filter "mv file.t $ambiguous" HEAD^.. && 492 git show HEAD:$ambiguous 493' 494 495test_expect_success 'rewrite repository including refs that point at non-commit object' ' 496 test_when_finished "git reset --hard original" && 497 tree=$(git rev-parse HEAD^{tree}) && 498 test_when_finished "git replace -d $tree" && 499 echo A >new && 500 git add new && 501 new_tree=$(git write-tree) && 502 git replace $tree $new_tree && 503 git tag -a -m "tag to a tree" treetag $new_tree && 504 git reset --hard HEAD && 505 git filter-branch -f -- --all >filter-output 2>&1 && 506 ! grep fatal filter-output 507' 508 509test_expect_success 'filter-branch handles ref deletion' ' 510 git switch --orphan empty-commit && 511 git commit --allow-empty -m "empty commit" && 512 git tag empty && 513 git branch to-delete && 514 git filter-branch -f --prune-empty to-delete >out 2>&1 && 515 grep "to-delete.*was deleted" out && 516 test_must_fail git rev-parse --verify to-delete 517' 518 519test_expect_success 'filter-branch handles ref rewrite' ' 520 git checkout empty && 521 test_commit to-drop && 522 git branch rewrite && 523 git filter-branch -f \ 524 --index-filter "git rm --ignore-unmatch --cached to-drop.t" \ 525 rewrite >out 2>&1 && 526 grep "rewrite.*was rewritten" out && 527 ! grep -i warning out && 528 git diff-tree empty rewrite 529' 530 531test_expect_success 'filter-branch handles ancestor rewrite' ' 532 test_commit to-exclude && 533 git branch ancestor && 534 git filter-branch -f ancestor -- :^to-exclude.t >out 2>&1 && 535 grep "ancestor.*was rewritten" out && 536 ! grep -i warning out && 537 git diff-tree HEAD^ ancestor 538' 539 540test_done