#!/bin/sh global test_description := ''git filter-branch'' source ./test-lib.sh source "$TEST_DIRECTORY/lib-gpg.sh" test_expect_success 'setup' ' test_commit A && GIT_COMMITTER_DATE="@0 +0000" GIT_AUTHOR_DATE="@0 +0000" && test_commit --notick B && git checkout -b branch B && test_commit D && mkdir dir && test_commit dir/D && test_commit E && git checkout master && test_commit C && git checkout branch && git merge C && git tag F && test_commit G && test_commit H ' # * (HEAD, branch) H # * G # * Merge commit 'C' into branch # |\ # | * (master) C # * | E # * | dir/D # * | D # |/ # * B # * A global H := $[git rev-parse H] test_expect_success 'rewrite identically' ' git filter-branch branch ' test_expect_success 'result is really identical' ' test $H = $(git rev-parse HEAD) ' test_expect_success 'rewrite bare repository identically' ' (git config core.bare true && cd .git && git filter-branch branch > filter-output 2>&1 && ! fgrep fatal filter-output) ' git config core.bare false test_expect_success 'result is really identical' ' test $H = $(git rev-parse HEAD) ' global TRASHDIR := $[pwd] test_expect_success 'correct GIT_DIR while using -d' ' mkdir drepo && ( cd drepo && git init && test_commit drepo && git filter-branch -d "$TRASHDIR/dfoo" \ --index-filter "cp \"$TRASHDIR\"/dfoo/backup-refs \"$TRASHDIR\"" \ ) && grep drepo "$TRASHDIR/backup-refs" ' test_expect_success 'tree-filter works with -d' ' git init drepo-tree && ( cd drepo-tree && test_commit one && git filter-branch -d "$TRASHDIR/dfoo" \ --tree-filter "echo changed >one.t" && echo changed >expect && git cat-file blob HEAD:one.t >actual && test_cmp expect actual && test_cmp one.t actual ) ' test_expect_success 'Fail if commit filter fails' ' test_must_fail git filter-branch -f --commit-filter "exit 1" HEAD ' test_expect_success 'rewrite, renaming a specific file' ' git filter-branch -f --tree-filter "mv D.t doh || :" HEAD ' test_expect_success 'test that the file was renamed' ' test D = "$(git show HEAD:doh --)" && ! test -f D.t && test -f doh && test D = "$(cat doh)" ' test_expect_success 'rewrite, renaming a specific directory' ' git filter-branch -f --tree-filter "mv dir diroh || :" HEAD ' test_expect_success 'test that the directory was renamed' ' test dir/D = "$(git show HEAD:diroh/D.t --)" && ! test -d dir && test -d diroh && ! test -d diroh/dir && test -f diroh/D.t && test dir/D = "$(cat diroh/D.t)" ' git tag oldD HEAD~4 test_expect_success 'rewrite one branch, keeping a side branch' ' git branch modD oldD && git filter-branch -f --tree-filter "mv B.t boh || :" D..modD ' test_expect_success 'common ancestor is still common (unchanged)' ' test "$(git merge-base modD D)" = "$(git rev-parse B)" ' test_expect_success 'filter subdirectory only' ' mkdir subdir && touch subdir/new && git add subdir/new && test_tick && git commit -m "subdir" && echo H > A.t && test_tick && git commit -m "not subdir" A.t && echo A > subdir/new && test_tick && git commit -m "again subdir" subdir/new && git rm A.t && test_tick && git commit -m "again not subdir" && git branch sub && git branch sub-earlier HEAD~2 && git filter-branch -f --subdirectory-filter subdir \ refs/heads/sub refs/heads/sub-earlier ' test_expect_success 'subdirectory filter result looks okay' ' test 2 = $(git rev-list sub | wc -l) && git show sub:new && test_must_fail git show sub:subdir && git show sub-earlier:new && test_must_fail git show sub-earlier:subdir ' test_expect_success 'more setup' ' git checkout master && mkdir subdir && echo A > subdir/new && git add subdir/new && test_tick && git commit -m "subdir on master" subdir/new && git rm A.t && test_tick && git commit -m "again subdir on master" && git merge branch ' test_expect_success 'use index-filter to move into a subdirectory' ' git branch directorymoved && git filter-branch -f --index-filter \ "git ls-files -s | sed \"s- -&newsubdir/-\" | GIT_INDEX_FILE=\$GIT_INDEX_FILE.new \ git update-index --index-info && mv \"\$GIT_INDEX_FILE.new\" \"\$GIT_INDEX_FILE\"" directorymoved && git diff --exit-code HEAD directorymoved:newsubdir ' test_expect_success 'stops when msg filter fails' ' old=$(git rev-parse HEAD) && test_must_fail git filter-branch -f --msg-filter false HEAD && test $old = $(git rev-parse HEAD) && rm -rf .git-rewrite ' test_expect_success 'author information is preserved' ' : > i && git add i && test_tick && GIT_AUTHOR_NAME="B V Uips" git commit -m bvuips && git branch preserved-author && (sane_unset GIT_AUTHOR_NAME && git filter-branch -f --msg-filter "cat; \ test \$GIT_COMMIT != $(git rev-parse master) || \ echo Hallo" \ preserved-author) && test 1 = $(git rev-list --author="B V Uips" preserved-author | wc -l) ' test_expect_success "remove a certain author's commits" ' echo i > i && test_tick && git commit -m i i && git branch removed-author && git filter-branch -f --commit-filter "\ if [ \"\$GIT_AUTHOR_NAME\" = \"B V Uips\" ];\ then\ skip_commit \"\$@\"; else\ git commit-tree \"\$@\";\ fi" removed-author && cnt1=$(git rev-list master | wc -l) && cnt2=$(git rev-list removed-author | wc -l) && test $cnt1 -eq $(($cnt2 + 1)) && test 0 = $(git rev-list --author="B V Uips" removed-author | wc -l) ' test_expect_success 'barf on invalid name' ' test_must_fail git filter-branch -f master xy-problem && test_must_fail git filter-branch -f HEAD^ ' test_expect_success '"map" works in commit filter' ' git filter-branch -f --commit-filter "\ parent=\$(git rev-parse \$GIT_COMMIT^) && mapped=\$(map \$parent) && actual=\$(echo \"\$@\" | sed \"s/^.*-p //\") && test \$mapped = \$actual && git commit-tree \"\$@\";" master~2..master && git rev-parse --verify master ' test_expect_success 'Name needing quotes' ' git checkout -b rerere A && mkdir foo && name="れれれ" && >foo/$name && git add foo && git commit -m "Adding a file" && git filter-branch --tree-filter "rm -fr foo" && test_must_fail git ls-files --error-unmatch "foo/$name" && test $(git rev-parse --verify rerere) != $(git rev-parse --verify A) ' test_expect_success 'Subdirectory filter with disappearing trees' ' git reset --hard && git checkout master && mkdir foo && touch foo/bar && git add foo && test_tick && git commit -m "Adding foo" && git rm -r foo && test_tick && git commit -m "Removing foo" && mkdir foo && touch foo/bar && git add foo && test_tick && git commit -m "Re-adding foo" && git filter-branch -f --subdirectory-filter foo && test $(git rev-list master | wc -l) = 3 ' test_expect_success 'Tag name filtering retains tag message' ' git tag -m atag T && git cat-file tag T > expect && git filter-branch -f --tag-name-filter cat && git cat-file tag T > actual && test_cmp expect actual ' global faux_gpg_tag := ''object XXXXXX type commit tag S tagger T A Gger 1206026339 -0500 This is a faux gpg signed tag. -----BEGIN PGP SIGNATURE----- Version: FauxGPG v0.0.0 (FAUX/Linux) gdsfoewhxu/6l06f1kxyxhKdZkrcbaiOMtkJUA9ITAc1mlamh0ooasxkH1XwMbYQ acmwXaWET20H0GeAGP+7vow= =agpO -----END PGP SIGNATURE----- '' test_expect_success 'Tag name filtering strips gpg signature' ' sha1=$(git rev-parse HEAD) && sha1t=$(echo "$faux_gpg_tag" | sed -e s/XXXXXX/$sha1/ | git mktag) && git update-ref "refs/tags/S" "$sha1t" && echo "$faux_gpg_tag" | sed -e s/XXXXXX/$sha1/ | head -n 6 > expect && git filter-branch -f --tag-name-filter cat && git cat-file tag S > actual && test_cmp expect actual ' test_expect_success GPG 'Filtering retains message of gpg signed commit' ' mkdir gpg && touch gpg/foo && git add gpg && test_tick && git commit -S -m "Adding gpg" && git log -1 --format="%s" > expect && git filter-branch -f --msg-filter "cat" && git log -1 --format="%s" > actual && test_cmp expect actual ' test_expect_success 'Tag name filtering allows slashes in tag names' ' git tag -m tag-with-slash X/1 && git cat-file tag X/1 | sed -e s,X/1,X/2, > expect && git filter-branch -f --tag-name-filter "echo X/2" && git cat-file tag X/2 > actual && test_cmp expect actual ' test_expect_success 'Prune empty commits' ' git rev-list HEAD > expect && test_commit to_remove && git filter-branch -f --index-filter "git update-index --remove to_remove.t" --prune-empty HEAD && git rev-list HEAD > actual && test_cmp expect actual ' test_expect_success 'prune empty collapsed merges' ' test_config merge.ff false && git rev-list HEAD >expect && test_commit to_remove_2 && git reset --hard HEAD^ && test_merge non-ff to_remove_2 && git filter-branch -f --index-filter "git update-index --remove to_remove_2.t" --prune-empty HEAD && git rev-list HEAD >actual && test_cmp expect actual ' test_expect_success 'prune empty works even without index/tree filters' ' git rev-list HEAD >expect && git commit --allow-empty -m empty && git filter-branch -f --prune-empty HEAD && git rev-list HEAD >actual && test_cmp expect actual ' test_expect_success '--remap-to-ancestor with filename filters' ' git checkout master && git reset --hard A && test_commit add-foo foo 1 && git branch moved-foo && test_commit add-bar bar a && git branch invariant && orig_invariant=$(git rev-parse invariant) && git branch moved-bar && test_commit change-foo foo 2 && git filter-branch -f --remap-to-ancestor \ moved-foo moved-bar A..master \ -- -- foo && test $(git rev-parse moved-foo) = $(git rev-parse moved-bar) && test $(git rev-parse moved-foo) = $(git rev-parse master^) && test $orig_invariant = $(git rev-parse invariant) ' test_expect_success 'automatic remapping to ancestor with filename filters' ' git checkout master && git reset --hard A && test_commit add-foo2 foo 1 && git branch moved-foo2 && test_commit add-bar2 bar a && git branch invariant2 && orig_invariant=$(git rev-parse invariant2) && git branch moved-bar2 && test_commit change-foo2 foo 2 && git filter-branch -f \ moved-foo2 moved-bar2 A..master \ -- -- foo && test $(git rev-parse moved-foo2) = $(git rev-parse moved-bar2) && test $(git rev-parse moved-foo2) = $(git rev-parse master^) && test $orig_invariant = $(git rev-parse invariant2) ' test_expect_success 'setup submodule' ' rm -fr ?* .git && git init && test_commit file && mkdir submod && submodurl="$PWD/submod" && ( cd submod && git init && test_commit file-in-submod ) && git submodule add "$submodurl" && git commit -m "added submodule" && test_commit add-file && ( cd submod && test_commit add-in-submodule ) && git add submod && git commit -m "changed submodule" && git branch original HEAD ' global orig_head := $[git show-ref --hash --head HEAD] test_expect_success 'rewrite submodule with another content' ' git filter-branch --tree-filter "test -d submod && { rm -rf submod && git rm -rf --quiet submod && mkdir submod && : > submod/file } || :" HEAD && test $orig_head != $(git show-ref --hash --head HEAD) ' test_expect_success 'replace submodule revision' ' git reset --hard original && git filter-branch -f --tree-filter \ "if git ls-files --error-unmatch -- submod > /dev/null 2>&1 then git update-index --cacheinfo 160000 0123456789012345678901234567890123456789 submod fi" HEAD && test $orig_head != $(git show-ref --hash --head HEAD) ' test_expect_success 'filter commit message without trailing newline' ' git reset --hard original && commit=$(printf "no newline" | git commit-tree HEAD^{tree}) && git update-ref refs/heads/no-newline $commit && git filter-branch -f refs/heads/no-newline && echo $commit >expect && git rev-parse refs/heads/no-newline >actual && test_cmp expect actual ' test_expect_success 'tree-filter deals with object name vs pathname ambiguity' ' test_when_finished "git reset --hard original" && ambiguous=$(git rev-list -1 HEAD) && git filter-branch --tree-filter "mv file.t $ambiguous" HEAD^.. && git show HEAD:$ambiguous ' test_done (CommandList children: [ (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:test_description) op: Equal rhs: {(SQ <"git filter-branch">)} spids: [4] ) ] spids: [4] ) (C {(.)} {(./test-lib.sh)}) (C {(.)} {(DQ ($ VSub_Name "$TEST_DIRECTORY") (/lib-gpg.sh))}) (C {(test_expect_success)} {(SQ )} { (SQ <"\n"> <"\ttest_commit A &&\n"> <"\tGIT_COMMITTER_DATE=\"@0 +0000\" GIT_AUTHOR_DATE=\"@0 +0000\" &&\n"> <"\ttest_commit --notick B &&\n"> <"\tgit checkout -b branch B &&\n"> <"\ttest_commit D &&\n"> <"\tmkdir dir &&\n"> <"\ttest_commit dir/D &&\n"> <"\ttest_commit E &&\n"> <"\tgit checkout master &&\n"> <"\ttest_commit C &&\n"> <"\tgit checkout branch &&\n"> <"\tgit merge C &&\n"> <"\tgit tag F &&\n"> <"\ttest_commit G &&\n"> <"\ttest_commit H\n"> ) } ) (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:H) op: Equal rhs: { (CommandSubPart command_list: (CommandList children:[(C {(git)} {(rev-parse)} {(H)})]) left_token: spids: [82 88] ) } spids: [81] ) ] spids: [81] ) (C {(test_expect_success)} {(SQ <"rewrite identically">)} {(SQ <"\n"> <"\tgit filter-branch branch\n">)} ) (C {(test_expect_success)} {(SQ <"result is really identical">)} {(SQ <"\n"> <"\ttest $H = $(git rev-parse HEAD)\n">)} ) (C {(test_expect_success)} {(SQ <"rewrite bare repository identically">)} { (SQ <"\n"> <"\t(git config core.bare true && cd .git &&\n"> <"\t git filter-branch branch > filter-output 2>&1 &&\n"> <"\t! fgrep fatal filter-output)\n"> ) } ) (C {(git)} {(config)} {(core.bare)} {(false)}) (C {(test_expect_success)} {(SQ <"result is really identical">)} {(SQ <"\n"> <"\ttest $H = $(git rev-parse HEAD)\n">)} ) (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:TRASHDIR) op: Equal rhs: { (CommandSubPart command_list: (CommandList children:[(C {(pwd)})]) left_token: spids: [148 150] ) } spids: [147] ) ] spids: [147] ) (C {(test_expect_success)} {(SQ <"correct GIT_DIR while using -d">)} { (SQ <"\n"> <"\tmkdir drepo &&\n"> <"\t( cd drepo &&\n"> <"\tgit init &&\n"> <"\ttest_commit drepo &&\n"> <"\tgit filter-branch -d \"$TRASHDIR/dfoo\" \\\n"> <"\t\t--index-filter \"cp \\\"$TRASHDIR\\\"/dfoo/backup-refs \\\"$TRASHDIR\\\"\" \\\n"> <"\t) &&\n"> <"\tgrep drepo \"$TRASHDIR/backup-refs\"\n"> ) } ) (C {(test_expect_success)} {(SQ <"tree-filter works with -d">)} { (SQ <"\n"> <"\tgit init drepo-tree &&\n"> <"\t(\n"> <"\t\tcd drepo-tree &&\n"> <"\t\ttest_commit one &&\n"> <"\t\tgit filter-branch -d \"$TRASHDIR/dfoo\" \\\n"> <"\t\t\t--tree-filter \"echo changed >one.t\" &&\n"> <"\t\techo changed >expect &&\n"> <"\t\tgit cat-file blob HEAD:one.t >actual &&\n"> <"\t\ttest_cmp expect actual &&\n"> <"\t\ttest_cmp one.t actual\n"> <"\t)\n"> ) } ) (C {(test_expect_success)} {(SQ <"Fail if commit filter fails">)} {(SQ <"\n"> <"\ttest_must_fail git filter-branch -f --commit-filter \"exit 1\" HEAD\n">)} ) (C {(test_expect_success)} {(SQ <"rewrite, renaming a specific file">)} {(SQ <"\n"> <"\tgit filter-branch -f --tree-filter \"mv D.t doh || :\" HEAD\n">)} ) (C {(test_expect_success)} {(SQ <"test that the file was renamed">)} { (SQ <"\n"> <"\ttest D = \"$(git show HEAD:doh --)\" &&\n"> <"\t! test -f D.t &&\n"> <"\ttest -f doh &&\n"> <"\ttest D = \"$(cat doh)\"\n"> ) } ) (C {(test_expect_success)} {(SQ <"rewrite, renaming a specific directory">)} {(SQ <"\n"> <"\tgit filter-branch -f --tree-filter \"mv dir diroh || :\" HEAD\n">)} ) (C {(test_expect_success)} {(SQ <"test that the directory was renamed">)} { (SQ <"\n"> <"\ttest dir/D = \"$(git show HEAD:diroh/D.t --)\" &&\n"> <"\t! test -d dir &&\n"> <"\ttest -d diroh &&\n"> <"\t! test -d diroh/dir &&\n"> <"\ttest -f diroh/D.t &&\n"> <"\ttest dir/D = \"$(cat diroh/D.t)\"\n"> ) } ) (C {(git)} {(tag)} {(oldD)} {(HEAD) (Lit_Tilde "~") (4)}) (C {(test_expect_success)} {(SQ <"rewrite one branch, keeping a side branch">)} { (SQ <"\n"> <"\tgit branch modD oldD &&\n"> <"\tgit filter-branch -f --tree-filter \"mv B.t boh || :\" D..modD\n"> ) } ) (C {(test_expect_success)} {(SQ <"common ancestor is still common (unchanged)">)} {(SQ <"\n"> <"\ttest \"$(git merge-base modD D)\" = \"$(git rev-parse B)\"\n">)} ) (C {(test_expect_success)} {(SQ <"filter subdirectory only">)} { (SQ <"\n"> <"\tmkdir subdir &&\n"> <"\ttouch subdir/new &&\n"> <"\tgit add subdir/new &&\n"> <"\ttest_tick &&\n"> <"\tgit commit -m \"subdir\" &&\n"> <"\techo H > A.t &&\n"> <"\ttest_tick &&\n"> <"\tgit commit -m \"not subdir\" A.t &&\n"> <"\techo A > subdir/new &&\n"> <"\ttest_tick &&\n"> <"\tgit commit -m \"again subdir\" subdir/new &&\n"> <"\tgit rm A.t &&\n"> <"\ttest_tick &&\n"> <"\tgit commit -m \"again not subdir\" &&\n"> <"\tgit branch sub &&\n"> <"\tgit branch sub-earlier HEAD~2 &&\n"> <"\tgit filter-branch -f --subdirectory-filter subdir \\\n"> <"\t\trefs/heads/sub refs/heads/sub-earlier\n"> ) } ) (C {(test_expect_success)} {(SQ <"subdirectory filter result looks okay">)} { (SQ <"\n"> <"\ttest 2 = $(git rev-list sub | wc -l) &&\n"> <"\tgit show sub:new &&\n"> <"\ttest_must_fail git show sub:subdir &&\n"> <"\tgit show sub-earlier:new &&\n"> <"\ttest_must_fail git show sub-earlier:subdir\n"> ) } ) (C {(test_expect_success)} {(SQ <"more setup">)} { (SQ <"\n"> <"\tgit checkout master &&\n"> <"\tmkdir subdir &&\n"> <"\techo A > subdir/new &&\n"> <"\tgit add subdir/new &&\n"> <"\ttest_tick &&\n"> <"\tgit commit -m \"subdir on master\" subdir/new &&\n"> <"\tgit rm A.t &&\n"> <"\ttest_tick &&\n"> <"\tgit commit -m \"again subdir on master\" &&\n"> <"\tgit merge branch\n"> ) } ) (C {(test_expect_success)} {(SQ <"use index-filter to move into a subdirectory">)} { (SQ <"\n"> <"\tgit branch directorymoved &&\n"> <"\tgit filter-branch -f --index-filter \\\n"> <"\t\t \"git ls-files -s | sed \\\"s-\t-&newsubdir/-\\\" |\n"> <"\t GIT_INDEX_FILE=\\$GIT_INDEX_FILE.new \\\n"> <"\t\t\tgit update-index --index-info &&\n"> < "\t\t mv \\\"\\$GIT_INDEX_FILE.new\\\" \\\"\\$GIT_INDEX_FILE\\\"\" directorymoved &&\n" > <"\tgit diff --exit-code HEAD directorymoved:newsubdir\n"> ) } ) (C {(test_expect_success)} {(SQ <"stops when msg filter fails">)} { (SQ <"\n"> <"\told=$(git rev-parse HEAD) &&\n"> <"\ttest_must_fail git filter-branch -f --msg-filter false HEAD &&\n"> <"\ttest $old = $(git rev-parse HEAD) &&\n"> <"\trm -rf .git-rewrite\n"> ) } ) (C {(test_expect_success)} {(SQ <"author information is preserved">)} { (SQ <"\n"> <"\t: > i &&\n"> <"\tgit add i &&\n"> <"\ttest_tick &&\n"> <"\tGIT_AUTHOR_NAME=\"B V Uips\" git commit -m bvuips &&\n"> <"\tgit branch preserved-author &&\n"> <"\t(sane_unset GIT_AUTHOR_NAME &&\n"> <"\t git filter-branch -f --msg-filter \"cat; \\\n"> <"\t\t\ttest \\$GIT_COMMIT != $(git rev-parse master) || \\\n"> <"\t\t\techo Hallo\" \\\n"> <"\t\tpreserved-author) &&\n"> <"\ttest 1 = $(git rev-list --author=\"B V Uips\" preserved-author | wc -l)\n"> ) } ) (C {(test_expect_success)} {(DQ ("remove a certain author's commits"))} { (SQ <"\n"> <"\techo i > i &&\n"> <"\ttest_tick &&\n"> <"\tgit commit -m i i &&\n"> <"\tgit branch removed-author &&\n"> <"\tgit filter-branch -f --commit-filter \"\\\n"> <"\t\tif [ \\\"\\$GIT_AUTHOR_NAME\\\" = \\\"B V Uips\\\" ];\\\n"> <"\t\tthen\\\n"> <"\t\t\tskip_commit \\\"\\$@\\\";\n"> <"\t\telse\\\n"> <"\t\t\tgit commit-tree \\\"\\$@\\\";\\\n"> <"\t\tfi\" removed-author &&\n"> <"\tcnt1=$(git rev-list master | wc -l) &&\n"> <"\tcnt2=$(git rev-list removed-author | wc -l) &&\n"> <"\ttest $cnt1 -eq $(($cnt2 + 1)) &&\n"> <"\ttest 0 = $(git rev-list --author=\"B V Uips\" removed-author | wc -l)\n"> ) } ) (C {(test_expect_success)} {(SQ <"barf on invalid name">)} { (SQ <"\n"> <"\ttest_must_fail git filter-branch -f master xy-problem &&\n"> <"\ttest_must_fail git filter-branch -f HEAD^\n"> ) } ) (C {(test_expect_success)} {(SQ <"\"map\" works in commit filter">)} { (SQ <"\n"> <"\tgit filter-branch -f --commit-filter \"\\\n"> <"\t\tparent=\\$(git rev-parse \\$GIT_COMMIT^) &&\n"> <"\t\tmapped=\\$(map \\$parent) &&\n"> <"\t\tactual=\\$(echo \\\"\\$@\\\" | sed \\\"s/^.*-p //\\\") &&\n"> <"\t\ttest \\$mapped = \\$actual &&\n"> <"\t\tgit commit-tree \\\"\\$@\\\";\" master~2..master &&\n"> <"\tgit rev-parse --verify master\n"> ) } ) (C {(test_expect_success)} {(SQ <"Name needing quotes">)} { (SQ <"\n"> <"\n"> <"\tgit checkout -b rerere A &&\n"> <"\tmkdir foo &&\n"> <"\tname=\"\u308c\u308c\u308c\" &&\n"> <"\t>foo/$name &&\n"> <"\tgit add foo &&\n"> <"\tgit commit -m \"Adding a file\" &&\n"> <"\tgit filter-branch --tree-filter \"rm -fr foo\" &&\n"> <"\ttest_must_fail git ls-files --error-unmatch \"foo/$name\" &&\n"> <"\ttest $(git rev-parse --verify rerere) != $(git rev-parse --verify A)\n"> <"\n"> ) } ) (C {(test_expect_success)} {(SQ <"Subdirectory filter with disappearing trees">)} { (SQ <"\n"> <"\tgit reset --hard &&\n"> <"\tgit checkout master &&\n"> <"\n"> <"\tmkdir foo &&\n"> <"\ttouch foo/bar &&\n"> <"\tgit add foo &&\n"> <"\ttest_tick &&\n"> <"\tgit commit -m \"Adding foo\" &&\n"> <"\n"> <"\tgit rm -r foo &&\n"> <"\ttest_tick &&\n"> <"\tgit commit -m \"Removing foo\" &&\n"> <"\n"> <"\tmkdir foo &&\n"> <"\ttouch foo/bar &&\n"> <"\tgit add foo &&\n"> <"\ttest_tick &&\n"> <"\tgit commit -m \"Re-adding foo\" &&\n"> <"\n"> <"\tgit filter-branch -f --subdirectory-filter foo &&\n"> <"\ttest $(git rev-list master | wc -l) = 3\n"> ) } ) (C {(test_expect_success)} {(SQ <"Tag name filtering retains tag message">)} { (SQ <"\n"> <"\tgit tag -m atag T &&\n"> <"\tgit cat-file tag T > expect &&\n"> <"\tgit filter-branch -f --tag-name-filter cat &&\n"> <"\tgit cat-file tag T > actual &&\n"> <"\ttest_cmp expect actual\n"> ) } ) (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:faux_gpg_tag) op: Equal rhs: { (SQ <"object XXXXXX\n"> <"type commit\n"> <"tag S\n"> <"tagger T A Gger 1206026339 -0500\n"> <"\n"> <"This is a faux gpg signed tag.\n"> <"-----BEGIN PGP SIGNATURE-----\n"> <"Version: FauxGPG v0.0.0 (FAUX/Linux)\n"> <"\n"> <"gdsfoewhxu/6l06f1kxyxhKdZkrcbaiOMtkJUA9ITAc1mlamh0ooasxkH1XwMbYQ\n"> <"acmwXaWET20H0GeAGP+7vow=\n"> <"=agpO\n"> <"-----END PGP SIGNATURE-----\n"> ) } spids: [544] ) ] spids: [544] ) (C {(test_expect_success)} {(SQ <"Tag name filtering strips gpg signature">)} { (SQ <"\n"> <"\tsha1=$(git rev-parse HEAD) &&\n"> <"\tsha1t=$(echo \"$faux_gpg_tag\" | sed -e s/XXXXXX/$sha1/ | git mktag) &&\n"> <"\tgit update-ref \"refs/tags/S\" \"$sha1t\" &&\n"> <"\techo \"$faux_gpg_tag\" | sed -e s/XXXXXX/$sha1/ | head -n 6 > expect &&\n"> <"\tgit filter-branch -f --tag-name-filter cat &&\n"> <"\tgit cat-file tag S > actual &&\n"> <"\ttest_cmp expect actual\n"> ) } ) (C {(test_expect_success)} {(GPG)} {(SQ <"Filtering retains message of gpg signed commit">)} { (SQ <"\n"> <"\tmkdir gpg &&\n"> <"\ttouch gpg/foo &&\n"> <"\tgit add gpg &&\n"> <"\ttest_tick &&\n"> <"\tgit commit -S -m \"Adding gpg\" &&\n"> <"\n"> <"\tgit log -1 --format=\"%s\" > expect &&\n"> <"\tgit filter-branch -f --msg-filter \"cat\" &&\n"> <"\tgit log -1 --format=\"%s\" > actual &&\n"> <"\ttest_cmp expect actual\n"> ) } ) (C {(test_expect_success)} {(SQ <"Tag name filtering allows slashes in tag names">)} { (SQ <"\n"> <"\tgit tag -m tag-with-slash X/1 &&\n"> <"\tgit cat-file tag X/1 | sed -e s,X/1,X/2, > expect &&\n"> <"\tgit filter-branch -f --tag-name-filter \"echo X/2\" &&\n"> <"\tgit cat-file tag X/2 > actual &&\n"> <"\ttest_cmp expect actual\n"> ) } ) (C {(test_expect_success)} {(SQ <"Prune empty commits">)} { (SQ <"\n"> <"\tgit rev-list HEAD > expect &&\n"> <"\ttest_commit to_remove &&\n"> < "\tgit filter-branch -f --index-filter \"git update-index --remove to_remove.t\" --prune-empty HEAD &&\n" > <"\tgit rev-list HEAD > actual &&\n"> <"\ttest_cmp expect actual\n"> ) } ) (C {(test_expect_success)} {(SQ <"prune empty collapsed merges">)} { (SQ <"\n"> <"\ttest_config merge.ff false &&\n"> <"\tgit rev-list HEAD >expect &&\n"> <"\ttest_commit to_remove_2 &&\n"> <"\tgit reset --hard HEAD^ &&\n"> <"\ttest_merge non-ff to_remove_2 &&\n"> < "\tgit filter-branch -f --index-filter \"git update-index --remove to_remove_2.t\" --prune-empty HEAD &&\n" > <"\tgit rev-list HEAD >actual &&\n"> <"\ttest_cmp expect actual\n"> ) } ) (C {(test_expect_success)} {(SQ <"prune empty works even without index/tree filters">)} { (SQ <"\n"> <"\tgit rev-list HEAD >expect &&\n"> <"\tgit commit --allow-empty -m empty &&\n"> <"\tgit filter-branch -f --prune-empty HEAD &&\n"> <"\tgit rev-list HEAD >actual &&\n"> <"\ttest_cmp expect actual\n"> ) } ) (C {(test_expect_success)} {(SQ <"--remap-to-ancestor with filename filters">)} { (SQ <"\n"> <"\tgit checkout master &&\n"> <"\tgit reset --hard A &&\n"> <"\ttest_commit add-foo foo 1 &&\n"> <"\tgit branch moved-foo &&\n"> <"\ttest_commit add-bar bar a &&\n"> <"\tgit branch invariant &&\n"> <"\torig_invariant=$(git rev-parse invariant) &&\n"> <"\tgit branch moved-bar &&\n"> <"\ttest_commit change-foo foo 2 &&\n"> <"\tgit filter-branch -f --remap-to-ancestor \\\n"> <"\t\tmoved-foo moved-bar A..master \\\n"> <"\t\t-- -- foo &&\n"> <"\ttest $(git rev-parse moved-foo) = $(git rev-parse moved-bar) &&\n"> <"\ttest $(git rev-parse moved-foo) = $(git rev-parse master^) &&\n"> <"\ttest $orig_invariant = $(git rev-parse invariant)\n"> ) } ) (C {(test_expect_success)} {(SQ <"automatic remapping to ancestor with filename filters">)} { (SQ <"\n"> <"\tgit checkout master &&\n"> <"\tgit reset --hard A &&\n"> <"\ttest_commit add-foo2 foo 1 &&\n"> <"\tgit branch moved-foo2 &&\n"> <"\ttest_commit add-bar2 bar a &&\n"> <"\tgit branch invariant2 &&\n"> <"\torig_invariant=$(git rev-parse invariant2) &&\n"> <"\tgit branch moved-bar2 &&\n"> <"\ttest_commit change-foo2 foo 2 &&\n"> <"\tgit filter-branch -f \\\n"> <"\t\tmoved-foo2 moved-bar2 A..master \\\n"> <"\t\t-- -- foo &&\n"> <"\ttest $(git rev-parse moved-foo2) = $(git rev-parse moved-bar2) &&\n"> <"\ttest $(git rev-parse moved-foo2) = $(git rev-parse master^) &&\n"> <"\ttest $orig_invariant = $(git rev-parse invariant2)\n"> ) } ) (C {(test_expect_success)} {(SQ <"setup submodule">)} { (SQ <"\n"> <"\trm -fr ?* .git &&\n"> <"\tgit init &&\n"> <"\ttest_commit file &&\n"> <"\tmkdir submod &&\n"> <"\tsubmodurl=\"$PWD/submod\" &&\n"> <"\t( cd submod &&\n"> <"\t git init &&\n"> <"\t test_commit file-in-submod ) &&\n"> <"\tgit submodule add \"$submodurl\" &&\n"> <"\tgit commit -m \"added submodule\" &&\n"> <"\ttest_commit add-file &&\n"> <"\t( cd submod && test_commit add-in-submodule ) &&\n"> <"\tgit add submod &&\n"> <"\tgit commit -m \"changed submodule\" &&\n"> <"\tgit branch original HEAD\n"> ) } ) (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:orig_head) op: Equal rhs: { (CommandSubPart command_list: (CommandList children: [(C {(git)} {(show-ref)} {(--hash)} {(--head)} {(HEAD)})] ) left_token: spids: [748 758] ) } spids: [747] ) ] spids: [747] ) (C {(test_expect_success)} {(SQ <"rewrite submodule with another content">)} { (SQ <"\n"> <"\tgit filter-branch --tree-filter \"test -d submod && {\n"> <"\t\t\t\t\t rm -rf submod &&\n"> <"\t\t\t\t\t git rm -rf --quiet submod &&\n"> <"\t\t\t\t\t mkdir submod &&\n"> <"\t\t\t\t\t : > submod/file\n"> <"\t\t\t\t\t } || :\" HEAD &&\n"> <"\ttest $orig_head != $(git show-ref --hash --head HEAD)\n"> ) } ) (C {(test_expect_success)} {(SQ <"replace submodule revision">)} { (SQ <"\n"> <"\tgit reset --hard original &&\n"> <"\tgit filter-branch -f --tree-filter \\\n"> <"\t \"if git ls-files --error-unmatch -- submod > /dev/null 2>&1\n"> <"\t then git update-index --cacheinfo 160000 0123456789012345678901234567890123456789 submod\n"> <"\t fi\" HEAD &&\n"> <"\ttest $orig_head != $(git show-ref --hash --head HEAD)\n"> ) } ) (C {(test_expect_success)} {(SQ <"filter commit message without trailing newline">)} { (SQ <"\n"> <"\tgit reset --hard original &&\n"> <"\tcommit=$(printf \"no newline\" | git commit-tree HEAD^{tree}) &&\n"> <"\tgit update-ref refs/heads/no-newline $commit &&\n"> <"\tgit filter-branch -f refs/heads/no-newline &&\n"> <"\techo $commit >expect &&\n"> <"\tgit rev-parse refs/heads/no-newline >actual &&\n"> <"\ttest_cmp expect actual\n"> ) } ) (C {(test_expect_success)} {(SQ <"tree-filter deals with object name vs pathname ambiguity">)} { (SQ <"\n"> <"\ttest_when_finished \"git reset --hard original\" &&\n"> <"\tambiguous=$(git rev-list -1 HEAD) &&\n"> <"\tgit filter-branch --tree-filter \"mv file.t $ambiguous\" HEAD^.. &&\n"> <"\tgit show HEAD:$ambiguous\n"> ) } ) (C {(test_done)}) ] )