1#!/bin/sh
2#
3# Copyright (c) 2009, 2010, 2012, 2013 David Aguilar
4#
5
6test_description='git-difftool
7
8Testing basic diff tool invocation
9'
10
11. ./test-lib.sh
12
13difftool_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
20prompt_given ()
21{
22 prompt="$1"
23 test "$prompt" = "Launch 'test-tool' [Y/n]? branch"
24}
25
26test_expect_success 'basic usage requires no repo' '
27 test_expect_code 129 git difftool -h >output &&
28 grep ^usage: output &&
29 # create a ceiling directory to prevent Git from finding a repo
30 mkdir -p not/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 grep ^usage: output
36'
37
38# Create a file on master and change it on branch
39test_expect_success 'setup' '
40 echo master >file &&
41 git add file &&
42 git commit -m "added file" &&
43
44 git checkout -b branch master &&
45 echo branch >file &&
46 git commit -a -m "branch changed file" &&
47 git checkout master
48'
49
50# Configure a custom difftool.<tool>.cmd and use it
51test_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 &&
57
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
62'
63
64test_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
69'
70
71test_expect_success 'difftool ignores bad --tool values' '
72 : >expect &&
73 test_must_fail \
74 git difftool --no-prompt --tool=bad-tool branch >actual &&
75 test_cmp expect actual
76'
77
78test_expect_success 'difftool forwards arguments to diff' '
79 difftool_test_setup &&
80 >for-diff &&
81 git add for-diff &&
82 echo changes>for-diff &&
83 git add for-diff &&
84 : >expect &&
85 git difftool --cached --no-prompt -- for-diff >actual &&
86 test_cmp expect actual &&
87 git reset -- for-diff &&
88 rm for-diff
89'
90
91test_expect_success 'difftool ignores exit code' '
92 test_config difftool.error.cmd false &&
93 git difftool -y -t error branch
94'
95
96test_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
99'
100
101test_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
104'
105
106test_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
110'
111
112test_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
116'
117
118test_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
122'
123
124test_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 &&
128 echo "$2"
129 exit 1
130 EOF
131 >for-diff &&
132 git add for-diff &&
133 echo file >expect &&
134 test_must_fail git difftool -y --trust-exit-code \
135 --extcmd .git/fail-right-file branch >actual &&
136 test_cmp expect actual
137'
138
139test_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
143'
144
145test_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 &&
150
151 echo branch >expect &&
152 git difftool --no-prompt --gui branch >actual &&
153 test_cmp expect actual
154'
155
156test_expect_success 'difftool --gui last setting wins' '
157 difftool_test_setup &&
158 : >expect &&
159 git difftool --no-prompt --gui --no-gui >actual &&
160 test_cmp expect actual &&
161
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
168'
169
170test_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
175'
176
177# Specify the diff tool using $GIT_DIFF_TOOL
178test_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
184'
185
186# Test the $GIT_*_TOOL variables and ensure
187# that $GIT_DIFF_TOOL always wins unless --tool is specified
188test_expect_success 'GIT_DIFF_TOOL overrides' '
189 difftool_test_setup &&
190 test_config diff.tool bogus-tool &&
191 test_config merge.tool bogus-tool &&
192
193 echo branch >expect &&
194 GIT_DIFF_TOOL=test-tool git difftool --no-prompt branch >actual &&
195 test_cmp expect actual &&
196
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
202'
203
204# Test that we don't have to pass --no-prompt to difftool
205# when $GIT_DIFFTOOL_NO_PROMPT is true
206test_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
211'
212
213# git-difftool supports the difftool.prompt variable.
214# Test that GIT_DIFFTOOL_PROMPT can override difftool.prompt = false
215test_expect_success 'GIT_DIFFTOOL_PROMPT variable' '
216 difftool_test_setup &&
217 test_config difftool.prompt false &&
218 echo >input &&
219 GIT_DIFFTOOL_PROMPT=true git difftool branch <input >output &&
220 prompt=$(tail -1 <output) &&
221 prompt_given "$prompt"
222'
223
224# Test that we don't have to pass --no-prompt when difftool.prompt is false
225test_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
231'
232
233# Test that we don't have to pass --no-prompt when mergetool.prompt is false
234test_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
241'
242
243# Test that the -y flag can override difftool.prompt = true
244test_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
250'
251
252# Test that the --prompt flag can override difftool.prompt = false
253test_expect_success 'difftool.prompt can overridden with --prompt' '
254 difftool_test_setup &&
255 test_config difftool.prompt false &&
256 echo >input &&
257 git difftool --prompt branch <input >output &&
258 prompt=$(tail -1 <output) &&
259 prompt_given "$prompt"
260'
261
262# Test that the last flag passed on the command-line wins
263test_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 &&
268 echo >input &&
269 git difftool --no-prompt --prompt branch <input >output &&
270 prompt=$(tail -1 <output) &&
271 prompt_given "$prompt"
272'
273
274# git-difftool falls back to git-mergetool config variables
275# so test that behavior here
276test_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 &&
282
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
288'
289
290test_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
295'
296
297test_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
302'
303
304test_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
309'
310
311test_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
316'
317
318test_expect_success 'difftool --extcmd echo arg1' '
319 echo file >expect &&
320 git difftool --no-prompt \
321 --extcmd sh\ -c\ \"echo\ \$1\" branch >actual &&
322 test_cmp expect actual
323'
324
325test_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
330'
331
332test_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
337'
338
339# Create a second file on master and a different version on branch
340test_expect_success 'setup with 2 files different' '
341 echo m2 >file2 &&
342 git add file2 &&
343 git commit -m "added file2" &&
344
345 git checkout branch &&
346 echo br2 >file2 &&
347 git add file2 &&
348 git commit -a -m "branch changed file2" &&
349 git checkout master
350'
351
352test_expect_success 'say no to the first file' '
353 (echo n && echo) >input &&
354 git difftool -x cat branch <input >output &&
355 grep m2 output &&
356 grep br2 output &&
357 ! grep master output &&
358 ! grep branch output
359'
360
361test_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 &&
366 ! grep m2 output &&
367 ! grep br2 output
368'
369
370test_expect_success 'ending prompt input with EOF' '
371 git difftool -x cat branch </dev/null >output &&
372 ! grep master output &&
373 ! grep branch output &&
374 ! grep m2 output &&
375 ! grep br2 output
376'
377
378test_expect_success 'difftool --tool-help' '
379 git difftool --tool-help >output &&
380 grep tool output
381'
382
383test_expect_success 'setup change in subdirectory' '
384 git checkout master &&
385 mkdir sub &&
386 echo master >sub/sub &&
387 git add sub/sub &&
388 git commit -m "added sub/sub" &&
389 git tag v1 &&
390 echo test >>file &&
391 echo test >>sub/sub &&
392 git add file sub/sub &&
393 git commit -m "modified both"
394'
395
396run_dir_diff_test () {
397 test_expect_success "$1 --no-symlinks" "
398 symlinks=--no-symlinks &&
399 $2
400 "
401 test_expect_success SYMLINKS "$1 --symlinks" "
402 symlinks=--symlinks &&
403 $2
404 "
405}
406
407run_dir_diff_test 'difftool -d' '
408 git difftool -d $symlinks --extcmd ls branch >output &&
409 grep sub output &&
410 grep file output
411'
412
413run_dir_diff_test 'difftool --dir-diff' '
414 git difftool --dir-diff $symlinks --extcmd ls branch >output &&
415 grep sub output &&
416 grep file output
417'
418
419run_dir_diff_test 'difftool --dir-diff ignores --prompt' '
420 git difftool --dir-diff $symlinks --prompt --extcmd ls branch >output &&
421 grep sub output &&
422 grep file output
423'
424
425run_dir_diff_test 'difftool --dir-diff branch from subdirectory' '
426 (
427 cd sub &&
428 git difftool --dir-diff $symlinks --extcmd ls branch >output &&
429 # "sub" must only exist in "right"
430 # "file" and "file2" must be listed in both "left" and "right"
431 grep sub output >sub-output &&
432 test_line_count = 1 sub-output &&
433 grep file"$" output >file-output &&
434 test_line_count = 2 file-output &&
435 grep file2 output >file2-output &&
436 test_line_count = 2 file2-output
437 )
438'
439
440run_dir_diff_test 'difftool --dir-diff v1 from subdirectory' '
441 (
442 cd sub &&
443 git difftool --dir-diff $symlinks --extcmd ls v1 >output &&
444 # "sub" and "file" exist in both v1 and HEAD.
445 # "file2" is unchanged.
446 grep sub output >sub-output &&
447 test_line_count = 2 sub-output &&
448 grep file output >file-output &&
449 test_line_count = 2 file-output &&
450 ! grep file2 output
451 )
452'
453
454run_dir_diff_test 'difftool --dir-diff branch from subdirectory w/ pathspec' '
455 (
456 cd sub &&
457 git difftool --dir-diff $symlinks --extcmd ls branch -- .>output &&
458 # "sub" only exists in "right"
459 # "file" and "file2" must not be listed
460 grep sub output >sub-output &&
461 test_line_count = 1 sub-output &&
462 ! grep file output
463 )
464'
465
466run_dir_diff_test 'difftool --dir-diff v1 from subdirectory w/ pathspec' '
467 (
468 cd sub &&
469 git difftool --dir-diff $symlinks --extcmd ls v1 -- .>output &&
470 # "sub" exists in v1 and HEAD
471 # "file" is filtered out by the pathspec
472 grep sub output >sub-output &&
473 test_line_count = 2 sub-output &&
474 ! grep file output
475 )
476'
477
478run_dir_diff_test 'difftool --dir-diff from subdirectory with GIT_DIR set' '
479 (
480 GIT_DIR=$(pwd)/.git &&
481 export GIT_DIR &&
482 GIT_WORK_TREE=$(pwd) &&
483 export GIT_WORK_TREE &&
484 cd sub &&
485 git difftool --dir-diff $symlinks --extcmd ls \
486 branch -- sub >output &&
487 grep sub output &&
488 ! grep file output
489 )
490'
491
492run_dir_diff_test 'difftool --dir-diff when worktree file is missing' '
493 test_when_finished git reset --hard &&
494 rm file2 &&
495 git difftool --dir-diff $symlinks --extcmd ls branch master >output &&
496 grep file2 output
497'
498
499run_dir_diff_test 'difftool --dir-diff with unmerged files' '
500 test_when_finished git reset --hard &&
501 test_config difftool.echo.cmd "echo ok" &&
502 git checkout -B conflict-a &&
503 git checkout -B conflict-b &&
504 git checkout conflict-a &&
505 echo a >>file &&
506 git add file &&
507 git commit -m conflict-a &&
508 git checkout conflict-b &&
509 echo b >>file &&
510 git add file &&
511 git commit -m conflict-b &&
512 git checkout master &&
513 git merge conflict-a &&
514 test_must_fail git merge conflict-b &&
515 cat >expect <<-EOF &&
516 ok
517 EOF
518 git difftool --dir-diff $symlinks -t echo >actual &&
519 test_cmp expect actual
520'
521
522write_script .git/CHECK_SYMLINKS <<\EOF
523for f in file file2 sub/sub
524do
525 echo "$f"
526 ls -ld "$2/$f" | sed -e 's/.* -> //'
527done >actual
528EOF
529
530test_expect_success SYMLINKS 'difftool --dir-diff --symlink without unstaged changes' '
531 cat >expect <<-EOF &&
532 file
533 $PWD/file
534 file2
535 $PWD/file2
536 sub/sub
537 $PWD/sub/sub
538 EOF
539 git difftool --dir-diff --symlink \
540 --extcmd "./.git/CHECK_SYMLINKS" branch HEAD &&
541 test_cmp actual expect
542'
543
544write_script modify-right-file <<\EOF
545echo "new content" >"$2/file"
546EOF
547
548run_dir_diff_test 'difftool --dir-diff syncs worktree with unstaged change' '
549 test_when_finished git reset --hard &&
550 echo "orig content" >file &&
551 git difftool -d $symlinks --extcmd "$PWD/modify-right-file" branch &&
552 echo "new content" >expect &&
553 test_cmp expect file
554'
555
556run_dir_diff_test 'difftool --dir-diff syncs worktree without unstaged change' '
557 test_when_finished git reset --hard &&
558 git difftool -d $symlinks --extcmd "$PWD/modify-right-file" branch &&
559 echo "new content" >expect &&
560 test_cmp expect file
561'
562
563write_script modify-file <<\EOF
564echo "new content" >file
565EOF
566
567test_expect_success 'difftool --no-symlinks does not overwrite working tree file ' '
568 echo "orig content" >file &&
569 git difftool --dir-diff --no-symlinks --extcmd "$PWD/modify-file" branch &&
570 echo "new content" >expect &&
571 test_cmp expect file
572'
573
574write_script modify-both-files <<\EOF
575echo "wt content" >file &&
576echo "tmp content" >"$2/file" &&
577echo "$2" >tmpdir
578EOF
579
580test_expect_success 'difftool --no-symlinks detects conflict ' '
581 (
582 TMPDIR=$TRASH_DIRECTORY &&
583 export TMPDIR &&
584 echo "orig content" >file &&
585 test_must_fail git difftool --dir-diff --no-symlinks --extcmd "$PWD/modify-both-files" branch &&
586 echo "wt content" >expect &&
587 test_cmp expect file &&
588 echo "tmp content" >expect &&
589 test_cmp expect "$(cat tmpdir)/file"
590 )
591'
592
593test_expect_success 'difftool properly honors gitlink and core.worktree' '
594 test_when_finished rm -rf submod/ule &&
595 git submodule add ./. submod/ule &&
596 test_config -C submod/ule diff.tool checktrees &&
597 test_config -C submod/ule difftool.checktrees.cmd '\''
598 test -d "$LOCAL" && test -d "$REMOTE" && echo good
599 '\'' &&
600 (
601 cd submod/ule &&
602 echo good >expect &&
603 git difftool --tool=checktrees --dir-diff HEAD~ >actual &&
604 test_cmp expect actual &&
605 rm -f expect actual
606 )
607'
608
609test_expect_success SYMLINKS 'difftool --dir-diff symlinked directories' '
610 test_when_finished git reset --hard &&
611 git init dirlinks &&
612 (
613 cd dirlinks &&
614 git config diff.tool checktrees &&
615 git config difftool.checktrees.cmd "echo good" &&
616 mkdir foo &&
617 : >foo/bar &&
618 git add foo/bar &&
619 test_commit symlink-one &&
620 ln -s foo link &&
621 git add link &&
622 test_commit symlink-two &&
623 echo good >expect &&
624 git difftool --tool=checktrees --dir-diff HEAD~ >actual &&
625 test_cmp expect actual
626 )
627'
628
629test_expect_success SYMLINKS 'difftool --dir-diff handles modified symlinks' '
630 test_when_finished git reset --hard &&
631 touch b &&
632 ln -s b c &&
633 git add b c &&
634 test_tick &&
635 git commit -m initial &&
636 touch d &&
637 rm c &&
638 ln -s d c &&
639 cat >expect <<-EOF &&
640 b
641 c
642
643 c
644 EOF
645 git difftool --symlinks --dir-diff --extcmd ls >output &&
646 grep -v ^/ output >actual &&
647 test_cmp expect actual &&
648
649 git difftool --no-symlinks --dir-diff --extcmd ls >output &&
650 grep -v ^/ output >actual &&
651 test_cmp expect actual &&
652
653 # The left side contains symlink "c" that points to "b"
654 test_config difftool.cat.cmd "cat \$LOCAL/c" &&
655 printf "%s\n" b >expect &&
656
657 git difftool --symlinks --dir-diff --tool cat >actual &&
658 test_cmp expect actual &&
659
660 git difftool --symlinks --no-symlinks --dir-diff --tool cat >actual &&
661 test_cmp expect actual &&
662
663 # The right side contains symlink "c" that points to "d"
664 test_config difftool.cat.cmd "cat \$REMOTE/c" &&
665 printf "%s\n" d >expect &&
666
667 git difftool --symlinks --dir-diff --tool cat >actual &&
668 test_cmp expect actual &&
669
670 git difftool --no-symlinks --dir-diff --tool cat >actual &&
671 test_cmp expect actual &&
672
673 # Deleted symlinks
674 rm -f c &&
675 cat >expect <<-EOF &&
676 b
677 c
678
679 EOF
680 git difftool --symlinks --dir-diff --extcmd ls >output &&
681 grep -v ^/ output >actual &&
682 test_cmp expect actual &&
683
684 git difftool --no-symlinks --dir-diff --extcmd ls >output &&
685 grep -v ^/ output >actual &&
686 test_cmp expect actual
687'
688
689test_done