mergetool--lib: simplify command expressions
[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         echo >&2 "merge tool candidates: $tools"
244
245         # Loop over each candidate and stop when a valid merge tool is found.
246         for i in $tools
247         do
248                 merge_tool_path=$(translate_merge_tool_path "$i")
249                 if type "$merge_tool_path" >/dev/null 2>&1
250                 then
251                         echo "$i"
252                         return 0
253                 fi
254         done
255
256         echo >&2 "No known merge resolution program available."
257         return 1
258 }
259
260 get_configured_merge_tool () {
261         # Diff mode first tries diff.tool and falls back to merge.tool.
262         # Merge mode only checks merge.tool
263         if diff_mode
264         then
265                 merge_tool=$(git config diff.tool || git config merge.tool)
266         else
267                 merge_tool=$(git config merge.tool)
268         fi
269         if test -n "$merge_tool" && ! valid_tool "$merge_tool"
270         then
271                 echo >&2 "git config option $TOOL_MODE.tool set to unknown tool: $merge_tool"
272                 echo >&2 "Resetting to default..."
273                 return 1
274         fi
275         echo "$merge_tool"
276 }
277
278 get_merge_tool_path () {
279         # A merge tool has been set, so verify that it's valid.
280         merge_tool="$1"
281         if ! valid_tool "$merge_tool"
282         then
283                 echo >&2 "Unknown merge tool $merge_tool"
284                 exit 1
285         fi
286         if diff_mode
287         then
288                 merge_tool_path=$(git config difftool."$merge_tool".path ||
289                                   git config mergetool."$merge_tool".path)
290         else
291                 merge_tool_path=$(git config mergetool."$merge_tool".path)
292         fi
293         if test -z "$merge_tool_path"
294         then
295                 merge_tool_path=$(translate_merge_tool_path "$merge_tool")
296         fi
297         if test -z "$(get_merge_tool_cmd "$merge_tool")" &&
298                 ! type "$merge_tool_path" >/dev/null 2>&1
299         then
300                 echo >&2 "The $TOOL_MODE tool $merge_tool is not available as"\
301                          "'$merge_tool_path'"
302                 exit 1
303         fi
304         echo "$merge_tool_path"
305 }
306
307 get_merge_tool () {
308         # Check if a merge tool has been configured
309         merge_tool=$(get_configured_merge_tool)
310         # Try to guess an appropriate merge tool if no tool has been set.
311         if test -z "$merge_tool"
312         then
313                 merge_tool=$(guess_merge_tool) || exit
314         fi
315         echo "$merge_tool"
316 }