1#!/bin/sh
2# git-difftool--helper is a GIT_EXTERNAL_DIFF-compatible diff tool launcher.
3# This script is typically launched by using the 'git difftool'
4# convenience command.
5#
6# Copyright (c) 2009 David Aguilar
78
# Set GIT_DIFFTOOL_NO_PROMPT to bypass the per-file prompt.
9should_prompt () {
10test -z "$GIT_DIFFTOOL_NO_PROMPT"
11}
1213
# This function prepares temporary files and launches the appropriate
14# merge tool.
15launch_merge_tool () {
16# Merged is the filename as it appears in the work tree
17# Local is the contents of a/filename
18# Remote is the contents of b/filename
19# Custom merge tool commands might use $BASE so we provide it
20MERGED="$1"
21LOCAL="$2"
22REMOTE="$3"
23BASE="$1"
2425
# $LOCAL and $REMOTE are temporary files so prompt
26# the user with the real $MERGED name before launching $merge_tool.
27if should_prompt; then
28printf "\nViewing: '$MERGED'\n"
29printf "Hit return to launch '%s': " "$merge_tool"
30read ans
31fi
3233
# Run the appropriate merge tool command
34case "$merge_tool" in
35kdiff3)
36basename=$(basename "$MERGED")
37"$merge_tool_path" --auto \
38--L1 "$basename (A)" \
39--L2 "$basename (B)" \
40"$LOCAL" "$REMOTE" \
41> /dev/null 2>&1
42;;
4344
kompare)
45"$merge_tool_path" "$LOCAL" "$REMOTE"
46;;
4748
tkdiff)
49"$merge_tool_path" "$LOCAL" "$REMOTE"
50;;
5152
meld)
53"$merge_tool_path" "$LOCAL" "$REMOTE"
54;;
5556
diffuse)
57"$merge_tool_path" "$LOCAL" "$REMOTE" | cat
58;;
5960
vimdiff)
61"$merge_tool_path" -d -c "wincmd l" "$LOCAL" "$REMOTE"
62;;
6364
gvimdiff)
65"$merge_tool_path" -d -c "wincmd l" -f "$LOCAL" "$REMOTE"
66;;
6768
xxdiff)
69"$merge_tool_path" \
70-R 'Accel.Search: "Ctrl+F"' \
71-R 'Accel.SearchForward: "Ctrl-G"' \
72"$LOCAL" "$REMOTE"
73;;
7475
opendiff)
76"$merge_tool_path" "$LOCAL" "$REMOTE" | cat
77;;
7879
ecmerge)
80"$merge_tool_path" "$LOCAL" "$REMOTE" \
81--default --mode=merge2 --to="$MERGED"
82;;
8384
emerge)
85"$merge_tool_path" -f emerge-files-command \
86"$LOCAL" "$REMOTE" "$(basename "$MERGED")"
87;;
8889
*)
90if test -n "$merge_tool_cmd"; then
91( eval $merge_tool_cmd )
92fi
93;;
94esac
95}
9697
# Verifies that (difftool|mergetool).<tool>.cmd exists
98valid_custom_tool() {
99merge_tool_cmd="$(git config difftool.$1.cmd)"
100test -z "$merge_tool_cmd" &&
101merge_tool_cmd="$(git config mergetool.$1.cmd)"
102test -n "$merge_tool_cmd"
103}
104105
# Verifies that the chosen merge tool is properly setup.
106# Built-in merge tools are always valid.
107valid_tool() {
108case "$1" in
109kdiff3 | kompare | tkdiff | xxdiff | meld | opendiff | emerge | vimdiff | gvimdiff | ecmerge)
110;; # happy
111*)
112if ! valid_custom_tool "$1"
113then
114return 1
115fi
116;;
117esac
118}
119120
# Sets up the merge_tool_path variable.
121# This handles the difftool.<tool>.path configuration.
122# This also falls back to mergetool defaults.
123init_merge_tool_path() {
124merge_tool_path=$(git config difftool."$1".path)
125test -z "$merge_tool_path" &&
126merge_tool_path=$(git config mergetool."$1".path)
127if test -z "$merge_tool_path"; then
128case "$1" in
129vimdiff)
130merge_tool_path=vim
131;;
132gvimdiff)
133merge_tool_path=gvim
134;;
135emerge)
136merge_tool_path=emacs
137;;
138*)
139merge_tool_path="$1"
140;;
141esac
142fi
143}
144145
# Allow GIT_DIFF_TOOL and GIT_MERGE_TOOL to provide default values
146test -n "$GIT_MERGE_TOOL" && merge_tool="$GIT_MERGE_TOOL"
147test -n "$GIT_DIFF_TOOL" && merge_tool="$GIT_DIFF_TOOL"
148149
# If merge tool was not specified then use the diff.tool
150# configuration variable. If that's invalid then reset merge_tool.
151# Fallback to merge.tool.
152if test -z "$merge_tool"; then
153merge_tool=$(git config diff.tool)
154test -z "$merge_tool" &&
155merge_tool=$(git config merge.tool)
156if test -n "$merge_tool" && ! valid_tool "$merge_tool"; then
157echo >&2 "git config option diff.tool set to unknown tool: $merge_tool"
158echo >&2 "Resetting to default..."
159unset merge_tool
160fi
161fi
162163
# Try to guess an appropriate merge tool if no tool has been set.
164if test -z "$merge_tool"; then
165# We have a $DISPLAY so try some common UNIX merge tools
166if test -n "$DISPLAY"; then
167# If gnome then prefer meld, otherwise, prefer kdiff3 or kompare
168if test -n "$GNOME_DESKTOP_SESSION_ID" ; then
169merge_tool_candidates="meld kdiff3 kompare tkdiff xxdiff gvimdiff diffuse"
170else
171merge_tool_candidates="kdiff3 kompare tkdiff xxdiff meld gvimdiff diffuse"
172fi
173fi
174if echo "${VISUAL:-$EDITOR}" | grep 'emacs' > /dev/null 2>&1; then
175# $EDITOR is emacs so add emerge as a candidate
176merge_tool_candidates="$merge_tool_candidates emerge opendiff vimdiff"
177elif echo "${VISUAL:-$EDITOR}" | grep 'vim' > /dev/null 2>&1; then
178# $EDITOR is vim so add vimdiff as a candidate
179merge_tool_candidates="$merge_tool_candidates vimdiff opendiff emerge"
180else
181merge_tool_candidates="$merge_tool_candidates opendiff emerge vimdiff"
182fi
183echo "merge tool candidates: $merge_tool_candidates"
184185
# Loop over each candidate and stop when a valid merge tool is found.
186for i in $merge_tool_candidates
187do
188init_merge_tool_path $i
189if type "$merge_tool_path" > /dev/null 2>&1; then
190merge_tool=$i
191break
192fi
193done
194195
if test -z "$merge_tool" ; then
196echo "No known merge resolution program available."
197exit 1
198fi
199200
else
201# A merge tool has been set, so verify that it's valid.
202if ! valid_tool "$merge_tool"; then
203echo >&2 "Unknown merge tool $merge_tool"
204exit 1
205fi
206207
init_merge_tool_path "$merge_tool"
208209
if test -z "$merge_tool_cmd" && ! type "$merge_tool_path" > /dev/null 2>&1; then
210echo "The merge tool $merge_tool is not available as '$merge_tool_path'"
211exit 1
212fi
213fi
214215
216
# Launch the merge tool on each path provided by 'git diff'
217while test $# -gt 6
218do
219launch_merge_tool "$1" "$2" "$5"
220shift 7
221done