Merge branch 'pe/doc-email-env-is-trumped-by-config' into maint
[git.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         (sane_unset GIT_AUTHOR_NAME &&
171          git filter-branch -f --msg-filter "cat; \
172                         test \$GIT_COMMIT != $(git rev-parse master) || \
173                         echo Hallo" \
174                 preserved-author) &&
175         test 1 = $(git rev-list --author="B V Uips" preserved-author | wc -l)
176 '
177
178 test_expect_success "remove a certain author's commits" '
179         echo i > i &&
180         test_tick &&
181         git commit -m i i &&
182         git branch removed-author &&
183         git filter-branch -f --commit-filter "\
184                 if [ \"\$GIT_AUTHOR_NAME\" = \"B V Uips\" ];\
185                 then\
186                         skip_commit \"\$@\";
187                 else\
188                         git commit-tree \"\$@\";\
189                 fi" removed-author &&
190         cnt1=$(git rev-list master | wc -l) &&
191         cnt2=$(git rev-list removed-author | wc -l) &&
192         test $cnt1 -eq $(($cnt2 + 1)) &&
193         test 0 = $(git rev-list --author="B V Uips" removed-author | wc -l)
194 '
195
196 test_expect_success 'barf on invalid name' '
197         test_must_fail git filter-branch -f master xy-problem &&
198         test_must_fail git filter-branch -f HEAD^
199 '
200
201 test_expect_success '"map" works in commit filter' '
202         git filter-branch -f --commit-filter "\
203                 parent=\$(git rev-parse \$GIT_COMMIT^) &&
204                 mapped=\$(map \$parent) &&
205                 actual=\$(echo \"\$@\" | sed \"s/^.*-p //\") &&
206                 test \$mapped = \$actual &&
207                 git commit-tree \"\$@\";" master~2..master &&
208         git rev-parse --verify master
209 '
210
211 test_expect_success 'Name needing quotes' '
212
213         git checkout -b rerere A &&
214         mkdir foo &&
215         name="れれれ" &&
216         >foo/$name &&
217         git add foo &&
218         git commit -m "Adding a file" &&
219         git filter-branch --tree-filter "rm -fr foo" &&
220         test_must_fail git ls-files --error-unmatch "foo/$name" &&
221         test $(git rev-parse --verify rerere) != $(git rev-parse --verify A)
222
223 '
224
225 test_expect_success 'Subdirectory filter with disappearing trees' '
226         git reset --hard &&
227         git checkout master &&
228
229         mkdir foo &&
230         touch foo/bar &&
231         git add foo &&
232         test_tick &&
233         git commit -m "Adding foo" &&
234
235         git rm -r foo &&
236         test_tick &&
237         git commit -m "Removing foo" &&
238
239         mkdir foo &&
240         touch foo/bar &&
241         git add foo &&
242         test_tick &&
243         git commit -m "Re-adding foo" &&
244
245         git filter-branch -f --subdirectory-filter foo &&
246         test $(git rev-list master | wc -l) = 3
247 '
248
249 test_expect_success 'Tag name filtering retains tag message' '
250         git tag -m atag T &&
251         git cat-file tag T > expect &&
252         git filter-branch -f --tag-name-filter cat &&
253         git cat-file tag T > actual &&
254         test_cmp expect actual
255 '
256
257 faux_gpg_tag='object XXXXXX
258 type commit
259 tag S
260 tagger T A Gger <tagger@example.com> 1206026339 -0500
261
262 This is a faux gpg signed tag.
263 -----BEGIN PGP SIGNATURE-----
264 Version: FauxGPG v0.0.0 (FAUX/Linux)
265
266 gdsfoewhxu/6l06f1kxyxhKdZkrcbaiOMtkJUA9ITAc1mlamh0ooasxkH1XwMbYQ
267 acmwXaWET20H0GeAGP+7vow=
268 =agpO
269 -----END PGP SIGNATURE-----
270 '
271 test_expect_success 'Tag name filtering strips gpg signature' '
272         sha1=$(git rev-parse HEAD) &&
273         sha1t=$(echo "$faux_gpg_tag" | sed -e s/XXXXXX/$sha1/ | git mktag) &&
274         git update-ref "refs/tags/S" "$sha1t" &&
275         echo "$faux_gpg_tag" | sed -e s/XXXXXX/$sha1/ | head -n 6 > expect &&
276         git filter-branch -f --tag-name-filter cat &&
277         git cat-file tag S > actual &&
278         test_cmp expect actual
279 '
280
281 test_expect_success 'Tag name filtering allows slashes in tag names' '
282         git tag -m tag-with-slash X/1 &&
283         git cat-file tag X/1 | sed -e s,X/1,X/2, > expect &&
284         git filter-branch -f --tag-name-filter "echo X/2" &&
285         git cat-file tag X/2 > actual &&
286         test_cmp expect actual
287 '
288
289 test_expect_success 'Prune empty commits' '
290         git rev-list HEAD > expect &&
291         test_commit to_remove &&
292         git filter-branch -f --index-filter "git update-index --remove to_remove.t" --prune-empty HEAD &&
293         git rev-list HEAD > actual &&
294         test_cmp expect actual
295 '
296
297 test_expect_success '--remap-to-ancestor with filename filters' '
298         git checkout master &&
299         git reset --hard A &&
300         test_commit add-foo foo 1 &&
301         git branch moved-foo &&
302         test_commit add-bar bar a &&
303         git branch invariant &&
304         orig_invariant=$(git rev-parse invariant) &&
305         git branch moved-bar &&
306         test_commit change-foo foo 2 &&
307         git filter-branch -f --remap-to-ancestor \
308                 moved-foo moved-bar A..master \
309                 -- -- foo &&
310         test $(git rev-parse moved-foo) = $(git rev-parse moved-bar) &&
311         test $(git rev-parse moved-foo) = $(git rev-parse master^) &&
312         test $orig_invariant = $(git rev-parse invariant)
313 '
314
315 test_expect_success 'automatic remapping to ancestor with filename filters' '
316         git checkout master &&
317         git reset --hard A &&
318         test_commit add-foo2 foo 1 &&
319         git branch moved-foo2 &&
320         test_commit add-bar2 bar a &&
321         git branch invariant2 &&
322         orig_invariant=$(git rev-parse invariant2) &&
323         git branch moved-bar2 &&
324         test_commit change-foo2 foo 2 &&
325         git filter-branch -f \
326                 moved-foo2 moved-bar2 A..master \
327                 -- -- foo &&
328         test $(git rev-parse moved-foo2) = $(git rev-parse moved-bar2) &&
329         test $(git rev-parse moved-foo2) = $(git rev-parse master^) &&
330         test $orig_invariant = $(git rev-parse invariant2)
331 '
332
333 test_expect_success 'setup submodule' '
334         rm -fr ?* .git &&
335         git init &&
336         test_commit file &&
337         mkdir submod &&
338         submodurl="$PWD/submod" &&
339         ( cd submod &&
340           git init &&
341           test_commit file-in-submod ) &&
342         git submodule add "$submodurl" &&
343         git commit -m "added submodule" &&
344         test_commit add-file &&
345         ( cd submod && test_commit add-in-submodule ) &&
346         git add submod &&
347         git commit -m "changed submodule" &&
348         git branch original HEAD
349 '
350
351 orig_head=`git show-ref --hash --head HEAD`
352
353 test_expect_success 'rewrite submodule with another content' '
354         git filter-branch --tree-filter "test -d submod && {
355                                          rm -rf submod &&
356                                          git rm -rf --quiet submod &&
357                                          mkdir submod &&
358                                          : > submod/file
359                                          } || :" HEAD &&
360         test $orig_head != `git show-ref --hash --head HEAD`
361 '
362
363 test_expect_success 'replace submodule revision' '
364         git reset --hard original &&
365         git filter-branch -f --tree-filter \
366             "if git ls-files --error-unmatch -- submod > /dev/null 2>&1
367              then git update-index --cacheinfo 160000 0123456789012345678901234567890123456789 submod
368              fi" HEAD &&
369         test $orig_head != `git show-ref --hash --head HEAD`
370 '
371
372 test_done