(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 <setup>)} { (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: <Left_CommandSub '$('> 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: <Left_CommandSub '$('> 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="\xe3\x82\x8c\xe3\x82\x8c\xe3\x82\x8c" &&\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 <tagger@example.com> 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: <Left_CommandSub '$('> 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)}) ] )