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