mergetool--lib: add functions for finding available tools
[alt-git.git] / git-mergetool--lib.sh
blob293b8e8a9330a8f70afc659dc7e38cc1441db2a5
1 #!/bin/sh
2 # git-mergetool--lib is a library for common merge tool functions
3 MERGE_TOOLS_DIR=$(git --exec-path)/mergetools
5 mode_ok () {
6 if diff_mode
7 then
8 can_diff
9 elif merge_mode
10 then
11 can_merge
12 else
13 false
17 is_available () {
18 merge_tool_path=$(translate_merge_tool_path "$1") &&
19 type "$merge_tool_path" >/dev/null 2>&1
22 show_tool_names () {
23 condition=${1:-true} per_line_prefix=${2:-} preamble=${3:-}
24 not_found_msg=${4:-}
26 shown_any=
27 ( cd "$MERGE_TOOLS_DIR" && ls ) | {
28 while read toolname
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=
38 shown_any=yes
39 printf "%s%s\n" "$per_line_prefix" "$toolname"
41 done
43 if test -n "$preamble" && test -n "$not_found_msg"
44 then
45 printf "%s\n" "$not_found_msg"
48 test -n "$shown_any"
52 diff_mode() {
53 test "$TOOL_MODE" = diff
56 merge_mode() {
57 test "$TOOL_MODE" = merge
60 translate_merge_tool_path () {
61 echo "$1"
64 check_unchanged () {
65 if test "$MERGED" -nt "$BACKUP"
66 then
67 status=0
68 else
69 while true
71 echo "$MERGED seems unchanged."
72 printf "Was the merge successful? [y/n] "
73 read answer || return 1
74 case "$answer" in
75 y*|Y*) status=0; break ;;
76 n*|N*) status=1; break ;;
77 esac
78 done
82 valid_tool () {
83 setup_tool "$1" && return 0
84 cmd=$(get_merge_tool_cmd "$1")
85 test -n "$cmd"
88 setup_tool () {
89 tool="$1"
91 # Fallback definitions, to be overriden by tools.
92 can_merge () {
93 return 0
96 can_diff () {
97 return 0
100 diff_cmd () {
101 status=1
102 return $status
105 merge_cmd () {
106 status=1
107 return $status
110 translate_merge_tool_path () {
111 echo "$1"
114 if ! test -f "$MERGE_TOOLS_DIR/$tool"
115 then
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
123 # Load the redefined functions
124 . "$MERGE_TOOLS_DIR/$tool"
126 if merge_mode && ! can_merge
127 then
128 echo "error: '$tool' can not be used to resolve merges" >&2
129 return 1
130 elif diff_mode && ! can_diff
131 then
132 echo "error: '$tool' can only be used to resolve merges" >&2
133 return 1
135 return 0
138 get_merge_tool_cmd () {
139 merge_tool="$1"
140 if diff_mode
141 then
142 git config "difftool.$merge_tool.cmd" ||
143 git config "mergetool.$merge_tool.cmd"
144 else
145 git config "mergetool.$merge_tool.cmd"
149 # Entry point for running tools
150 run_merge_tool () {
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
156 merge_tool_path=$(get_merge_tool_path "$1") || exit
157 base_present="$2"
158 status=0
160 # Bring tool-specific functions into scope
161 setup_tool "$1"
162 exitcode=$?
163 case $exitcode in
168 # The configured tool is not a built-in tool.
169 test -n "$merge_tool_path" || return 1
172 return $exitcode
174 esac
176 if merge_mode
177 then
178 run_merge_cmd "$1"
179 else
180 run_diff_cmd "$1"
182 return $status
185 # Run a either a configured or built-in diff tool
186 run_diff_cmd () {
187 merge_tool_cmd=$(get_merge_tool_cmd "$1")
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"
198 # Run a either a configured or built-in merge tool
199 run_merge_cmd () {
200 merge_tool_cmd=$(get_merge_tool_cmd "$1")
201 if test -n "$merge_tool_cmd"
202 then
203 trust_exit_code=$(git config --bool \
204 "mergetool.$1.trustExitCode" || echo false)
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=$?
215 return $status
216 else
217 merge_cmd "$1"
221 list_merge_tool_candidates () {
222 if merge_mode
223 then
224 tools="tortoisemerge"
225 else
226 tools="kompare"
228 if test -n "$DISPLAY"
229 then
230 if test -n "$GNOME_DESKTOP_SESSION_ID"
231 then
232 tools="meld opendiff kdiff3 tkdiff xxdiff $tools"
233 else
234 tools="opendiff kdiff3 tkdiff xxdiff meld $tools"
236 tools="$tools gvimdiff diffuse ecmerge p4merge araxis bc3 codecompare"
238 case "${VISUAL:-$EDITOR}" in
239 *vim*)
240 tools="$tools vimdiff emerge"
243 tools="$tools emerge vimdiff"
245 esac
248 show_tool_help () {
249 tool_opt="'git ${TOOL_MODE}tool --tool-<tool>'"
251 tab=' '
252 LF='
254 any_shown=no
256 cmd_name=${TOOL_MODE}tool
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
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
266 if test "$any_shown" = yes
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."
272 exit 0
275 guess_merge_tool () {
276 list_merge_tool_candidates
277 cat >&2 <<-EOF
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
285 # Loop over each candidate and stop when a valid merge tool is found.
286 for tool in $tools
288 is_available "$tool" && echo "$tool" && return 0
289 done
291 echo >&2 "No known ${TOOL_MODE} tool is available."
292 return 1
295 get_configured_merge_tool () {
296 # Diff mode first tries diff.tool and falls back to merge.tool.
297 # Merge mode only checks merge.tool
298 if diff_mode
299 then
300 merge_tool=$(git config diff.tool || git config merge.tool)
301 else
302 merge_tool=$(git config merge.tool)
304 if test -n "$merge_tool" && ! valid_tool "$merge_tool"
305 then
306 echo >&2 "git config option $TOOL_MODE.tool set to unknown tool: $merge_tool"
307 echo >&2 "Resetting to default..."
308 return 1
310 echo "$merge_tool"
313 get_merge_tool_path () {
314 # A merge tool has been set, so verify that it's valid.
315 merge_tool="$1"
316 if ! valid_tool "$merge_tool"
317 then
318 echo >&2 "Unknown merge tool $merge_tool"
319 exit 1
321 if diff_mode
322 then
323 merge_tool_path=$(git config difftool."$merge_tool".path ||
324 git config mergetool."$merge_tool".path)
325 else
326 merge_tool_path=$(git config mergetool."$merge_tool".path)
328 if test -z "$merge_tool_path"
329 then
330 merge_tool_path=$(translate_merge_tool_path "$merge_tool")
332 if test -z "$(get_merge_tool_cmd "$merge_tool")" &&
333 ! type "$merge_tool_path" >/dev/null 2>&1
334 then
335 echo >&2 "The $TOOL_MODE tool $merge_tool is not available as"\
336 "'$merge_tool_path'"
337 exit 1
339 echo "$merge_tool_path"
342 get_merge_tool () {
343 # Check if a merge tool has been configured
344 merge_tool=$(get_configured_merge_tool)
345 # Try to guess an appropriate merge tool if no tool has been set.
346 if test -z "$merge_tool"
347 then
348 merge_tool=$(guess_merge_tool) || exit
350 echo "$merge_tool"