Raw
1 #!/bin/sh
2
3 test_description='external diff interface test'
4
5 . ./test-lib.sh
6
7 test_expect_success setup '
8
9 test_tick &&
10 echo initial >file &&
11 git add file &&
12 git commit -m initial &&
13
14 test_tick &&
15 echo second >file &&
16 before=$(git hash-object file) &&
17 before=$(git rev-parse --short $before) &&
18 git add file &&
19 git commit -m second &&
20
21 test_tick &&
22 echo third >file
23 '
24
25 test_expect_success 'GIT_EXTERNAL_DIFF environment' '
26 cat >expect <<-EOF &&
27 file $(git rev-parse --verify HEAD:file) 100644 file $(test_oid zero) 100644
28 EOF
29 GIT_EXTERNAL_DIFF=echo git diff >out &&
30 cut -d" " -f1,3- <out >actual &&
31 test_cmp expect actual
32
33 '
34
35 test_expect_success 'GIT_EXTERNAL_DIFF environment should apply only to diff' '
36 GIT_EXTERNAL_DIFF=echo git log -p -1 HEAD >out &&
37 test_grep "^diff --git a/file b/file" out
38
39 '
40
41 test_expect_success 'GIT_EXTERNAL_DIFF environment and --no-ext-diff' '
42 GIT_EXTERNAL_DIFF=echo git diff --no-ext-diff >out &&
43 test_grep "^diff --git a/file b/file" out
44
45 '
46
47 test_expect_success 'GIT_EXTERNAL_DIFF and --output' '
48 cat >expect <<-EOF &&
49 file $(git rev-parse --verify HEAD:file) 100644 file $(test_oid zero) 100644
50 EOF
51 GIT_EXTERNAL_DIFF=echo git diff --output=out >stdout &&
52 cut -d" " -f1,3- <out >actual &&
53 test_must_be_empty stdout &&
54 test_cmp expect actual
55 '
56
57 test_expect_success SYMLINKS 'typechange diff' '
58 rm -f file &&
59 ln -s elif file &&
60
61 cat >expect <<-EOF &&
62 file $(git rev-parse --verify HEAD:file) 100644 $(test_oid zero) 120000
63 EOF
64 GIT_EXTERNAL_DIFF=echo git diff >out &&
65 cut -d" " -f1,3-4,6- <out >actual &&
66 test_cmp expect actual &&
67
68 GIT_EXTERNAL_DIFF=echo git diff --no-ext-diff >actual &&
69 git diff >expect &&
70 test_cmp expect actual
71 '
72
73 test_expect_success 'diff.external' '
74 git reset --hard &&
75 echo third >file &&
76 test_config diff.external echo &&
77
78 cat >expect <<-EOF &&
79 file $(git rev-parse --verify HEAD:file) 100644 $(test_oid zero) 100644
80 EOF
81 git diff >out &&
82 cut -d" " -f1,3-4,6- <out >actual &&
83 test_cmp expect actual
84 '
85
86 test_expect_success 'diff.external should apply only to diff' '
87 test_config diff.external echo &&
88 git log -p -1 HEAD >out &&
89 test_grep "^diff --git a/file b/file" out
90 '
91
92 test_expect_success 'diff.external and --no-ext-diff' '
93 test_config diff.external echo &&
94 git diff --no-ext-diff >out &&
95 test_grep "^diff --git a/file b/file" out
96 '
97
98 test_expect_success 'diff attribute' '
99 git reset --hard &&
100 echo third >file &&
101
102 git config diff.parrot.command echo &&
103
104 echo >.gitattributes "file diff=parrot" &&
105
106 cat >expect <<-EOF &&
107 file $(git rev-parse --verify HEAD:file) 100644 $(test_oid zero) 100644
108 EOF
109 git diff >out &&
110 cut -d" " -f1,3-4,6- <out >actual &&
111 test_cmp expect actual
112 '
113
114 test_expect_success 'diff attribute should apply only to diff' '
115 git log -p -1 HEAD >out &&
116 test_grep "^diff --git a/file b/file" out
117
118 '
119
120 test_expect_success 'diff attribute and --no-ext-diff' '
121 git diff --no-ext-diff >out &&
122 test_grep "^diff --git a/file b/file" out
123
124 '
125
126 test_expect_success 'diff attribute' '
127
128 git config --unset diff.parrot.command &&
129 git config diff.color.command echo &&
130
131 echo >.gitattributes "file diff=color" &&
132
133 cat >expect <<-EOF &&
134 file $(git rev-parse --verify HEAD:file) 100644 $(test_oid zero) 100644
135 EOF
136 git diff >out &&
137 cut -d" " -f1,3-4,6- <out >actual &&
138 test_cmp expect actual
139 '
140
141 test_expect_success 'diff attribute should apply only to diff' '
142 git log -p -1 HEAD >out &&
143 test_grep "^diff --git a/file b/file" out
144
145 '
146
147 test_expect_success 'diff attribute and --no-ext-diff' '
148 git diff --no-ext-diff >out &&
149 test_grep "^diff --git a/file b/file" out
150
151 '
152
153 test_expect_success 'GIT_EXTERNAL_DIFF trumps diff.external' '
154 >.gitattributes &&
155 test_config diff.external "echo ext-global" &&
156
157 cat >expect <<-EOF &&
158 ext-env file $(git rev-parse --verify HEAD:file) 100644 file $(test_oid zero) 100644
159 EOF
160 GIT_EXTERNAL_DIFF="echo ext-env" git diff >out &&
161 cut -d" " -f1-2,4- <out >actual &&
162 test_cmp expect actual
163 '
164
165 test_expect_success 'attributes trump GIT_EXTERNAL_DIFF and diff.external' '
166 test_config diff.foo.command "echo ext-attribute" &&
167 test_config diff.external "echo ext-global" &&
168 echo "file diff=foo" >.gitattributes &&
169
170 cat >expect <<-EOF &&
171 ext-attribute file $(git rev-parse --verify HEAD:file) 100644 file $(test_oid zero) 100644
172 EOF
173 GIT_EXTERNAL_DIFF="echo ext-env" git diff >out &&
174 cut -d" " -f1-2,4- <out >actual &&
175 test_cmp expect actual
176 '
177
178 test_expect_success 'no diff with -diff' '
179 echo >.gitattributes "file -diff" &&
180 git diff >out &&
181 test_grep Binary out
182 '
183
184 check_external_diff () {
185 expect_code=$1
186 expect_out=$2
187 expect_err=$3
188 command_code=$4
189 trust_exit_code=$5
190 shift 5
191 options="$@"
192
193 command="echo output; exit $command_code;"
194 desc="external diff '$command' with trustExitCode=$trust_exit_code"
195 with_options="${options:+ with }$options"
196
197 test_expect_success "$desc via attribute$with_options" "
198 test_config diff.foo.command \"$command\" &&
199 test_config diff.foo.trustExitCode $trust_exit_code &&
200 echo \"file diff=foo\" >.gitattributes &&
201 test_expect_code $expect_code git diff $options >out 2>err &&
202 test_cmp $expect_out out &&
203 test_cmp $expect_err err
204 "
205
206 test_expect_success "$desc via diff.external$with_options" "
207 test_config diff.external \"$command\" &&
208 test_config diff.trustExitCode $trust_exit_code &&
209 >.gitattributes &&
210 test_expect_code $expect_code git diff $options >out 2>err &&
211 test_cmp $expect_out out &&
212 test_cmp $expect_err err
213 "
214
215 test_expect_success "$desc via GIT_EXTERNAL_DIFF$with_options" "
216 >.gitattributes &&
217 test_expect_code $expect_code env \
218 GIT_EXTERNAL_DIFF=\"$command\" \
219 GIT_EXTERNAL_DIFF_TRUST_EXIT_CODE=$trust_exit_code \
220 git diff $options >out 2>err &&
221 test_cmp $expect_out out &&
222 test_cmp $expect_err err
223 "
224 }
225
226 test_expect_success 'setup output files' '
227 : >empty &&
228 echo output >output &&
229 echo "fatal: external diff died, stopping at file" >error
230 '
231
232 check_external_diff 0 output empty 0 off
233 check_external_diff 128 output error 1 off
234 check_external_diff 0 output empty 0 on
235 check_external_diff 0 output empty 1 on
236 check_external_diff 128 output error 2 on
237
238 check_external_diff 1 output empty 0 off --exit-code
239 check_external_diff 128 output error 1 off --exit-code
240 check_external_diff 0 output empty 0 on --exit-code
241 check_external_diff 1 output empty 1 on --exit-code
242 check_external_diff 128 output error 2 on --exit-code
243
244 check_external_diff 1 empty empty 0 off --quiet
245 check_external_diff 1 empty empty 1 off --quiet # we don't even call the program
246 check_external_diff 0 empty empty 0 on --quiet
247 check_external_diff 1 empty empty 1 on --quiet
248 check_external_diff 128 empty error 2 on --quiet
249
250 echo NULZbetweenZwords | tr "Z" "\000" > file
251
252 test_expect_success 'force diff with "diff"' '
253 after=$(git hash-object file) &&
254 after=$(git rev-parse --short $after) &&
255 echo >.gitattributes "file diff" &&
256 git diff >actual &&
257 sed -e "s/^index .*/index $before..$after 100644/" \
258 "$TEST_DIRECTORY"/t4020/diff.NUL >expected-diff &&
259 test_cmp expected-diff actual
260 '
261
262 test_expect_success 'GIT_EXTERNAL_DIFF with more than one changed files' '
263 echo anotherfile > file2 &&
264 git add file2 &&
265 git commit -m "added 2nd file" &&
266 echo modified >file2 &&
267 GIT_EXTERNAL_DIFF=echo git diff
268 '
269
270 test_expect_success 'GIT_EXTERNAL_DIFF path counter/total' '
271 write_script external-diff.sh <<-\EOF &&
272 echo $GIT_DIFF_PATH_COUNTER of $GIT_DIFF_PATH_TOTAL >>counter.txt
273 EOF
274 >counter.txt &&
275 cat >expect <<-\EOF &&
276 1 of 2
277 2 of 2
278 EOF
279 GIT_EXTERNAL_DIFF=./external-diff.sh git diff &&
280 test_cmp expect counter.txt
281 '
282
283 test_expect_success 'GIT_EXTERNAL_DIFF generates pretty paths' '
284 test_when_finished "git rm -f file.ext" &&
285 touch file.ext &&
286 git add file.ext &&
287 echo with extension > file.ext &&
288
289 cat >expect <<-EOF &&
290 file.ext
291 EOF
292 GIT_EXTERNAL_DIFF=echo git diff file.ext >out &&
293 basename $(cut -d" " -f2 <out) >actual &&
294 test_cmp expect actual
295 '
296
297 echo "#!$SHELL_PATH" >fake-diff.sh
298 cat >> fake-diff.sh <<\EOF
299 cat $2 >> crlfed.txt
300 EOF
301 chmod a+x fake-diff.sh
302
303 keep_only_cr () {
304 tr -dc '\015'
305 }
306
307 test_expect_success 'external diff with autocrlf = true' '
308 test_config core.autocrlf true &&
309 GIT_EXTERNAL_DIFF=./fake-diff.sh git diff &&
310 test $(wc -l <crlfed.txt) = $(keep_only_cr <crlfed.txt | wc -c)
311 '
312
313 test_expect_success 'diff --cached' '
314 test_config core.autocrlf true &&
315 git add file &&
316 git update-index --assume-unchanged file &&
317 echo second >file &&
318 git diff --cached >actual &&
319 test_cmp expected-diff actual
320 '
321
322 test_expect_success 'clean up crlf leftovers' '
323 git update-index --no-assume-unchanged file &&
324 rm -f file* &&
325 git reset --hard
326 '
327
328 test_expect_success 'submodule diff' '
329 git init sub &&
330 ( cd sub && test_commit sub1 ) &&
331 git add sub &&
332 test_tick &&
333 git commit -m "add submodule" &&
334 ( cd sub && test_commit sub2 ) &&
335 write_script gather_pre_post.sh <<-\EOF &&
336 echo "$1 $4" # path, mode
337 cat "$2" # old file
338 cat "$5" # new file
339 EOF
340 GIT_EXTERNAL_DIFF=./gather_pre_post.sh git diff >actual &&
341 cat >expected <<-EOF &&
342 sub 160000
343 Subproject commit $(git rev-parse HEAD:sub)
344 Subproject commit $(cd sub && git rev-parse HEAD)
345 EOF
346 test_cmp expected actual
347 '
348
349 test_done