Merge branch 'js/status-pre-rebase-i'
[git] / t / t7800-difftool.sh
1 #!/bin/sh
2 #
3 # Copyright (c) 2009, 2010, 2012, 2013 David Aguilar
4 #
5
6 test_description='git-difftool
7
8 Testing basic diff tool invocation
9 '
10
11 . ./test-lib.sh
12
13 difftool_test_setup ()
14 {
15         test_config diff.tool test-tool &&
16         test_config difftool.test-tool.cmd 'cat "$LOCAL"' &&
17         test_config difftool.bogus-tool.cmd false
18 }
19
20 prompt_given ()
21 {
22         prompt="$1"
23         test "$prompt" = "Launch 'test-tool' [Y/n]? branch"
24 }
25
26 # Create a file on master and change it on branch
27 test_expect_success 'setup' '
28         echo master >file &&
29         git add file &&
30         git commit -m "added file" &&
31
32         git checkout -b branch master &&
33         echo branch >file &&
34         git commit -a -m "branch changed file" &&
35         git checkout master
36 '
37
38 # Configure a custom difftool.<tool>.cmd and use it
39 test_expect_success 'custom commands' '
40         difftool_test_setup &&
41         test_config difftool.test-tool.cmd "cat \"\$REMOTE\"" &&
42         echo master >expect &&
43         git difftool --no-prompt branch >actual &&
44         test_cmp expect actual &&
45
46         test_config difftool.test-tool.cmd "cat \"\$LOCAL\"" &&
47         echo branch >expect &&
48         git difftool --no-prompt branch >actual &&
49         test_cmp expect actual
50 '
51
52 test_expect_success 'custom tool commands override built-ins' '
53         test_config difftool.vimdiff.cmd "cat \"\$REMOTE\"" &&
54         echo master >expect &&
55         git difftool --tool vimdiff --no-prompt branch >actual &&
56         test_cmp expect actual
57 '
58
59 test_expect_success 'difftool ignores bad --tool values' '
60         : >expect &&
61         test_must_fail \
62                 git difftool --no-prompt --tool=bad-tool branch >actual &&
63         test_cmp expect actual
64 '
65
66 test_expect_success 'difftool forwards arguments to diff' '
67         difftool_test_setup &&
68         >for-diff &&
69         git add for-diff &&
70         echo changes>for-diff &&
71         git add for-diff &&
72         : >expect &&
73         git difftool --cached --no-prompt -- for-diff >actual &&
74         test_cmp expect actual &&
75         git reset -- for-diff &&
76         rm for-diff
77 '
78
79 test_expect_success 'difftool ignores exit code' '
80         test_config difftool.error.cmd false &&
81         git difftool -y -t error branch
82 '
83
84 test_expect_success 'difftool forwards exit code with --trust-exit-code' '
85         test_config difftool.error.cmd false &&
86         test_must_fail git difftool -y --trust-exit-code -t error branch
87 '
88
89 test_expect_success 'difftool forwards exit code with --trust-exit-code for built-ins' '
90         test_config difftool.vimdiff.path false &&
91         test_must_fail git difftool -y --trust-exit-code -t vimdiff branch
92 '
93
94 test_expect_success 'difftool honors difftool.trustExitCode = true' '
95         test_config difftool.error.cmd false &&
96         test_config difftool.trustExitCode true &&
97         test_must_fail git difftool -y -t error branch
98 '
99
100 test_expect_success 'difftool honors difftool.trustExitCode = false' '
101         test_config difftool.error.cmd false &&
102         test_config difftool.trustExitCode false &&
103         git difftool -y -t error branch
104 '
105
106 test_expect_success 'difftool ignores exit code with --no-trust-exit-code' '
107         test_config difftool.error.cmd false &&
108         test_config difftool.trustExitCode true &&
109         git difftool -y --no-trust-exit-code -t error branch
110 '
111
112 test_expect_success 'difftool stops on error with --trust-exit-code' '
113         test_when_finished "rm -f for-diff .git/fail-right-file" &&
114         test_when_finished "git reset -- for-diff" &&
115         write_script .git/fail-right-file <<-\EOF &&
116         echo "$2"
117         exit 1
118         EOF
119         >for-diff &&
120         git add for-diff &&
121         echo file >expect &&
122         test_must_fail git difftool -y --trust-exit-code \
123                 --extcmd .git/fail-right-file branch >actual &&
124         test_cmp expect actual
125 '
126
127 test_expect_success 'difftool honors exit status if command not found' '
128         test_config difftool.nonexistent.cmd i-dont-exist &&
129         test_config difftool.trustExitCode false &&
130         test_must_fail git difftool -y -t nonexistent branch
131 '
132
133 test_expect_success 'difftool honors --gui' '
134         difftool_test_setup &&
135         test_config merge.tool bogus-tool &&
136         test_config diff.tool bogus-tool &&
137         test_config diff.guitool test-tool &&
138
139         echo branch >expect &&
140         git difftool --no-prompt --gui branch >actual &&
141         test_cmp expect actual
142 '
143
144 test_expect_success 'difftool --gui last setting wins' '
145         difftool_test_setup &&
146         : >expect &&
147         git difftool --no-prompt --gui --no-gui >actual &&
148         test_cmp expect actual &&
149
150         test_config merge.tool bogus-tool &&
151         test_config diff.tool bogus-tool &&
152         test_config diff.guitool test-tool &&
153         echo branch >expect &&
154         git difftool --no-prompt --no-gui --gui branch >actual &&
155         test_cmp expect actual
156 '
157
158 test_expect_success 'difftool --gui works without configured diff.guitool' '
159         difftool_test_setup &&
160         echo branch >expect &&
161         git difftool --no-prompt --gui branch >actual &&
162         test_cmp expect actual
163 '
164
165 # Specify the diff tool using $GIT_DIFF_TOOL
166 test_expect_success 'GIT_DIFF_TOOL variable' '
167         difftool_test_setup &&
168         git config --unset diff.tool &&
169         echo branch >expect &&
170         GIT_DIFF_TOOL=test-tool git difftool --no-prompt branch >actual &&
171         test_cmp expect actual
172 '
173
174 # Test the $GIT_*_TOOL variables and ensure
175 # that $GIT_DIFF_TOOL always wins unless --tool is specified
176 test_expect_success 'GIT_DIFF_TOOL overrides' '
177         difftool_test_setup &&
178         test_config diff.tool bogus-tool &&
179         test_config merge.tool bogus-tool &&
180
181         echo branch >expect &&
182         GIT_DIFF_TOOL=test-tool git difftool --no-prompt branch >actual &&
183         test_cmp expect actual &&
184
185         test_config diff.tool bogus-tool &&
186         test_config merge.tool bogus-tool &&
187         GIT_DIFF_TOOL=bogus-tool \
188                 git difftool --no-prompt --tool=test-tool branch >actual &&
189         test_cmp expect actual
190 '
191
192 # Test that we don't have to pass --no-prompt to difftool
193 # when $GIT_DIFFTOOL_NO_PROMPT is true
194 test_expect_success 'GIT_DIFFTOOL_NO_PROMPT variable' '
195         difftool_test_setup &&
196         echo branch >expect &&
197         GIT_DIFFTOOL_NO_PROMPT=true git difftool branch >actual &&
198         test_cmp expect actual
199 '
200
201 # git-difftool supports the difftool.prompt variable.
202 # Test that GIT_DIFFTOOL_PROMPT can override difftool.prompt = false
203 test_expect_success 'GIT_DIFFTOOL_PROMPT variable' '
204         difftool_test_setup &&
205         test_config difftool.prompt false &&
206         echo >input &&
207         GIT_DIFFTOOL_PROMPT=true git difftool branch <input >output &&
208         prompt=$(tail -1 <output) &&
209         prompt_given "$prompt"
210 '
211
212 # Test that we don't have to pass --no-prompt when difftool.prompt is false
213 test_expect_success 'difftool.prompt config variable is false' '
214         difftool_test_setup &&
215         test_config difftool.prompt false &&
216         echo branch >expect &&
217         git difftool branch >actual &&
218         test_cmp expect actual
219 '
220
221 # Test that we don't have to pass --no-prompt when mergetool.prompt is false
222 test_expect_success 'difftool merge.prompt = false' '
223         difftool_test_setup &&
224         test_might_fail git config --unset difftool.prompt &&
225         test_config mergetool.prompt false &&
226         echo branch >expect &&
227         git difftool branch >actual &&
228         test_cmp expect actual
229 '
230
231 # Test that the -y flag can override difftool.prompt = true
232 test_expect_success 'difftool.prompt can overridden with -y' '
233         difftool_test_setup &&
234         test_config difftool.prompt true &&
235         echo branch >expect &&
236         git difftool -y branch >actual &&
237         test_cmp expect actual
238 '
239
240 # Test that the --prompt flag can override difftool.prompt = false
241 test_expect_success 'difftool.prompt can overridden with --prompt' '
242         difftool_test_setup &&
243         test_config difftool.prompt false &&
244         echo >input &&
245         git difftool --prompt branch <input >output &&
246         prompt=$(tail -1 <output) &&
247         prompt_given "$prompt"
248 '
249
250 # Test that the last flag passed on the command-line wins
251 test_expect_success 'difftool last flag wins' '
252         difftool_test_setup &&
253         echo branch >expect &&
254         git difftool --prompt --no-prompt branch >actual &&
255         test_cmp expect actual &&
256         echo >input &&
257         git difftool --no-prompt --prompt branch <input >output &&
258         prompt=$(tail -1 <output) &&
259         prompt_given "$prompt"
260 '
261
262 # git-difftool falls back to git-mergetool config variables
263 # so test that behavior here
264 test_expect_success 'difftool + mergetool config variables' '
265         test_config merge.tool test-tool &&
266         test_config mergetool.test-tool.cmd "cat \$LOCAL" &&
267         echo branch >expect &&
268         git difftool --no-prompt branch >actual &&
269         test_cmp expect actual &&
270
271         # set merge.tool to something bogus, diff.tool to test-tool
272         test_config merge.tool bogus-tool &&
273         test_config diff.tool test-tool &&
274         git difftool --no-prompt branch >actual &&
275         test_cmp expect actual
276 '
277
278 test_expect_success 'difftool.<tool>.path' '
279         test_config difftool.tkdiff.path echo &&
280         git difftool --tool=tkdiff --no-prompt branch >output &&
281         lines=$(grep file output | wc -l) &&
282         test "$lines" -eq 1
283 '
284
285 test_expect_success 'difftool --extcmd=cat' '
286         echo branch >expect &&
287         echo master >>expect &&
288         git difftool --no-prompt --extcmd=cat branch >actual &&
289         test_cmp expect actual
290 '
291
292 test_expect_success 'difftool --extcmd cat' '
293         echo branch >expect &&
294         echo master >>expect &&
295         git difftool --no-prompt --extcmd=cat branch >actual &&
296         test_cmp expect actual
297 '
298
299 test_expect_success 'difftool -x cat' '
300         echo branch >expect &&
301         echo master >>expect &&
302         git difftool --no-prompt -x cat branch >actual &&
303         test_cmp expect actual
304 '
305
306 test_expect_success 'difftool --extcmd echo arg1' '
307         echo file >expect &&
308         git difftool --no-prompt \
309                 --extcmd sh\ -c\ \"echo\ \$1\" branch >actual &&
310         test_cmp expect actual
311 '
312
313 test_expect_success 'difftool --extcmd cat arg1' '
314         echo master >expect &&
315         git difftool --no-prompt \
316                 --extcmd sh\ -c\ \"cat\ \$1\" branch >actual &&
317         test_cmp expect actual
318 '
319
320 test_expect_success 'difftool --extcmd cat arg2' '
321         echo branch >expect &&
322         git difftool --no-prompt \
323                 --extcmd sh\ -c\ \"cat\ \$2\" branch >actual &&
324         test_cmp expect actual
325 '
326
327 # Create a second file on master and a different version on branch
328 test_expect_success 'setup with 2 files different' '
329         echo m2 >file2 &&
330         git add file2 &&
331         git commit -m "added file2" &&
332
333         git checkout branch &&
334         echo br2 >file2 &&
335         git add file2 &&
336         git commit -a -m "branch changed file2" &&
337         git checkout master
338 '
339
340 test_expect_success 'say no to the first file' '
341         (echo n && echo) >input &&
342         git difftool -x cat branch <input >output &&
343         grep m2 output &&
344         grep br2 output &&
345         ! grep master output &&
346         ! grep branch output
347 '
348
349 test_expect_success 'say no to the second file' '
350         (echo && echo n) >input &&
351         git difftool -x cat branch <input >output &&
352         grep master output &&
353         grep branch output &&
354         ! grep m2 output &&
355         ! grep br2 output
356 '
357
358 test_expect_success 'ending prompt input with EOF' '
359         git difftool -x cat branch </dev/null >output &&
360         ! grep master output &&
361         ! grep branch output &&
362         ! grep m2 output &&
363         ! grep br2 output
364 '
365
366 test_expect_success 'difftool --tool-help' '
367         git difftool --tool-help >output &&
368         grep tool output
369 '
370
371 test_expect_success 'setup change in subdirectory' '
372         git checkout master &&
373         mkdir sub &&
374         echo master >sub/sub &&
375         git add sub/sub &&
376         git commit -m "added sub/sub" &&
377         git tag v1 &&
378         echo test >>file &&
379         echo test >>sub/sub &&
380         git add file sub/sub &&
381         git commit -m "modified both"
382 '
383
384 run_dir_diff_test () {
385         test_expect_success "$1 --no-symlinks" "
386                 symlinks=--no-symlinks &&
387                 $2
388         "
389         test_expect_success SYMLINKS "$1 --symlinks" "
390                 symlinks=--symlinks &&
391                 $2
392         "
393 }
394
395 run_dir_diff_test 'difftool -d' '
396         git difftool -d $symlinks --extcmd ls branch >output &&
397         grep sub output &&
398         grep file output
399 '
400
401 run_dir_diff_test 'difftool --dir-diff' '
402         git difftool --dir-diff $symlinks --extcmd ls branch >output &&
403         grep sub output &&
404         grep file output
405 '
406
407 run_dir_diff_test 'difftool --dir-diff ignores --prompt' '
408         git difftool --dir-diff $symlinks --prompt --extcmd ls branch >output &&
409         grep sub output &&
410         grep file output
411 '
412
413 run_dir_diff_test 'difftool --dir-diff branch from subdirectory' '
414         (
415                 cd sub &&
416                 git difftool --dir-diff $symlinks --extcmd ls branch >output &&
417                 # "sub" must only exist in "right"
418                 # "file" and "file2" must be listed in both "left" and "right"
419                 test "1" = $(grep sub output | wc -l) &&
420                 test "2" = $(grep file"$" output | wc -l) &&
421                 test "2" = $(grep file2 output | wc -l)
422         )
423 '
424
425 run_dir_diff_test 'difftool --dir-diff v1 from subdirectory' '
426         (
427                 cd sub &&
428                 git difftool --dir-diff $symlinks --extcmd ls v1 >output &&
429                 # "sub" and "file" exist in both v1 and HEAD.
430                 # "file2" is unchanged.
431                 test "2" = $(grep sub output | wc -l) &&
432                 test "2" = $(grep file output | wc -l) &&
433                 test "0" = $(grep file2 output | wc -l)
434         )
435 '
436
437 run_dir_diff_test 'difftool --dir-diff branch from subdirectory w/ pathspec' '
438         (
439                 cd sub &&
440                 git difftool --dir-diff $symlinks --extcmd ls branch -- .>output &&
441                 # "sub" only exists in "right"
442                 # "file" and "file2" must not be listed
443                 test "1" = $(grep sub output | wc -l) &&
444                 test "0" = $(grep file output | wc -l)
445         )
446 '
447
448 run_dir_diff_test 'difftool --dir-diff v1 from subdirectory w/ pathspec' '
449         (
450                 cd sub &&
451                 git difftool --dir-diff $symlinks --extcmd ls v1 -- .>output &&
452                 # "sub" exists in v1 and HEAD
453                 # "file" is filtered out by the pathspec
454                 test "2" = $(grep sub output | wc -l) &&
455                 test "0" = $(grep file output | wc -l)
456         )
457 '
458
459 run_dir_diff_test 'difftool --dir-diff from subdirectory with GIT_DIR set' '
460         (
461                 GIT_DIR=$(pwd)/.git &&
462                 export GIT_DIR &&
463                 GIT_WORK_TREE=$(pwd) &&
464                 export GIT_WORK_TREE &&
465                 cd sub &&
466                 git difftool --dir-diff $symlinks --extcmd ls \
467                         branch -- sub >output &&
468                 grep sub output &&
469                 ! grep file output
470         )
471 '
472
473 run_dir_diff_test 'difftool --dir-diff when worktree file is missing' '
474         test_when_finished git reset --hard &&
475         rm file2 &&
476         git difftool --dir-diff $symlinks --extcmd ls branch master >output &&
477         grep file2 output
478 '
479
480 run_dir_diff_test 'difftool --dir-diff with unmerged files' '
481         test_when_finished git reset --hard &&
482         test_config difftool.echo.cmd "echo ok" &&
483         git checkout -B conflict-a &&
484         git checkout -B conflict-b &&
485         git checkout conflict-a &&
486         echo a >>file &&
487         git add file &&
488         git commit -m conflict-a &&
489         git checkout conflict-b &&
490         echo b >>file &&
491         git add file &&
492         git commit -m conflict-b &&
493         git checkout master &&
494         git merge conflict-a &&
495         test_must_fail git merge conflict-b &&
496         cat >expect <<-EOF &&
497                 ok
498         EOF
499         git difftool --dir-diff $symlinks -t echo >actual &&
500         test_cmp expect actual
501 '
502
503 write_script .git/CHECK_SYMLINKS <<\EOF
504 for f in file file2 sub/sub
505 do
506         echo "$f"
507         ls -ld "$2/$f" | sed -e 's/.* -> //'
508 done >actual
509 EOF
510
511 test_expect_success SYMLINKS 'difftool --dir-diff --symlink without unstaged changes' '
512         cat >expect <<-EOF &&
513         file
514         $PWD/file
515         file2
516         $PWD/file2
517         sub/sub
518         $PWD/sub/sub
519         EOF
520         git difftool --dir-diff --symlink \
521                 --extcmd "./.git/CHECK_SYMLINKS" branch HEAD &&
522         test_cmp actual expect
523 '
524
525 write_script modify-right-file <<\EOF
526 echo "new content" >"$2/file"
527 EOF
528
529 run_dir_diff_test 'difftool --dir-diff syncs worktree with unstaged change' '
530         test_when_finished git reset --hard &&
531         echo "orig content" >file &&
532         git difftool -d $symlinks --extcmd "$PWD/modify-right-file" branch &&
533         echo "new content" >expect &&
534         test_cmp expect file
535 '
536
537 run_dir_diff_test 'difftool --dir-diff syncs worktree without unstaged change' '
538         test_when_finished git reset --hard &&
539         git difftool -d $symlinks --extcmd "$PWD/modify-right-file" branch &&
540         echo "new content" >expect &&
541         test_cmp expect file
542 '
543
544 write_script modify-file <<\EOF
545 echo "new content" >file
546 EOF
547
548 test_expect_success 'difftool --no-symlinks does not overwrite working tree file ' '
549         echo "orig content" >file &&
550         git difftool --dir-diff --no-symlinks --extcmd "$PWD/modify-file" branch &&
551         echo "new content" >expect &&
552         test_cmp expect file
553 '
554
555 write_script modify-both-files <<\EOF
556 echo "wt content" >file &&
557 echo "tmp content" >"$2/file" &&
558 echo "$2" >tmpdir
559 EOF
560
561 test_expect_success 'difftool --no-symlinks detects conflict ' '
562         (
563                 TMPDIR=$TRASH_DIRECTORY &&
564                 export TMPDIR &&
565                 echo "orig content" >file &&
566                 test_must_fail git difftool --dir-diff --no-symlinks --extcmd "$PWD/modify-both-files" branch &&
567                 echo "wt content" >expect &&
568                 test_cmp expect file &&
569                 echo "tmp content" >expect &&
570                 test_cmp expect "$(cat tmpdir)/file"
571         )
572 '
573
574 test_expect_success 'difftool properly honors gitlink and core.worktree' '
575         git submodule add ./. submod/ule &&
576         test_config -C submod/ule diff.tool checktrees &&
577         test_config -C submod/ule difftool.checktrees.cmd '\''
578                 test -d "$LOCAL" && test -d "$REMOTE" && echo good
579                 '\'' &&
580         (
581                 cd submod/ule &&
582                 echo good >expect &&
583                 git difftool --tool=checktrees --dir-diff HEAD~ >actual &&
584                 test_cmp expect actual
585         )
586 '
587
588 test_expect_success SYMLINKS 'difftool --dir-diff symlinked directories' '
589         git init dirlinks &&
590         (
591                 cd dirlinks &&
592                 git config diff.tool checktrees &&
593                 git config difftool.checktrees.cmd "echo good" &&
594                 mkdir foo &&
595                 : >foo/bar &&
596                 git add foo/bar &&
597                 test_commit symlink-one &&
598                 ln -s foo link &&
599                 git add link &&
600                 test_commit symlink-two &&
601                 echo good >expect &&
602                 git difftool --tool=checktrees --dir-diff HEAD~ >actual &&
603                 test_cmp expect actual
604         )
605 '
606
607 test_done