Merge branch 'mm/maint-hint-failed-merge' into maint
[git] / git-mergetool--lib.sh
1 # git-mergetool--lib is a library for common merge tool functions
2 diff_mode() {
3         test "$TOOL_MODE" = diff
4 }
5
6 merge_mode() {
7         test "$TOOL_MODE" = merge
8 }
9
10 translate_merge_tool_path () {
11         case "$1" in
12         vimdiff)
13                 echo vim
14                 ;;
15         gvimdiff)
16                 echo gvim
17                 ;;
18         emerge)
19                 echo emacs
20                 ;;
21         araxis)
22                 echo compare
23                 ;;
24         *)
25                 echo "$1"
26                 ;;
27         esac
28 }
29
30 check_unchanged () {
31         if test "$MERGED" -nt "$BACKUP"; then
32                 status=0
33         else
34                 while true; do
35                         echo "$MERGED seems unchanged."
36                         printf "Was the merge successful? [y/n] "
37                         read answer < /dev/tty
38                         case "$answer" in
39                         y*|Y*) status=0; break ;;
40                         n*|N*) status=1; break ;;
41                         esac
42                 done
43         fi
44 }
45
46 valid_tool () {
47         case "$1" in
48         kdiff3 | tkdiff | xxdiff | meld | opendiff | \
49         emerge | vimdiff | gvimdiff | ecmerge | diffuse | araxis)
50                 ;; # happy
51         tortoisemerge)
52                 if ! merge_mode; then
53                         return 1
54                 fi
55                 ;;
56         kompare)
57                 if ! diff_mode; then
58                         return 1
59                 fi
60                 ;;
61         *)
62                 if test -z "$(get_merge_tool_cmd "$1")"; then
63                         return 1
64                 fi
65                 ;;
66         esac
67 }
68
69 get_merge_tool_cmd () {
70         # Prints the custom command for a merge tool
71         if test -n "$1"; then
72                 merge_tool="$1"
73         else
74                 merge_tool="$(get_merge_tool)"
75         fi
76         if diff_mode; then
77                 echo "$(git config difftool.$merge_tool.cmd ||
78                         git config mergetool.$merge_tool.cmd)"
79         else
80                 echo "$(git config mergetool.$merge_tool.cmd)"
81         fi
82 }
83
84 run_merge_tool () {
85         merge_tool_path="$(get_merge_tool_path "$1")" || exit
86         base_present="$2"
87         status=0
88
89         case "$1" in
90         kdiff3)
91                 if merge_mode; then
92                         if $base_present; then
93                                 ("$merge_tool_path" --auto \
94                                         --L1 "$MERGED (Base)" \
95                                         --L2 "$MERGED (Local)" \
96                                         --L3 "$MERGED (Remote)" \
97                                         -o "$MERGED" \
98                                         "$BASE" "$LOCAL" "$REMOTE" \
99                                 > /dev/null 2>&1)
100                         else
101                                 ("$merge_tool_path" --auto \
102                                         --L1 "$MERGED (Local)" \
103                                         --L2 "$MERGED (Remote)" \
104                                         -o "$MERGED" \
105                                         "$LOCAL" "$REMOTE" \
106                                 > /dev/null 2>&1)
107                         fi
108                         status=$?
109                 else
110                         ("$merge_tool_path" --auto \
111                                 --L1 "$MERGED (A)" \
112                                 --L2 "$MERGED (B)" "$LOCAL" "$REMOTE" \
113                         > /dev/null 2>&1)
114                 fi
115                 ;;
116         kompare)
117                 "$merge_tool_path" "$LOCAL" "$REMOTE"
118                 ;;
119         tkdiff)
120                 if merge_mode; then
121                         if $base_present; then
122                                 "$merge_tool_path" -a "$BASE" \
123                                         -o "$MERGED" "$LOCAL" "$REMOTE"
124                         else
125                                 "$merge_tool_path" \
126                                         -o "$MERGED" "$LOCAL" "$REMOTE"
127                         fi
128                         status=$?
129                 else
130                         "$merge_tool_path" "$LOCAL" "$REMOTE"
131                 fi
132                 ;;
133         meld)
134                 if merge_mode; then
135                         touch "$BACKUP"
136                         "$merge_tool_path" "$LOCAL" "$MERGED" "$REMOTE"
137                         check_unchanged
138                 else
139                         "$merge_tool_path" "$LOCAL" "$REMOTE"
140                 fi
141                 ;;
142         diffuse)
143                 if merge_mode; then
144                         touch "$BACKUP"
145                         if $base_present; then
146                                 "$merge_tool_path" \
147                                         "$LOCAL" "$MERGED" "$REMOTE" \
148                                         "$BASE" | cat
149                         else
150                                 "$merge_tool_path" \
151                                         "$LOCAL" "$MERGED" "$REMOTE" | cat
152                         fi
153                         check_unchanged
154                 else
155                         "$merge_tool_path" "$LOCAL" "$REMOTE" | cat
156                 fi
157                 ;;
158         vimdiff)
159                 if merge_mode; then
160                         touch "$BACKUP"
161                         "$merge_tool_path" -d -c "wincmd l" \
162                                 "$LOCAL" "$MERGED" "$REMOTE"
163                         check_unchanged
164                 else
165                         "$merge_tool_path" -d -c "wincmd l" \
166                                 "$LOCAL" "$REMOTE"
167                 fi
168                 ;;
169         gvimdiff)
170                 if merge_mode; then
171                         touch "$BACKUP"
172                         "$merge_tool_path" -d -c "wincmd l" -f \
173                                 "$LOCAL" "$MERGED" "$REMOTE"
174                         check_unchanged
175                 else
176                         "$merge_tool_path" -d -c "wincmd l" -f \
177                                 "$LOCAL" "$REMOTE"
178                 fi
179                 ;;
180         xxdiff)
181                 if merge_mode; then
182                         touch "$BACKUP"
183                         if $base_present; then
184                                 "$merge_tool_path" -X --show-merged-pane \
185                                         -R 'Accel.SaveAsMerged: "Ctrl-S"' \
186                                         -R 'Accel.Search: "Ctrl+F"' \
187                                         -R 'Accel.SearchForward: "Ctrl-G"' \
188                                         --merged-file "$MERGED" \
189                                         "$LOCAL" "$BASE" "$REMOTE"
190                         else
191                                 "$merge_tool_path" -X $extra \
192                                         -R 'Accel.SaveAsMerged: "Ctrl-S"' \
193                                         -R 'Accel.Search: "Ctrl+F"' \
194                                         -R 'Accel.SearchForward: "Ctrl-G"' \
195                                         --merged-file "$MERGED" \
196                                         "$LOCAL" "$REMOTE"
197                         fi
198                         check_unchanged
199                 else
200                         "$merge_tool_path" \
201                                 -R 'Accel.Search: "Ctrl+F"' \
202                                 -R 'Accel.SearchForward: "Ctrl-G"' \
203                                 "$LOCAL" "$REMOTE"
204                 fi
205                 ;;
206         opendiff)
207                 if merge_mode; then
208                         touch "$BACKUP"
209                         if $base_present; then
210                                 "$merge_tool_path" "$LOCAL" "$REMOTE" \
211                                         -ancestor "$BASE" \
212                                         -merge "$MERGED" | cat
213                         else
214                                 "$merge_tool_path" "$LOCAL" "$REMOTE" \
215                                         -merge "$MERGED" | cat
216                         fi
217                         check_unchanged
218                 else
219                         "$merge_tool_path" "$LOCAL" "$REMOTE" | cat
220                 fi
221                 ;;
222         ecmerge)
223                 if merge_mode; then
224                         touch "$BACKUP"
225                         if $base_present; then
226                                 "$merge_tool_path" "$BASE" "$LOCAL" "$REMOTE" \
227                                         --default --mode=merge3 --to="$MERGED"
228                         else
229                                 "$merge_tool_path" "$LOCAL" "$REMOTE" \
230                                         --default --mode=merge2 --to="$MERGED"
231                         fi
232                         check_unchanged
233                 else
234                         "$merge_tool_path" --default --mode=diff2 \
235                                 "$LOCAL" "$REMOTE"
236                 fi
237                 ;;
238         emerge)
239                 if merge_mode; then
240                         if $base_present; then
241                                 "$merge_tool_path" \
242                                         -f emerge-files-with-ancestor-command \
243                                         "$LOCAL" "$REMOTE" "$BASE" \
244                                         "$(basename "$MERGED")"
245                         else
246                                 "$merge_tool_path" \
247                                         -f emerge-files-command \
248                                         "$LOCAL" "$REMOTE" \
249                                         "$(basename "$MERGED")"
250                         fi
251                         status=$?
252                 else
253                         "$merge_tool_path" -f emerge-files-command \
254                                 "$LOCAL" "$REMOTE"
255                 fi
256                 ;;
257         tortoisemerge)
258                 if $base_present; then
259                         touch "$BACKUP"
260                         "$merge_tool_path" \
261                                 -base:"$BASE" -mine:"$LOCAL" \
262                                 -theirs:"$REMOTE" -merged:"$MERGED"
263                         check_unchanged
264                 else
265                         echo "TortoiseMerge cannot be used without a base" 1>&2
266                         status=1
267                 fi
268                 ;;
269         araxis)
270                 if merge_mode; then
271                         touch "$BACKUP"
272                         if $base_present; then
273                                 "$merge_tool_path" -wait -merge -3 -a1 \
274                                         "$BASE" "$LOCAL" "$REMOTE" "$MERGED" \
275                                         >/dev/null 2>&1
276                         else
277                                 "$merge_tool_path" -wait -2 \
278                                         "$LOCAL" "$REMOTE" "$MERGED" \
279                                         >/dev/null 2>&1
280                         fi
281                         check_unchanged
282                 else
283                         "$merge_tool_path" -wait -2 "$LOCAL" "$REMOTE" \
284                                 >/dev/null 2>&1
285                 fi
286                 ;;
287         *)
288                 merge_tool_cmd="$(get_merge_tool_cmd "$1")"
289                 if test -z "$merge_tool_cmd"; then
290                         if merge_mode; then
291                                 status=1
292                         fi
293                         break
294                 fi
295                 if merge_mode; then
296                         trust_exit_code="$(git config --bool \
297                                 mergetool."$1".trustExitCode || echo false)"
298                         if test "$trust_exit_code" = "false"; then
299                                 touch "$BACKUP"
300                                 ( eval $merge_tool_cmd )
301                                 check_unchanged
302                         else
303                                 ( eval $merge_tool_cmd )
304                                 status=$?
305                         fi
306                 else
307                         ( eval $merge_tool_cmd )
308                 fi
309                 ;;
310         esac
311         return $status
312 }
313
314 guess_merge_tool () {
315         if merge_mode; then
316                 tools="tortoisemerge"
317         else
318                 tools="kompare"
319         fi
320         if test -n "$DISPLAY"; then
321                 if test -n "$GNOME_DESKTOP_SESSION_ID" ; then
322                         tools="meld opendiff kdiff3 tkdiff xxdiff $tools"
323                 else
324                         tools="opendiff kdiff3 tkdiff xxdiff meld $tools"
325                 fi
326                 tools="$tools gvimdiff diffuse ecmerge araxis"
327         fi
328         if echo "${VISUAL:-$EDITOR}" | grep emacs > /dev/null 2>&1; then
329                 # $EDITOR is emacs so add emerge as a candidate
330                 tools="$tools emerge vimdiff"
331         elif echo "${VISUAL:-$EDITOR}" | grep vim > /dev/null 2>&1; then
332                 # $EDITOR is vim so add vimdiff as a candidate
333                 tools="$tools vimdiff emerge"
334         else
335                 tools="$tools emerge vimdiff"
336         fi
337         echo >&2 "merge tool candidates: $tools"
338
339         # Loop over each candidate and stop when a valid merge tool is found.
340         for i in $tools
341         do
342                 merge_tool_path="$(translate_merge_tool_path "$i")"
343                 if type "$merge_tool_path" > /dev/null 2>&1; then
344                         echo "$i"
345                         return 0
346                 fi
347         done
348
349         echo >&2 "No known merge resolution program available."
350         return 1
351 }
352
353 get_configured_merge_tool () {
354         # Diff mode first tries diff.tool and falls back to merge.tool.
355         # Merge mode only checks merge.tool
356         if diff_mode; then
357                 merge_tool=$(git config diff.tool || git config merge.tool)
358         else
359                 merge_tool=$(git config merge.tool)
360         fi
361         if test -n "$merge_tool" && ! valid_tool "$merge_tool"; then
362                 echo >&2 "git config option $TOOL_MODE.tool set to unknown tool: $merge_tool"
363                 echo >&2 "Resetting to default..."
364                 return 1
365         fi
366         echo "$merge_tool"
367 }
368
369 get_merge_tool_path () {
370         # A merge tool has been set, so verify that it's valid.
371         if test -n "$1"; then
372                 merge_tool="$1"
373         else
374                 merge_tool="$(get_merge_tool)"
375         fi
376         if ! valid_tool "$merge_tool"; then
377                 echo >&2 "Unknown merge tool $merge_tool"
378                 exit 1
379         fi
380         if diff_mode; then
381                 merge_tool_path=$(git config difftool."$merge_tool".path ||
382                                   git config mergetool."$merge_tool".path)
383         else
384                 merge_tool_path=$(git config mergetool."$merge_tool".path)
385         fi
386         if test -z "$merge_tool_path"; then
387                 merge_tool_path="$(translate_merge_tool_path "$merge_tool")"
388         fi
389         if test -z "$(get_merge_tool_cmd "$merge_tool")" &&
390         ! type "$merge_tool_path" > /dev/null 2>&1; then
391                 echo >&2 "The $TOOL_MODE tool $merge_tool is not available as"\
392                          "'$merge_tool_path'"
393                 exit 1
394         fi
395         echo "$merge_tool_path"
396 }
397
398 get_merge_tool () {
399         # Check if a merge tool has been configured
400         merge_tool=$(get_configured_merge_tool)
401         # Try to guess an appropriate merge tool if no tool has been set.
402         if test -z "$merge_tool"; then
403                 merge_tool="$(guess_merge_tool)" || exit
404         fi
405         echo "$merge_tool"
406 }