clean: typofix
[git] / git-mergetool--lib.sh
1 # git-mergetool--lib is a shell library for common merge tool functions
2
3 : ${MERGE_TOOLS_DIR=$(git --exec-path)/mergetools}
4
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
14         fi
15 }
16
17 is_available () {
18         merge_tool_path=$(translate_merge_tool_path "$1") &&
19         type "$merge_tool_path" >/dev/null 2>&1
20 }
21
22 list_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
36 show_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
80 diff_mode() {
81         test "$TOOL_MODE" = diff
82 }
83
84 merge_mode() {
85         test "$TOOL_MODE" = merge
86 }
87
88 translate_merge_tool_path () {
89         echo "$1"
90 }
91
92 check_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
110 valid_tool () {
111         setup_tool "$1" && return 0
112         cmd=$(get_merge_tool_cmd "$1")
113         test -n "$cmd"
114 }
115
116 setup_user_tool () {
117         merge_tool_cmd=$(get_merge_tool_cmd "$tool")
118         test -n "$merge_tool_cmd" || return 1
119
120         diff_cmd () {
121                 ( eval $merge_tool_cmd )
122                 status=$?
123                 return $status
124         }
125
126         merge_cmd () {
127                 trust_exit_code=$(git config --bool \
128                         "mergetool.$1.trustExitCode" || echo false)
129                 if test "$trust_exit_code" = "false"
130                 then
131                         touch "$BACKUP"
132                         ( eval $merge_tool_cmd )
133                         status=$?
134                         check_unchanged
135                 else
136                         ( eval $merge_tool_cmd )
137                         status=$?
138                 fi
139                 return $status
140         }
141 }
142
143 setup_tool () {
144         tool="$1"
145
146         # Fallback definitions, to be overridden by tools.
147         can_merge () {
148                 return 0
149         }
150
151         can_diff () {
152                 return 0
153         }
154
155         diff_cmd () {
156                 status=1
157                 return $status
158         }
159
160         merge_cmd () {
161                 status=1
162                 return $status
163         }
164
165         translate_merge_tool_path () {
166                 echo "$1"
167         }
168
169         if ! test -f "$MERGE_TOOLS_DIR/$tool"
170         then
171                 setup_user_tool
172                 return $?
173         fi
174
175         # Load the redefined functions
176         . "$MERGE_TOOLS_DIR/$tool"
177         # Now let the user override the default command for the tool.  If
178         # they have not done so then this will return 1 which we ignore.
179         setup_user_tool
180
181         if merge_mode && ! can_merge
182         then
183                 echo "error: '$tool' can not be used to resolve merges" >&2
184                 return 1
185         elif diff_mode && ! can_diff
186         then
187                 echo "error: '$tool' can only be used to resolve merges" >&2
188                 return 1
189         fi
190         return 0
191 }
192
193 get_merge_tool_cmd () {
194         merge_tool="$1"
195         if diff_mode
196         then
197                 git config "difftool.$merge_tool.cmd" ||
198                 git config "mergetool.$merge_tool.cmd"
199         else
200                 git config "mergetool.$merge_tool.cmd"
201         fi
202 }
203
204 # Entry point for running tools
205 run_merge_tool () {
206         # If GIT_PREFIX is empty then we cannot use it in tools
207         # that expect to be able to chdir() to its value.
208         GIT_PREFIX=${GIT_PREFIX:-.}
209         export GIT_PREFIX
210
211         merge_tool_path=$(get_merge_tool_path "$1") || exit
212         base_present="$2"
213         status=0
214
215         # Bring tool-specific functions into scope
216         setup_tool "$1" || return 1
217
218         if merge_mode
219         then
220                 run_merge_cmd "$1"
221         else
222                 run_diff_cmd "$1"
223         fi
224         status=$?
225         return $status
226 }
227
228 # Run a either a configured or built-in diff tool
229 run_diff_cmd () {
230         diff_cmd "$1"
231 }
232
233 # Run a either a configured or built-in merge tool
234 run_merge_cmd () {
235         merge_cmd "$1"
236 }
237
238 list_merge_tool_candidates () {
239         if merge_mode
240         then
241                 tools="tortoisemerge"
242         else
243                 tools="kompare"
244         fi
245         if test -n "$DISPLAY"
246         then
247                 if test -n "$GNOME_DESKTOP_SESSION_ID"
248                 then
249                         tools="meld opendiff kdiff3 tkdiff xxdiff $tools"
250                 else
251                         tools="opendiff kdiff3 tkdiff xxdiff meld $tools"
252                 fi
253                 tools="$tools gvimdiff diffuse diffmerge ecmerge"
254                 tools="$tools p4merge araxis bc codecompare"
255         fi
256         case "${VISUAL:-$EDITOR}" in
257         *vim*)
258                 tools="$tools vimdiff emerge"
259                 ;;
260         *)
261                 tools="$tools emerge vimdiff"
262                 ;;
263         esac
264 }
265
266 show_tool_help () {
267         tool_opt="'git ${TOOL_MODE}tool --tool=<tool>'"
268
269         tab='   '
270         LF='
271 '
272         any_shown=no
273
274         cmd_name=${TOOL_MODE}tool
275         config_tools=$({
276                 diff_mode && list_config_tools difftool "$tab$tab"
277                 list_config_tools mergetool "$tab$tab"
278         } | sort)
279         extra_content=
280         if test -n "$config_tools"
281         then
282                 extra_content="${tab}user-defined:${LF}$config_tools"
283         fi
284
285         show_tool_names 'mode_ok && is_available' "$tab$tab" \
286                 "$tool_opt may be set to one of the following:" \
287                 "No suitable tool for 'git $cmd_name --tool=<tool>' found." \
288                 "$extra_content" &&
289                 any_shown=yes
290
291         show_tool_names 'mode_ok && ! is_available' "$tab$tab" \
292                 "${LF}The following tools are valid, but not currently available:" &&
293                 any_shown=yes
294
295         if test "$any_shown" = yes
296         then
297                 echo
298                 echo "Some of the tools listed above only work in a windowed"
299                 echo "environment. If run in a terminal-only session, they will fail."
300         fi
301         exit 0
302 }
303
304 guess_merge_tool () {
305         list_merge_tool_candidates
306         cat >&2 <<-EOF
307
308         This message is displayed because '$TOOL_MODE.tool' is not configured.
309         See 'git ${TOOL_MODE}tool --tool-help' or 'git help config' for more details.
310         'git ${TOOL_MODE}tool' will now attempt to use one of the following tools:
311         $tools
312         EOF
313
314         # Loop over each candidate and stop when a valid merge tool is found.
315         for tool in $tools
316         do
317                 is_available "$tool" && echo "$tool" && return 0
318         done
319
320         echo >&2 "No known ${TOOL_MODE} tool is available."
321         return 1
322 }
323
324 get_configured_merge_tool () {
325         # Diff mode first tries diff.tool and falls back to merge.tool.
326         # Merge mode only checks merge.tool
327         if diff_mode
328         then
329                 merge_tool=$(git config diff.tool || git config merge.tool)
330         else
331                 merge_tool=$(git config merge.tool)
332         fi
333         if test -n "$merge_tool" && ! valid_tool "$merge_tool"
334         then
335                 echo >&2 "git config option $TOOL_MODE.tool set to unknown tool: $merge_tool"
336                 echo >&2 "Resetting to default..."
337                 return 1
338         fi
339         echo "$merge_tool"
340 }
341
342 get_merge_tool_path () {
343         # A merge tool has been set, so verify that it's valid.
344         merge_tool="$1"
345         if ! valid_tool "$merge_tool"
346         then
347                 echo >&2 "Unknown merge tool $merge_tool"
348                 exit 1
349         fi
350         if diff_mode
351         then
352                 merge_tool_path=$(git config difftool."$merge_tool".path ||
353                                   git config mergetool."$merge_tool".path)
354         else
355                 merge_tool_path=$(git config mergetool."$merge_tool".path)
356         fi
357         if test -z "$merge_tool_path"
358         then
359                 merge_tool_path=$(translate_merge_tool_path "$merge_tool")
360         fi
361         if test -z "$(get_merge_tool_cmd "$merge_tool")" &&
362                 ! type "$merge_tool_path" >/dev/null 2>&1
363         then
364                 echo >&2 "The $TOOL_MODE tool $merge_tool is not available as"\
365                          "'$merge_tool_path'"
366                 exit 1
367         fi
368         echo "$merge_tool_path"
369 }
370
371 get_merge_tool () {
372         # Check if a merge tool has been configured
373         merge_tool=$(get_configured_merge_tool)
374         # Try to guess an appropriate merge tool if no tool has been set.
375         if test -z "$merge_tool"
376         then
377                 merge_tool=$(guess_merge_tool) || exit
378         fi
379         echo "$merge_tool"
380 }