mergetool--lib: add functions for finding available tools
[git/git.git] / git-mergetool--lib.sh
CommitLineData
02e51243 1#!/bin/sh
21d0ba7e 2# git-mergetool--lib is a library for common merge tool functions
073678b8
DA
3MERGE_TOOLS_DIR=$(git --exec-path)/mergetools
4
17a1f1c5
DA
5mode_ok () {
6 if diff_mode
7 then
8 can_diff
9 elif merge_mode
10 then
11 can_merge
12 else
13 false
14 fi
15}
16
17is_available () {
18 merge_tool_path=$(translate_merge_tool_path "$1") &&
19 type "$merge_tool_path" >/dev/null 2>&1
20}
21
22show_tool_names () {
23 condition=${1:-true} per_line_prefix=${2:-} preamble=${3:-}
24 not_found_msg=${4:-}
25
26 shown_any=
27 ( cd "$MERGE_TOOLS_DIR" && ls ) | {
28 while read toolname
29 do
30 if setup_tool "$toolname" 2>/dev/null &&
31 (eval "$condition" "$toolname")
32 then
33 if test -n "$preamble"
34 then
35 printf "%s\n" "$preamble"
36 preamble=
37 fi
38 shown_any=yes
39 printf "%s%s\n" "$per_line_prefix" "$toolname"
40 fi
41 done
42
43 if test -n "$preamble" && test -n "$not_found_msg"
44 then
45 printf "%s\n" "$not_found_msg"
46 fi
47
48 test -n "$shown_any"
49 }
50}
51
21d0ba7e
DA
52diff_mode() {
53 test "$TOOL_MODE" = diff
54}
55
56merge_mode() {
57 test "$TOOL_MODE" = merge
58}
59
60translate_merge_tool_path () {
bc7a96a8 61 echo "$1"
21d0ba7e
DA
62}
63
64check_unchanged () {
240dc3e8
DA
65 if test "$MERGED" -nt "$BACKUP"
66 then
21d0ba7e
DA
67 status=0
68 else
240dc3e8
DA
69 while true
70 do
21d0ba7e
DA
71 echo "$MERGED seems unchanged."
72 printf "Was the merge successful? [y/n] "
e622f41d 73 read answer || return 1
21d0ba7e
DA
74 case "$answer" in
75 y*|Y*) status=0; break ;;
76 n*|N*) status=1; break ;;
77 esac
78 done
79 fi
80}
81
82valid_tool () {
80ff2b68
DA
83 setup_tool "$1" && return 0
84 cmd=$(get_merge_tool_cmd "$1")
85 test -n "$cmd"
bc7a96a8
DA
86}
87
88setup_tool () {
073678b8
DA
89 tool="$1"
90
91 # Fallback definitions, to be overriden by tools.
92 can_merge () {
93 return 0
94 }
95
96 can_diff () {
97 return 0
98 }
99
100 diff_cmd () {
101 status=1
102 return $status
103 }
104
105 merge_cmd () {
106 status=1
107 return $status
108 }
bc7a96a8 109
073678b8
DA
110 translate_merge_tool_path () {
111 echo "$1"
112 }
113
114 if ! test -f "$MERGE_TOOLS_DIR/$tool"
bc7a96a8 115 then
62957bea
JK
116 # Use a special return code for this case since we want to
117 # source "defaults" even when an explicit tool path is
118 # configured since the user can use that to override the
119 # default path in the scriptlet.
120 return 2
bc7a96a8
DA
121 fi
122
123 # Load the redefined functions
073678b8 124 . "$MERGE_TOOLS_DIR/$tool"
bc7a96a8
DA
125
126 if merge_mode && ! can_merge
127 then
128 echo "error: '$tool' can not be used to resolve merges" >&2
62957bea 129 return 1
bc7a96a8
DA
130 elif diff_mode && ! can_diff
131 then
132 echo "error: '$tool' can only be used to resolve merges" >&2
62957bea 133 return 1
bc7a96a8
DA
134 fi
135 return 0
21d0ba7e
DA
136}
137
138get_merge_tool_cmd () {
bc7a96a8 139 merge_tool="$1"
240dc3e8
DA
140 if diff_mode
141 then
80ff2b68
DA
142 git config "difftool.$merge_tool.cmd" ||
143 git config "mergetool.$merge_tool.cmd"
47d65924 144 else
80ff2b68 145 git config "mergetool.$merge_tool.cmd"
47d65924 146 fi
21d0ba7e
DA
147}
148
bc7a96a8 149# Entry point for running tools
21d0ba7e 150run_merge_tool () {
f9ad901f
DA
151 # If GIT_PREFIX is empty then we cannot use it in tools
152 # that expect to be able to chdir() to its value.
153 GIT_PREFIX=${GIT_PREFIX:-.}
154 export GIT_PREFIX
155
80ff2b68 156 merge_tool_path=$(get_merge_tool_path "$1") || exit
21d0ba7e
DA
157 base_present="$2"
158 status=0
159
bc7a96a8
DA
160 # Bring tool-specific functions into scope
161 setup_tool "$1"
62957bea
JK
162 exitcode=$?
163 case $exitcode in
164 0)
165 :
166 ;;
167 2)
168 # The configured tool is not a built-in tool.
169 test -n "$merge_tool_path" || return 1
170 ;;
171 *)
172 return $exitcode
173 ;;
174 esac
bc7a96a8
DA
175
176 if merge_mode
177 then
a427ef7a 178 run_merge_cmd "$1"
bc7a96a8 179 else
a427ef7a 180 run_diff_cmd "$1"
bc7a96a8 181 fi
21d0ba7e
DA
182 return $status
183}
184
a427ef7a
DA
185# Run a either a configured or built-in diff tool
186run_diff_cmd () {
80ff2b68 187 merge_tool_cmd=$(get_merge_tool_cmd "$1")
a427ef7a
DA
188 if test -n "$merge_tool_cmd"
189 then
190 ( eval $merge_tool_cmd )
191 status=$?
192 return $status
193 else
194 diff_cmd "$1"
195 fi
196}
197
198# Run a either a configured or built-in merge tool
199run_merge_cmd () {
80ff2b68 200 merge_tool_cmd=$(get_merge_tool_cmd "$1")
a427ef7a
DA
201 if test -n "$merge_tool_cmd"
202 then
80ff2b68
DA
203 trust_exit_code=$(git config --bool \
204 "mergetool.$1.trustExitCode" || echo false)
a427ef7a
DA
205 if test "$trust_exit_code" = "false"
206 then
207 touch "$BACKUP"
208 ( eval $merge_tool_cmd )
209 status=$?
210 check_unchanged
211 else
212 ( eval $merge_tool_cmd )
213 status=$?
214 fi
215 return $status
216 else
217 merge_cmd "$1"
218 fi
219}
220
109859e2 221list_merge_tool_candidates () {
240dc3e8
DA
222 if merge_mode
223 then
21d0ba7e
DA
224 tools="tortoisemerge"
225 else
226 tools="kompare"
227 fi
240dc3e8
DA
228 if test -n "$DISPLAY"
229 then
230 if test -n "$GNOME_DESKTOP_SESSION_ID"
231 then
21d0ba7e
DA
232 tools="meld opendiff kdiff3 tkdiff xxdiff $tools"
233 else
234 tools="opendiff kdiff3 tkdiff xxdiff meld $tools"
235 fi
755e8b3f 236 tools="$tools gvimdiff diffuse ecmerge p4merge araxis bc3 codecompare"
21d0ba7e 237 fi
7b104229
RS
238 case "${VISUAL:-$EDITOR}" in
239 *vim*)
21d0ba7e 240 tools="$tools vimdiff emerge"
7b104229
RS
241 ;;
242 *)
21d0ba7e 243 tools="$tools emerge vimdiff"
7b104229
RS
244 ;;
245 esac
109859e2
JH
246}
247
4a8273a3 248show_tool_help () {
17a1f1c5 249 tool_opt="'git ${TOOL_MODE}tool --tool-<tool>'"
88d3406a 250
17a1f1c5
DA
251 tab=' '
252 LF='
253'
254 any_shown=no
62b6f7e0
JK
255
256 cmd_name=${TOOL_MODE}tool
17a1f1c5
DA
257 show_tool_names 'mode_ok && is_available' "$tab$tab" \
258 "$tool_opt may be set to one of the following:" \
259 "No suitable tool for 'git $cmd_name --tool=<tool>' found." &&
260 any_shown=yes
261
262 show_tool_names 'mode_ok && ! is_available' "$tab$tab" \
263 "${LF}The following tools are valid, but not currently available:" &&
264 any_shown=yes
265
266 if test "$any_shown" = yes
4a8273a3
JK
267 then
268 echo
269 echo "Some of the tools listed above only work in a windowed"
270 echo "environment. If run in a terminal-only session, they will fail."
271 fi
272 exit 0
273}
274
109859e2
JH
275guess_merge_tool () {
276 list_merge_tool_candidates
5338a6a9
DA
277 cat >&2 <<-EOF
278
279 This message is displayed because '$TOOL_MODE.tool' is not configured.
280 See 'git ${TOOL_MODE}tool --tool-help' or 'git help config' for more details.
281 'git ${TOOL_MODE}tool' will now attempt to use one of the following tools:
282 $tools
283 EOF
21d0ba7e
DA
284
285 # Loop over each candidate and stop when a valid merge tool is found.
17a1f1c5 286 for tool in $tools
21d0ba7e 287 do
17a1f1c5 288 is_available "$tool" && echo "$tool" && return 0
21d0ba7e
DA
289 done
290
17a1f1c5 291 echo >&2 "No known ${TOOL_MODE} tool is available."
47d65924 292 return 1
21d0ba7e
DA
293}
294
295get_configured_merge_tool () {
296 # Diff mode first tries diff.tool and falls back to merge.tool.
297 # Merge mode only checks merge.tool
240dc3e8
DA
298 if diff_mode
299 then
47d65924
DA
300 merge_tool=$(git config diff.tool || git config merge.tool)
301 else
302 merge_tool=$(git config merge.tool)
21d0ba7e 303 fi
240dc3e8
DA
304 if test -n "$merge_tool" && ! valid_tool "$merge_tool"
305 then
21d0ba7e
DA
306 echo >&2 "git config option $TOOL_MODE.tool set to unknown tool: $merge_tool"
307 echo >&2 "Resetting to default..."
308 return 1
309 fi
47d65924 310 echo "$merge_tool"
21d0ba7e
DA
311}
312
313get_merge_tool_path () {
314 # A merge tool has been set, so verify that it's valid.
bc7a96a8 315 merge_tool="$1"
240dc3e8
DA
316 if ! valid_tool "$merge_tool"
317 then
21d0ba7e
DA
318 echo >&2 "Unknown merge tool $merge_tool"
319 exit 1
320 fi
240dc3e8
DA
321 if diff_mode
322 then
47d65924 323 merge_tool_path=$(git config difftool."$merge_tool".path ||
285c6cbf 324 git config mergetool."$merge_tool".path)
47d65924
DA
325 else
326 merge_tool_path=$(git config mergetool."$merge_tool".path)
21d0ba7e 327 fi
240dc3e8
DA
328 if test -z "$merge_tool_path"
329 then
80ff2b68 330 merge_tool_path=$(translate_merge_tool_path "$merge_tool")
21d0ba7e 331 fi
47d65924 332 if test -z "$(get_merge_tool_cmd "$merge_tool")" &&
240dc3e8
DA
333 ! type "$merge_tool_path" >/dev/null 2>&1
334 then
47d65924 335 echo >&2 "The $TOOL_MODE tool $merge_tool is not available as"\
285c6cbf 336 "'$merge_tool_path'"
21d0ba7e
DA
337 exit 1
338 fi
339 echo "$merge_tool_path"
340}
341
342get_merge_tool () {
21d0ba7e 343 # Check if a merge tool has been configured
80ff2b68 344 merge_tool=$(get_configured_merge_tool)
21d0ba7e 345 # Try to guess an appropriate merge tool if no tool has been set.
240dc3e8
DA
346 if test -z "$merge_tool"
347 then
80ff2b68 348 merge_tool=$(guess_merge_tool) || exit
21d0ba7e
DA
349 fi
350 echo "$merge_tool"
351}