3 test_description
='external diff interface test'
5 TEST_PASSES_SANITIZE_LEAK
=true
8 test_expect_success setup
'
13 git commit -m initial &&
17 before=$(git hash-object file) &&
18 before=$(git rev-parse --short $before) &&
20 git commit -m second &&
26 test_expect_success
'GIT_EXTERNAL_DIFF environment' '
28 file $(git rev-parse --verify HEAD:file) 100644 file $(test_oid zero) 100644
30 GIT_EXTERNAL_DIFF=echo git diff >out &&
31 cut -d" " -f1,3- <out >actual &&
32 test_cmp expect actual
36 test_expect_success
'GIT_EXTERNAL_DIFF environment should apply only to diff' '
37 GIT_EXTERNAL_DIFF=echo git log -p -1 HEAD >out &&
38 grep "^diff --git a/file b/file" out
42 test_expect_success
'GIT_EXTERNAL_DIFF environment and --no-ext-diff' '
43 GIT_EXTERNAL_DIFF=echo git diff --no-ext-diff >out &&
44 grep "^diff --git a/file b/file" out
48 test_expect_success SYMLINKS
'typechange diff' '
53 file $(git rev-parse --verify HEAD:file) 100644 $(test_oid zero) 120000
55 GIT_EXTERNAL_DIFF=echo git diff >out &&
56 cut -d" " -f1,3-4,6- <out >actual &&
57 test_cmp expect actual &&
59 GIT_EXTERNAL_DIFF=echo git diff --no-ext-diff >actual &&
61 test_cmp expect actual
64 test_expect_success
'diff.external' '
67 test_config diff.external echo &&
70 file $(git rev-parse --verify HEAD:file) 100644 $(test_oid zero) 100644
73 cut -d" " -f1,3-4,6- <out >actual &&
74 test_cmp expect actual
77 test_expect_success
'diff.external should apply only to diff' '
78 test_config diff.external echo &&
79 git log -p -1 HEAD >out &&
80 grep "^diff --git a/file b/file" out
83 test_expect_success
'diff.external and --no-ext-diff' '
84 test_config diff.external echo &&
85 git diff --no-ext-diff >out &&
86 grep "^diff --git a/file b/file" out
89 test_expect_success
'diff attribute' '
93 git config diff.parrot.command echo &&
95 echo >.gitattributes "file diff=parrot" &&
98 file $(git rev-parse --verify HEAD:file) 100644 $(test_oid zero) 100644
101 cut -d" " -f1,3-4,6- <out >actual &&
102 test_cmp expect actual
105 test_expect_success
!SANITIZE_LEAK
'diff attribute should apply only to diff' '
106 git log -p -1 HEAD >out &&
107 grep "^diff --git a/file b/file" out
111 test_expect_success
'diff attribute and --no-ext-diff' '
112 git diff --no-ext-diff >out &&
113 grep "^diff --git a/file b/file" out
117 test_expect_success
'diff attribute' '
119 git config --unset diff.parrot.command &&
120 git config diff.color.command echo &&
122 echo >.gitattributes "file diff=color" &&
124 cat >expect <<-EOF &&
125 file $(git rev-parse --verify HEAD:file) 100644 $(test_oid zero) 100644
128 cut -d" " -f1,3-4,6- <out >actual &&
129 test_cmp expect actual
132 test_expect_success
!SANITIZE_LEAK
'diff attribute should apply only to diff' '
133 git log -p -1 HEAD >out &&
134 grep "^diff --git a/file b/file" out
138 test_expect_success
'diff attribute and --no-ext-diff' '
139 git diff --no-ext-diff >out &&
140 grep "^diff --git a/file b/file" out
144 test_expect_success
'GIT_EXTERNAL_DIFF trumps diff.external' '
146 test_config diff.external "echo ext-global" &&
148 cat >expect <<-EOF &&
149 ext-env file $(git rev-parse --verify HEAD:file) 100644 file $(test_oid zero) 100644
151 GIT_EXTERNAL_DIFF="echo ext-env" git diff >out &&
152 cut -d" " -f1-2,4- <out >actual &&
153 test_cmp expect actual
156 test_expect_success
'attributes trump GIT_EXTERNAL_DIFF and diff.external' '
157 test_config diff.foo.command "echo ext-attribute" &&
158 test_config diff.external "echo ext-global" &&
159 echo "file diff=foo" >.gitattributes &&
161 cat >expect <<-EOF &&
162 ext-attribute file $(git rev-parse --verify HEAD:file) 100644 file $(test_oid zero) 100644
164 GIT_EXTERNAL_DIFF="echo ext-env" git diff >out &&
165 cut -d" " -f1-2,4- <out >actual &&
166 test_cmp expect actual
169 test_expect_success
'no diff with -diff' '
170 echo >.gitattributes "file -diff" &&
175 check_external_diff
() {
184 command="echo output; exit $command_code;"
185 desc
="external diff '$command' with trustExitCode=$trust_exit_code"
186 with_options
="${options:+ with }$options"
188 test_expect_success
"$desc via attribute$with_options" "
189 test_config diff.foo.command \"$command\" &&
190 test_config diff.foo.trustExitCode $trust_exit_code &&
191 echo \"file diff=foo\" >.gitattributes &&
192 test_expect_code $expect_code git diff $options >out 2>err &&
193 test_cmp $expect_out out &&
194 test_cmp $expect_err err
197 test_expect_success
"$desc via diff.external$with_options" "
198 test_config diff.external \"$command\" &&
199 test_config diff.trustExitCode $trust_exit_code &&
201 test_expect_code $expect_code git diff $options >out 2>err &&
202 test_cmp $expect_out out &&
203 test_cmp $expect_err err
206 test_expect_success
"$desc via GIT_EXTERNAL_DIFF$with_options" "
208 test_expect_code $expect_code env \
209 GIT_EXTERNAL_DIFF=\"$command\" \
210 GIT_EXTERNAL_DIFF_TRUST_EXIT_CODE=$trust_exit_code \
211 git diff $options >out 2>err &&
212 test_cmp $expect_out out &&
213 test_cmp $expect_err err
217 test_expect_success
'setup output files' '
219 echo output >output &&
220 echo "fatal: external diff died, stopping at file" >error
223 check_external_diff
0 output empty
0 off
224 check_external_diff
128 output error
1 off
225 check_external_diff
0 output empty
0 on
226 check_external_diff
0 output empty
1 on
227 check_external_diff
128 output error
2 on
229 check_external_diff
1 output empty
0 off
--exit-code
230 check_external_diff
128 output error
1 off
--exit-code
231 check_external_diff
0 output empty
0 on
--exit-code
232 check_external_diff
1 output empty
1 on
--exit-code
233 check_external_diff
128 output error
2 on
--exit-code
235 check_external_diff
1 empty empty
0 off
--quiet
236 check_external_diff
1 empty empty
1 off
--quiet # we don't even call the program
237 check_external_diff
0 empty empty
0 on
--quiet
238 check_external_diff
1 empty empty
1 on
--quiet
239 check_external_diff
128 empty error
2 on
--quiet
241 echo NULZbetweenZwords | perl
-pe 'y/Z/\000/' > file
243 test_expect_success
'force diff with "diff"' '
244 after=$(git hash-object file) &&
245 after=$(git rev-parse --short $after) &&
246 echo >.gitattributes "file diff" &&
248 sed -e "s/^index .*/index $before..$after 100644/" \
249 "$TEST_DIRECTORY"/t4020/diff.NUL >expected-diff &&
250 test_cmp expected-diff actual
253 test_expect_success
'GIT_EXTERNAL_DIFF with more than one changed files' '
254 echo anotherfile > file2 &&
256 git commit -m "added 2nd file" &&
257 echo modified >file2 &&
258 GIT_EXTERNAL_DIFF=echo git diff
261 test_expect_success
'GIT_EXTERNAL_DIFF path counter/total' '
262 write_script external-diff.sh <<-\EOF &&
263 echo $GIT_DIFF_PATH_COUNTER of $GIT_DIFF_PATH_TOTAL >>counter.txt
266 cat >expect <<-\EOF &&
270 GIT_EXTERNAL_DIFF=./external-diff.sh git diff &&
271 test_cmp expect counter.txt
274 test_expect_success
'GIT_EXTERNAL_DIFF generates pretty paths' '
275 test_when_finished "git rm -f file.ext" &&
278 echo with extension > file.ext &&
280 cat >expect <<-EOF &&
283 GIT_EXTERNAL_DIFF=echo git diff file.ext >out &&
284 basename $(cut -d" " -f2 <out) >actual &&
285 test_cmp expect actual
288 echo "#!$SHELL_PATH" >fake-diff.sh
289 cat >> fake-diff.sh
<<\EOF
292 chmod a
+x fake-diff.sh
298 test_expect_success
'external diff with autocrlf = true' '
299 test_config core.autocrlf true &&
300 GIT_EXTERNAL_DIFF=./fake-diff.sh git diff &&
301 test $(wc -l <crlfed.txt) = $(keep_only_cr <crlfed.txt | wc -c)
304 test_expect_success
'diff --cached' '
305 test_config core.autocrlf true &&
307 git update-index --assume-unchanged file &&
309 git diff --cached >actual &&
310 test_cmp expected-diff actual
313 test_expect_success
'clean up crlf leftovers' '
314 git update-index --no-assume-unchanged file &&
319 test_expect_success
'submodule diff' '
321 ( cd sub && test_commit sub1 ) &&
324 git commit -m "add submodule" &&
325 ( cd sub && test_commit sub2 ) &&
326 write_script gather_pre_post.sh <<-\EOF &&
327 echo "$1 $4" # path, mode
331 GIT_EXTERNAL_DIFF=./gather_pre_post.sh git diff >actual &&
332 cat >expected <<-EOF &&
334 Subproject commit $(git rev-parse HEAD:sub)
335 Subproject commit $(cd sub && git rev-parse HEAD)
337 test_cmp expected actual