Merge branch 'jc/maint-protect-sh-from-ifs'
[git] / t / t7003-filter-branch.sh
1 #!/bin/sh
2
3 test_description='git filter-branch'
4 . ./test-lib.sh
5
6 test_expect_success 'setup' '
7         test_commit A &&
8         GIT_COMMITTER_DATE="@0 +0000" GIT_AUTHOR_DATE="@0 +0000" &&
9         test_commit --notick B &&
10         git checkout -b branch B &&
11         test_commit D &&
12         mkdir dir &&
13         test_commit dir/D &&
14         test_commit E &&
15         git checkout master &&
16         test_commit C &&
17         git checkout branch &&
18         git merge C &&
19         git tag F &&
20         test_commit G &&
21         test_commit H
22 '
23 # * (HEAD, branch) H
24 # * G
25 # *   Merge commit 'C' into branch
26 # |\
27 # | * (master) C
28 # * | E
29 # * | dir/D
30 # * | D
31 # |/
32 # * B
33 # * A
34
35
36 H=$(git rev-parse H)
37
38 test_expect_success 'rewrite identically' '
39         git filter-branch branch
40 '
41 test_expect_success 'result is really identical' '
42         test $H = $(git rev-parse HEAD)
43 '
44
45 test_expect_success 'rewrite bare repository identically' '
46         (git config core.bare true && cd .git &&
47          git filter-branch branch > filter-output 2>&1 &&
48         ! fgrep fatal filter-output)
49 '
50 git config core.bare false
51 test_expect_success 'result is really identical' '
52         test $H = $(git rev-parse HEAD)
53 '
54
55 TRASHDIR=$(pwd)
56 test_expect_success 'correct GIT_DIR while using -d' '
57         mkdir drepo &&
58         ( cd drepo &&
59         git init &&
60         test_commit drepo &&
61         git filter-branch -d "$TRASHDIR/dfoo" \
62                 --index-filter "cp \"$TRASHDIR\"/dfoo/backup-refs \"$TRASHDIR\"" \
63         ) &&
64         grep drepo "$TRASHDIR/backup-refs"
65 '
66
67 test_expect_success 'Fail if commit filter fails' '
68         test_must_fail git filter-branch -f --commit-filter "exit 1" HEAD
69 '
70
71 test_expect_success 'rewrite, renaming a specific file' '
72         git filter-branch -f --tree-filter "mv D.t doh || :" HEAD
73 '
74
75 test_expect_success 'test that the file was renamed' '
76         test D = "$(git show HEAD:doh --)" &&
77         ! test -f D.t &&
78         test -f doh &&
79         test D = "$(cat doh)"
80 '
81
82 test_expect_success 'rewrite, renaming a specific directory' '
83         git filter-branch -f --tree-filter "mv dir diroh || :" HEAD
84 '
85
86 test_expect_success 'test that the directory was renamed' '
87         test dir/D = "$(git show HEAD:diroh/D.t --)" &&
88         ! test -d dir &&
89         test -d diroh &&
90         ! test -d diroh/dir &&
91         test -f diroh/D.t &&
92         test dir/D = "$(cat diroh/D.t)"
93 '
94
95 git tag oldD HEAD~4
96 test_expect_success 'rewrite one branch, keeping a side branch' '
97         git branch modD oldD &&
98         git filter-branch -f --tree-filter "mv B.t boh || :" D..modD
99 '
100
101 test_expect_success 'common ancestor is still common (unchanged)' '
102         test "$(git merge-base modD D)" = "$(git rev-parse B)"
103 '
104
105 test_expect_success 'filter subdirectory only' '
106         mkdir subdir &&
107         touch subdir/new &&
108         git add subdir/new &&
109         test_tick &&
110         git commit -m "subdir" &&
111         echo H > A.t &&
112         test_tick &&
113         git commit -m "not subdir" A.t &&
114         echo A > subdir/new &&
115         test_tick &&
116         git commit -m "again subdir" subdir/new &&
117         git rm A.t &&
118         test_tick &&
119         git commit -m "again not subdir" &&
120         git branch sub &&
121         git branch sub-earlier HEAD~2 &&
122         git filter-branch -f --subdirectory-filter subdir \
123                 refs/heads/sub refs/heads/sub-earlier
124 '
125
126 test_expect_success 'subdirectory filter result looks okay' '
127         test 2 = $(git rev-list sub | wc -l) &&
128         git show sub:new &&
129         test_must_fail git show sub:subdir &&
130         git show sub-earlier:new &&
131         test_must_fail git show sub-earlier:subdir
132 '
133
134 test_expect_success 'more setup' '
135         git checkout master &&
136         mkdir subdir &&
137         echo A > subdir/new &&
138         git add subdir/new &&
139         test_tick &&
140         git commit -m "subdir on master" subdir/new &&
141         git rm A.t &&
142         test_tick &&
143         git commit -m "again subdir on master" &&
144         git merge branch
145 '
146
147 test_expect_success 'use index-filter to move into a subdirectory' '
148         git branch directorymoved &&
149         git filter-branch -f --index-filter \
150                  "git ls-files -s | sed \"s-    -&newsubdir/-\" |
151                   GIT_INDEX_FILE=\$GIT_INDEX_FILE.new \
152                         git update-index --index-info &&
153                   mv \"\$GIT_INDEX_FILE.new\" \"\$GIT_INDEX_FILE\"" directorymoved &&
154         git diff --exit-code HEAD directorymoved:newsubdir
155 '
156
157 test_expect_success 'stops when msg filter fails' '
158         old=$(git rev-parse HEAD) &&
159         test_must_fail git filter-branch -f --msg-filter false HEAD &&
160         test $old = $(git rev-parse HEAD) &&
161         rm -rf .git-rewrite
162 '
163
164 test_expect_success 'author information is preserved' '
165         : > i &&
166         git add i &&
167         test_tick &&
168         GIT_AUTHOR_NAME="B V Uips" git commit -m bvuips &&
169         git branch preserved-author &&
170         git filter-branch -f --msg-filter "cat; \
171                         test \$GIT_COMMIT != $(git rev-parse master) || \
172                         echo Hallo" \
173                 preserved-author &&
174         test 1 = $(git rev-list --author="B V Uips" preserved-author | wc -l)
175 '
176
177 test_expect_success "remove a certain author's commits" '
178         echo i > i &&
179         test_tick &&
180         git commit -m i i &&
181         git branch removed-author &&
182         git filter-branch -f --commit-filter "\
183                 if [ \"\$GIT_AUTHOR_NAME\" = \"B V Uips\" ];\
184                 then\
185                         skip_commit \"\$@\";
186                 else\
187                         git commit-tree \"\$@\";\
188                 fi" removed-author &&
189         cnt1=$(git rev-list master | wc -l) &&
190         cnt2=$(git rev-list removed-author | wc -l) &&
191         test $cnt1 -eq $(($cnt2 + 1)) &&
192         test 0 = $(git rev-list --author="B V Uips" removed-author | wc -l)
193 '
194
195 test_expect_success 'barf on invalid name' '
196         test_must_fail git filter-branch -f master xy-problem &&
197         test_must_fail git filter-branch -f HEAD^
198 '
199
200 test_expect_success '"map" works in commit filter' '
201         git filter-branch -f --commit-filter "\
202                 parent=\$(git rev-parse \$GIT_COMMIT^) &&
203                 mapped=\$(map \$parent) &&
204                 actual=\$(echo \"\$@\" | sed \"s/^.*-p //\") &&
205                 test \$mapped = \$actual &&
206                 git commit-tree \"\$@\";" master~2..master &&
207         git rev-parse --verify master
208 '
209
210 test_expect_success 'Name needing quotes' '
211
212         git checkout -b rerere A &&
213         mkdir foo &&
214         name="れれれ" &&
215         >foo/$name &&
216         git add foo &&
217         git commit -m "Adding a file" &&
218         git filter-branch --tree-filter "rm -fr foo" &&
219         test_must_fail git ls-files --error-unmatch "foo/$name" &&
220         test $(git rev-parse --verify rerere) != $(git rev-parse --verify A)
221
222 '
223
224 test_expect_success 'Subdirectory filter with disappearing trees' '
225         git reset --hard &&
226         git checkout master &&
227
228         mkdir foo &&
229         touch foo/bar &&
230         git add foo &&
231         test_tick &&
232         git commit -m "Adding foo" &&
233
234         git rm -r foo &&
235         test_tick &&
236         git commit -m "Removing foo" &&
237
238         mkdir foo &&
239         touch foo/bar &&
240         git add foo &&
241         test_tick &&
242         git commit -m "Re-adding foo" &&
243
244         git filter-branch -f --subdirectory-filter foo &&
245         test $(git rev-list master | wc -l) = 3
246 '
247
248 test_expect_success 'Tag name filtering retains tag message' '
249         git tag -m atag T &&
250         git cat-file tag T > expect &&
251         git filter-branch -f --tag-name-filter cat &&
252         git cat-file tag T > actual &&
253         test_cmp expect actual
254 '
255
256 faux_gpg_tag='object XXXXXX
257 type commit
258 tag S
259 tagger T A Gger <tagger@example.com> 1206026339 -0500
260
261 This is a faux gpg signed tag.
262 -----BEGIN PGP SIGNATURE-----
263 Version: FauxGPG v0.0.0 (FAUX/Linux)
264
265 gdsfoewhxu/6l06f1kxyxhKdZkrcbaiOMtkJUA9ITAc1mlamh0ooasxkH1XwMbYQ
266 acmwXaWET20H0GeAGP+7vow=
267 =agpO
268 -----END PGP SIGNATURE-----
269 '
270 test_expect_success 'Tag name filtering strips gpg signature' '
271         sha1=$(git rev-parse HEAD) &&
272         sha1t=$(echo "$faux_gpg_tag" | sed -e s/XXXXXX/$sha1/ | git mktag) &&
273         git update-ref "refs/tags/S" "$sha1t" &&
274         echo "$faux_gpg_tag" | sed -e s/XXXXXX/$sha1/ | head -n 6 > expect &&
275         git filter-branch -f --tag-name-filter cat &&
276         git cat-file tag S > actual &&
277         test_cmp expect actual
278 '
279
280 test_expect_success 'Tag name filtering allows slashes in tag names' '
281         git tag -m tag-with-slash X/1 &&
282         git cat-file tag X/1 | sed -e s,X/1,X/2, > expect &&
283         git filter-branch -f --tag-name-filter "echo X/2" &&
284         git cat-file tag X/2 > actual &&
285         test_cmp expect actual
286 '
287
288 test_expect_success 'Prune empty commits' '
289         git rev-list HEAD > expect &&
290         test_commit to_remove &&
291         git filter-branch -f --index-filter "git update-index --remove to_remove.t" --prune-empty HEAD &&
292         git rev-list HEAD > actual &&
293         test_cmp expect actual
294 '
295
296 test_expect_success '--remap-to-ancestor with filename filters' '
297         git checkout master &&
298         git reset --hard A &&
299         test_commit add-foo foo 1 &&
300         git branch moved-foo &&
301         test_commit add-bar bar a &&
302         git branch invariant &&
303         orig_invariant=$(git rev-parse invariant) &&
304         git branch moved-bar &&
305         test_commit change-foo foo 2 &&
306         git filter-branch -f --remap-to-ancestor \
307                 moved-foo moved-bar A..master \
308                 -- -- foo &&
309         test $(git rev-parse moved-foo) = $(git rev-parse moved-bar) &&
310         test $(git rev-parse moved-foo) = $(git rev-parse master^) &&
311         test $orig_invariant = $(git rev-parse invariant)
312 '
313
314 test_expect_success 'automatic remapping to ancestor with filename filters' '
315         git checkout master &&
316         git reset --hard A &&
317         test_commit add-foo2 foo 1 &&
318         git branch moved-foo2 &&
319         test_commit add-bar2 bar a &&
320         git branch invariant2 &&
321         orig_invariant=$(git rev-parse invariant2) &&
322         git branch moved-bar2 &&
323         test_commit change-foo2 foo 2 &&
324         git filter-branch -f \
325                 moved-foo2 moved-bar2 A..master \
326                 -- -- foo &&
327         test $(git rev-parse moved-foo2) = $(git rev-parse moved-bar2) &&
328         test $(git rev-parse moved-foo2) = $(git rev-parse master^) &&
329         test $orig_invariant = $(git rev-parse invariant2)
330 '
331
332 test_expect_success 'setup submodule' '
333         rm -fr ?* .git &&
334         git init &&
335         test_commit file &&
336         mkdir submod &&
337         submodurl="$PWD/submod" &&
338         ( cd submod &&
339           git init &&
340           test_commit file-in-submod ) &&
341         git submodule add "$submodurl" &&
342         git commit -m "added submodule" &&
343         test_commit add-file &&
344         ( cd submod && test_commit add-in-submodule ) &&
345         git add submod &&
346         git commit -m "changed submodule" &&
347         git branch original HEAD
348 '
349
350 orig_head=`git show-ref --hash --head HEAD`
351
352 test_expect_success 'rewrite submodule with another content' '
353         git filter-branch --tree-filter "test -d submod && {
354                                          rm -rf submod &&
355                                          git rm -rf --quiet submod &&
356                                          mkdir submod &&
357                                          : > submod/file
358                                          } || :" HEAD &&
359         test $orig_head != `git show-ref --hash --head HEAD`
360 '
361
362 test_expect_success 'replace submodule revision' '
363         git reset --hard original &&
364         git filter-branch -f --tree-filter \
365             "if git ls-files --error-unmatch -- submod > /dev/null 2>&1
366              then git update-index --cacheinfo 160000 0123456789012345678901234567890123456789 submod
367              fi" HEAD &&
368         test $orig_head != `git show-ref --hash --head HEAD`
369 '
370
371 test_done