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