3 # Copyright (c) 2009, 2010, 2012, 2013 David Aguilar
6 test_description
='git-difftool
8 Testing basic diff tool invocation
13 difftool_test_setup
()
15 test_config
diff.tool test-tool
&&
16 test_config difftool.test-tool.cmd
'cat "$LOCAL"' &&
17 test_config difftool.bogus-tool.cmd false
23 test "$prompt" = "Launch 'test-tool' [Y/n]? branch"
26 test_expect_success
'basic usage requires no repo' '
27 test_expect_code 129 git difftool -h >output &&
28 test_i18ngrep ^usage: output &&
29 # create a ceiling directory to prevent Git from finding a repo
31 test_when_finished rm -r not &&
32 test_expect_code 129 \
33 env GIT_CEILING_DIRECTORIES="$(pwd)/not" \
34 git -C not/repo difftool -h >output &&
35 test_i18ngrep ^usage: output
38 # Create a file on master and change it on branch
39 test_expect_success
'setup' '
42 git commit -m "added file" &&
44 git checkout -b branch master &&
46 git commit -a -m "branch changed file" &&
50 # Configure a custom difftool.<tool>.cmd and use it
51 test_expect_success
'custom commands' '
52 difftool_test_setup &&
53 test_config difftool.test-tool.cmd "cat \"\$REMOTE\"" &&
54 echo master >expect &&
55 git difftool --no-prompt branch >actual &&
56 test_cmp expect actual &&
58 test_config difftool.test-tool.cmd "cat \"\$LOCAL\"" &&
59 echo branch >expect &&
60 git difftool --no-prompt branch >actual &&
61 test_cmp expect actual
64 test_expect_success
'custom tool commands override built-ins' '
65 test_config difftool.vimdiff.cmd "cat \"\$REMOTE\"" &&
66 echo master >expect &&
67 git difftool --tool vimdiff --no-prompt branch >actual &&
68 test_cmp expect actual
71 test_expect_success
'difftool ignores bad --tool values' '
74 git difftool --no-prompt --tool=bad-tool branch >actual &&
75 test_cmp expect actual
78 test_expect_success
'difftool forwards arguments to diff' '
79 difftool_test_setup &&
82 echo changes>for-diff &&
85 git difftool --cached --no-prompt -- for-diff >actual &&
86 test_cmp expect actual &&
87 git reset -- for-diff &&
91 test_expect_success
'difftool ignores exit code' '
92 test_config difftool.error.cmd false &&
93 git difftool -y -t error branch
96 test_expect_success
'difftool forwards exit code with --trust-exit-code' '
97 test_config difftool.error.cmd false &&
98 test_must_fail git difftool -y --trust-exit-code -t error branch
101 test_expect_success
'difftool forwards exit code with --trust-exit-code for built-ins' '
102 test_config difftool.vimdiff.path false &&
103 test_must_fail git difftool -y --trust-exit-code -t vimdiff branch
106 test_expect_success
'difftool honors difftool.trustExitCode = true' '
107 test_config difftool.error.cmd false &&
108 test_config difftool.trustExitCode true &&
109 test_must_fail git difftool -y -t error branch
112 test_expect_success
'difftool honors difftool.trustExitCode = false' '
113 test_config difftool.error.cmd false &&
114 test_config difftool.trustExitCode false &&
115 git difftool -y -t error branch
118 test_expect_success
'difftool ignores exit code with --no-trust-exit-code' '
119 test_config difftool.error.cmd false &&
120 test_config difftool.trustExitCode true &&
121 git difftool -y --no-trust-exit-code -t error branch
124 test_expect_success
'difftool stops on error with --trust-exit-code' '
125 test_when_finished "rm -f for-diff .git/fail-right-file" &&
126 test_when_finished "git reset -- for-diff" &&
127 write_script .git/fail-right-file <<-\EOF &&
134 test_must_fail git difftool -y --trust-exit-code \
135 --extcmd .git/fail-right-file branch >actual &&
136 test_cmp expect actual
139 test_expect_success
'difftool honors exit status if command not found' '
140 test_config difftool.nonexistent.cmd i-dont-exist &&
141 test_config difftool.trustExitCode false &&
142 test_must_fail git difftool -y -t nonexistent branch
145 test_expect_success
'difftool honors --gui' '
146 difftool_test_setup &&
147 test_config merge.tool bogus-tool &&
148 test_config diff.tool bogus-tool &&
149 test_config diff.guitool test-tool &&
151 echo branch >expect &&
152 git difftool --no-prompt --gui branch >actual &&
153 test_cmp expect actual
156 test_expect_success
'difftool --gui last setting wins' '
157 difftool_test_setup &&
159 git difftool --no-prompt --gui --no-gui >actual &&
160 test_cmp expect actual &&
162 test_config merge.tool bogus-tool &&
163 test_config diff.tool bogus-tool &&
164 test_config diff.guitool test-tool &&
165 echo branch >expect &&
166 git difftool --no-prompt --no-gui --gui branch >actual &&
167 test_cmp expect actual
170 test_expect_success
'difftool --gui works without configured diff.guitool' '
171 difftool_test_setup &&
172 echo branch >expect &&
173 git difftool --no-prompt --gui branch >actual &&
174 test_cmp expect actual
177 # Specify the diff tool using $GIT_DIFF_TOOL
178 test_expect_success
'GIT_DIFF_TOOL variable' '
179 difftool_test_setup &&
180 git config --unset diff.tool &&
181 echo branch >expect &&
182 GIT_DIFF_TOOL=test-tool git difftool --no-prompt branch >actual &&
183 test_cmp expect actual
186 # Test the $GIT_*_TOOL variables and ensure
187 # that $GIT_DIFF_TOOL always wins unless --tool is specified
188 test_expect_success
'GIT_DIFF_TOOL overrides' '
189 difftool_test_setup &&
190 test_config diff.tool bogus-tool &&
191 test_config merge.tool bogus-tool &&
193 echo branch >expect &&
194 GIT_DIFF_TOOL=test-tool git difftool --no-prompt branch >actual &&
195 test_cmp expect actual &&
197 test_config diff.tool bogus-tool &&
198 test_config merge.tool bogus-tool &&
199 GIT_DIFF_TOOL=bogus-tool \
200 git difftool --no-prompt --tool=test-tool branch >actual &&
201 test_cmp expect actual
204 # Test that we don't have to pass --no-prompt to difftool
205 # when $GIT_DIFFTOOL_NO_PROMPT is true
206 test_expect_success
'GIT_DIFFTOOL_NO_PROMPT variable' '
207 difftool_test_setup &&
208 echo branch >expect &&
209 GIT_DIFFTOOL_NO_PROMPT=true git difftool branch >actual &&
210 test_cmp expect actual
213 # git-difftool supports the difftool.prompt variable.
214 # Test that GIT_DIFFTOOL_PROMPT can override difftool.prompt = false
215 test_expect_success
'GIT_DIFFTOOL_PROMPT variable' '
216 difftool_test_setup &&
217 test_config difftool.prompt false &&
219 GIT_DIFFTOOL_PROMPT=true git difftool branch <input >output &&
220 prompt=$(tail -1 <output) &&
221 prompt_given "$prompt"
224 # Test that we don't have to pass --no-prompt when difftool.prompt is false
225 test_expect_success
'difftool.prompt config variable is false' '
226 difftool_test_setup &&
227 test_config difftool.prompt false &&
228 echo branch >expect &&
229 git difftool branch >actual &&
230 test_cmp expect actual
233 # Test that we don't have to pass --no-prompt when mergetool.prompt is false
234 test_expect_success
'difftool merge.prompt = false' '
235 difftool_test_setup &&
236 test_might_fail git config --unset difftool.prompt &&
237 test_config mergetool.prompt false &&
238 echo branch >expect &&
239 git difftool branch >actual &&
240 test_cmp expect actual
243 # Test that the -y flag can override difftool.prompt = true
244 test_expect_success
'difftool.prompt can overridden with -y' '
245 difftool_test_setup &&
246 test_config difftool.prompt true &&
247 echo branch >expect &&
248 git difftool -y branch >actual &&
249 test_cmp expect actual
252 # Test that the --prompt flag can override difftool.prompt = false
253 test_expect_success
'difftool.prompt can overridden with --prompt' '
254 difftool_test_setup &&
255 test_config difftool.prompt false &&
257 git difftool --prompt branch <input >output &&
258 prompt=$(tail -1 <output) &&
259 prompt_given "$prompt"
262 # Test that the last flag passed on the command-line wins
263 test_expect_success
'difftool last flag wins' '
264 difftool_test_setup &&
265 echo branch >expect &&
266 git difftool --prompt --no-prompt branch >actual &&
267 test_cmp expect actual &&
269 git difftool --no-prompt --prompt branch <input >output &&
270 prompt=$(tail -1 <output) &&
271 prompt_given "$prompt"
274 # git-difftool falls back to git-mergetool config variables
275 # so test that behavior here
276 test_expect_success
'difftool + mergetool config variables' '
277 test_config merge.tool test-tool &&
278 test_config mergetool.test-tool.cmd "cat \$LOCAL" &&
279 echo branch >expect &&
280 git difftool --no-prompt branch >actual &&
281 test_cmp expect actual &&
283 # set merge.tool to something bogus, diff.tool to test-tool
284 test_config merge.tool bogus-tool &&
285 test_config diff.tool test-tool &&
286 git difftool --no-prompt branch >actual &&
287 test_cmp expect actual
290 test_expect_success
'difftool.<tool>.path' '
291 test_config difftool.tkdiff.path echo &&
292 git difftool --tool=tkdiff --no-prompt branch >output &&
293 grep file output >grep-output &&
294 test_line_count = 1 grep-output
297 test_expect_success
'difftool --extcmd=cat' '
298 echo branch >expect &&
299 echo master >>expect &&
300 git difftool --no-prompt --extcmd=cat branch >actual &&
301 test_cmp expect actual
304 test_expect_success
'difftool --extcmd cat' '
305 echo branch >expect &&
306 echo master >>expect &&
307 git difftool --no-prompt --extcmd=cat branch >actual &&
308 test_cmp expect actual
311 test_expect_success
'difftool -x cat' '
312 echo branch >expect &&
313 echo master >>expect &&
314 git difftool --no-prompt -x cat branch >actual &&
315 test_cmp expect actual
318 test_expect_success
'difftool --extcmd echo arg1' '
320 git difftool --no-prompt \
321 --extcmd sh\ -c\ \"echo\ \$1\" branch >actual &&
322 test_cmp expect actual
325 test_expect_success
'difftool --extcmd cat arg1' '
326 echo master >expect &&
327 git difftool --no-prompt \
328 --extcmd sh\ -c\ \"cat\ \$1\" branch >actual &&
329 test_cmp expect actual
332 test_expect_success
'difftool --extcmd cat arg2' '
333 echo branch >expect &&
334 git difftool --no-prompt \
335 --extcmd sh\ -c\ \"cat\ \$2\" branch >actual &&
336 test_cmp expect actual
339 # Create a second file on master and a different version on branch
340 test_expect_success
'setup with 2 files different' '
343 git commit -m "added file2" &&
345 git checkout branch &&
348 git commit -a -m "branch changed file2" &&
352 test_expect_success
'say no to the first file' '
353 (echo n && echo) >input &&
354 git difftool -x cat branch <input >output &&
357 ! grep master output &&
361 test_expect_success
'say no to the second file' '
362 (echo && echo n) >input &&
363 git difftool -x cat branch <input >output &&
364 grep master output &&
365 grep branch output &&
370 test_expect_success
'ending prompt input with EOF' '
371 git difftool -x cat branch </dev/null >output &&
372 ! grep master output &&
373 ! grep branch output &&
378 test_expect_success
'difftool --tool-help' '
379 git difftool --tool-help >output &&
383 test_expect_success
'setup change in subdirectory' '
384 git checkout master &&
386 echo master >sub/sub &&
388 git commit -m "added sub/sub" &&
391 echo test >>sub/sub &&
392 git add file sub/sub &&
393 git commit -m "modified both"
396 test_expect_success
'difftool -d with growing paths' '
397 a=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa &&
401 echo "test -f \"\$2/b\"" | write_script .git/test-for-b.sh &&
402 one=$(printf 1 | git hash-object -w --stdin) &&
403 two=$(printf 2 | git hash-object -w --stdin) &&
404 git update-index --add \
405 --cacheinfo 100644,$one,$a --cacheinfo 100644,$two,b &&
406 tree1=$(git write-tree) &&
407 git update-index --add \
408 --cacheinfo 100644,$two,$a --cacheinfo 100644,$one,b &&
409 tree2=$(git write-tree) &&
410 git checkout -- $a &&
411 git difftool -d --extcmd .git/test-for-b.sh $tree1 $tree2
415 run_dir_diff_test
() {
416 test_expect_success
"$1 --no-symlinks" "
417 symlinks=--no-symlinks &&
420 test_expect_success SYMLINKS
"$1 --symlinks" "
421 symlinks=--symlinks &&
426 run_dir_diff_test
'difftool -d' '
427 git difftool -d $symlinks --extcmd ls branch >output &&
432 run_dir_diff_test
'difftool --dir-diff' '
433 git difftool --dir-diff $symlinks --extcmd ls branch >output &&
438 run_dir_diff_test
'difftool --dir-diff ignores --prompt' '
439 git difftool --dir-diff $symlinks --prompt --extcmd ls branch >output &&
444 run_dir_diff_test
'difftool --dir-diff branch from subdirectory' '
447 git difftool --dir-diff $symlinks --extcmd ls branch >output &&
448 # "sub" must only exist in "right"
449 # "file" and "file2" must be listed in both "left" and "right"
450 grep sub output >sub-output &&
451 test_line_count = 1 sub-output &&
452 grep file"$" output >file-output &&
453 test_line_count = 2 file-output &&
454 grep file2 output >file2-output &&
455 test_line_count = 2 file2-output
459 run_dir_diff_test
'difftool --dir-diff v1 from subdirectory' '
462 git difftool --dir-diff $symlinks --extcmd ls v1 >output &&
463 # "sub" and "file" exist in both v1 and HEAD.
464 # "file2" is unchanged.
465 grep sub output >sub-output &&
466 test_line_count = 2 sub-output &&
467 grep file output >file-output &&
468 test_line_count = 2 file-output &&
473 run_dir_diff_test
'difftool --dir-diff branch from subdirectory w/ pathspec' '
476 git difftool --dir-diff $symlinks --extcmd ls branch -- .>output &&
477 # "sub" only exists in "right"
478 # "file" and "file2" must not be listed
479 grep sub output >sub-output &&
480 test_line_count = 1 sub-output &&
485 run_dir_diff_test
'difftool --dir-diff v1 from subdirectory w/ pathspec' '
488 git difftool --dir-diff $symlinks --extcmd ls v1 -- .>output &&
489 # "sub" exists in v1 and HEAD
490 # "file" is filtered out by the pathspec
491 grep sub output >sub-output &&
492 test_line_count = 2 sub-output &&
497 run_dir_diff_test
'difftool --dir-diff from subdirectory with GIT_DIR set' '
499 GIT_DIR=$(pwd)/.git &&
501 GIT_WORK_TREE=$(pwd) &&
502 export GIT_WORK_TREE &&
504 git difftool --dir-diff $symlinks --extcmd ls \
505 branch -- sub >output &&
511 run_dir_diff_test
'difftool --dir-diff when worktree file is missing' '
512 test_when_finished git reset --hard &&
514 git difftool --dir-diff $symlinks --extcmd ls branch master >output &&
518 run_dir_diff_test
'difftool --dir-diff with unmerged files' '
519 test_when_finished git reset --hard &&
520 test_config difftool.echo.cmd "echo ok" &&
521 git checkout -B conflict-a &&
522 git checkout -B conflict-b &&
523 git checkout conflict-a &&
526 git commit -m conflict-a &&
527 git checkout conflict-b &&
530 git commit -m conflict-b &&
531 git checkout master &&
532 git merge conflict-a &&
533 test_must_fail git merge conflict-b &&
534 cat >expect <<-EOF &&
537 git difftool --dir-diff $symlinks -t echo >actual &&
538 test_cmp expect actual
541 write_script .git
/CHECK_SYMLINKS
<<\EOF
542 for f
in file file2 sub
/sub
545 ls -ld "$2/$f" |
sed -e 's/.* -> //'
549 test_expect_success SYMLINKS
'difftool --dir-diff --symlink without unstaged changes' '
550 cat >expect <<-EOF &&
558 git difftool --dir-diff --symlink \
559 --extcmd "./.git/CHECK_SYMLINKS" branch HEAD &&
560 test_cmp actual expect
563 write_script modify-right-file
<<\EOF
564 echo "new content" >"$2/file"
567 run_dir_diff_test
'difftool --dir-diff syncs worktree with unstaged change' '
568 test_when_finished git reset --hard &&
569 echo "orig content" >file &&
570 git difftool -d $symlinks --extcmd "$PWD/modify-right-file" branch &&
571 echo "new content" >expect &&
575 run_dir_diff_test
'difftool --dir-diff syncs worktree without unstaged change' '
576 test_when_finished git reset --hard &&
577 git difftool -d $symlinks --extcmd "$PWD/modify-right-file" branch &&
578 echo "new content" >expect &&
582 write_script modify-file
<<\EOF
583 echo "new content" >file
586 test_expect_success
'difftool --no-symlinks does not overwrite working tree file ' '
587 echo "orig content" >file &&
588 git difftool --dir-diff --no-symlinks --extcmd "$PWD/modify-file" branch &&
589 echo "new content" >expect &&
593 write_script modify-both-files
<<\EOF
594 echo "wt content" >file &&
595 echo "tmp content" >"$2/file" &&
599 test_expect_success
'difftool --no-symlinks detects conflict ' '
601 TMPDIR=$TRASH_DIRECTORY &&
603 echo "orig content" >file &&
604 test_must_fail git difftool --dir-diff --no-symlinks --extcmd "$PWD/modify-both-files" branch &&
605 echo "wt content" >expect &&
606 test_cmp expect file &&
607 echo "tmp content" >expect &&
608 test_cmp expect "$(cat tmpdir)/file"
612 test_expect_success
'difftool properly honors gitlink and core.worktree' '
613 test_when_finished rm -rf submod/ule &&
614 git submodule add ./. submod/ule &&
615 test_config -C submod/ule diff.tool checktrees &&
616 test_config -C submod/ule difftool.checktrees.cmd '\''
617 test -d "$LOCAL" && test -d "$REMOTE" && echo good
622 git difftool --tool=checktrees --dir-diff HEAD~ >actual &&
623 test_cmp expect actual &&
628 test_expect_success SYMLINKS
'difftool --dir-diff symlinked directories' '
629 test_when_finished git reset --hard &&
633 git config diff.tool checktrees &&
634 git config difftool.checktrees.cmd "echo good" &&
638 test_commit symlink-one &&
641 test_commit symlink-two &&
643 git difftool --tool=checktrees --dir-diff HEAD~ >actual &&
644 test_cmp expect actual
648 test_expect_success SYMLINKS
'difftool --dir-diff handles modified symlinks' '
649 test_when_finished git reset --hard &&
654 git commit -m initial &&
658 cat >expect <<-EOF &&
664 git difftool --symlinks --dir-diff --extcmd ls >output &&
665 grep -v ^/ output >actual &&
666 test_cmp expect actual &&
668 git difftool --no-symlinks --dir-diff --extcmd ls >output &&
669 grep -v ^/ output >actual &&
670 test_cmp expect actual &&
672 # The left side contains symlink "c" that points to "b"
673 test_config difftool.cat.cmd "cat \$LOCAL/c" &&
674 printf "%s\n" b >expect &&
676 git difftool --symlinks --dir-diff --tool cat >actual &&
677 test_cmp expect actual &&
679 git difftool --symlinks --no-symlinks --dir-diff --tool cat >actual &&
680 test_cmp expect actual &&
682 # The right side contains symlink "c" that points to "d"
683 test_config difftool.cat.cmd "cat \$REMOTE/c" &&
684 printf "%s\n" d >expect &&
686 git difftool --symlinks --dir-diff --tool cat >actual &&
687 test_cmp expect actual &&
689 git difftool --no-symlinks --dir-diff --tool cat >actual &&
690 test_cmp expect actual &&
694 cat >expect <<-EOF &&
699 git difftool --symlinks --dir-diff --extcmd ls >output &&
700 grep -v ^/ output >actual &&
701 test_cmp expect actual &&
703 git difftool --no-symlinks --dir-diff --extcmd ls >output &&
704 grep -v ^/ output >actual &&
705 test_cmp expect actual