Merge branch 'maint-1.6.5' into maint-1.6.6
[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 | p4merge)
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         p4merge)
134                 if merge_mode; then
135                     touch "$BACKUP"
136                         if $base_present; then
137                                 "$merge_tool_path" "$BASE" "$LOCAL" "$REMOTE" "$MERGED"
138                         else
139                                 "$merge_tool_path" "$LOCAL" "$LOCAL" "$REMOTE" "$MERGED"
140                         fi
141                         check_unchanged
142                 else
143                         "$merge_tool_path" "$LOCAL" "$REMOTE"
144                 fi
145                 ;;
146         meld)
147                 if merge_mode; then
148                         touch "$BACKUP"
149                         "$merge_tool_path" "$LOCAL" "$MERGED" "$REMOTE"
150                         check_unchanged
151                 else
152                         "$merge_tool_path" "$LOCAL" "$REMOTE"
153                 fi
154                 ;;
155         diffuse)
156                 if merge_mode; then
157                         touch "$BACKUP"
158                         if $base_present; then
159                                 "$merge_tool_path" \
160                                         "$LOCAL" "$MERGED" "$REMOTE" \
161                                         "$BASE" | cat
162                         else
163                                 "$merge_tool_path" \
164                                         "$LOCAL" "$MERGED" "$REMOTE" | cat
165                         fi
166                         check_unchanged
167                 else
168                         "$merge_tool_path" "$LOCAL" "$REMOTE" | cat
169                 fi
170                 ;;
171         vimdiff)
172                 if merge_mode; then
173                         touch "$BACKUP"
174                         "$merge_tool_path" -d -c "wincmd l" \
175                                 "$LOCAL" "$MERGED" "$REMOTE"
176                         check_unchanged
177                 else
178                         "$merge_tool_path" -d -c "wincmd l" \
179                                 "$LOCAL" "$REMOTE"
180                 fi
181                 ;;
182         gvimdiff)
183                 if merge_mode; then
184                         touch "$BACKUP"
185                         "$merge_tool_path" -d -c "wincmd l" -f \
186                                 "$LOCAL" "$MERGED" "$REMOTE"
187                         check_unchanged
188                 else
189                         "$merge_tool_path" -d -c "wincmd l" -f \
190                                 "$LOCAL" "$REMOTE"
191                 fi
192                 ;;
193         xxdiff)
194                 if merge_mode; then
195                         touch "$BACKUP"
196                         if $base_present; then
197                                 "$merge_tool_path" -X --show-merged-pane \
198                                         -R 'Accel.SaveAsMerged: "Ctrl-S"' \
199                                         -R 'Accel.Search: "Ctrl+F"' \
200                                         -R 'Accel.SearchForward: "Ctrl-G"' \
201                                         --merged-file "$MERGED" \
202                                         "$LOCAL" "$BASE" "$REMOTE"
203                         else
204                                 "$merge_tool_path" -X $extra \
205                                         -R 'Accel.SaveAsMerged: "Ctrl-S"' \
206                                         -R 'Accel.Search: "Ctrl+F"' \
207                                         -R 'Accel.SearchForward: "Ctrl-G"' \
208                                         --merged-file "$MERGED" \
209                                         "$LOCAL" "$REMOTE"
210                         fi
211                         check_unchanged
212                 else
213                         "$merge_tool_path" \
214                                 -R 'Accel.Search: "Ctrl+F"' \
215                                 -R 'Accel.SearchForward: "Ctrl-G"' \
216                                 "$LOCAL" "$REMOTE"
217                 fi
218                 ;;
219         opendiff)
220                 if merge_mode; then
221                         touch "$BACKUP"
222                         if $base_present; then
223                                 "$merge_tool_path" "$LOCAL" "$REMOTE" \
224                                         -ancestor "$BASE" \
225                                         -merge "$MERGED" | cat
226                         else
227                                 "$merge_tool_path" "$LOCAL" "$REMOTE" \
228                                         -merge "$MERGED" | cat
229                         fi
230                         check_unchanged
231                 else
232                         "$merge_tool_path" "$LOCAL" "$REMOTE" | cat
233                 fi
234                 ;;
235         ecmerge)
236                 if merge_mode; then
237                         touch "$BACKUP"
238                         if $base_present; then
239                                 "$merge_tool_path" "$BASE" "$LOCAL" "$REMOTE" \
240                                         --default --mode=merge3 --to="$MERGED"
241                         else
242                                 "$merge_tool_path" "$LOCAL" "$REMOTE" \
243                                         --default --mode=merge2 --to="$MERGED"
244                         fi
245                         check_unchanged
246                 else
247                         "$merge_tool_path" --default --mode=diff2 \
248                                 "$LOCAL" "$REMOTE"
249                 fi
250                 ;;
251         emerge)
252                 if merge_mode; then
253                         if $base_present; then
254                                 "$merge_tool_path" \
255                                         -f emerge-files-with-ancestor-command \
256                                         "$LOCAL" "$REMOTE" "$BASE" \
257                                         "$(basename "$MERGED")"
258                         else
259                                 "$merge_tool_path" \
260                                         -f emerge-files-command \
261                                         "$LOCAL" "$REMOTE" \
262                                         "$(basename "$MERGED")"
263                         fi
264                         status=$?
265                 else
266                         "$merge_tool_path" -f emerge-files-command \
267                                 "$LOCAL" "$REMOTE"
268                 fi
269                 ;;
270         tortoisemerge)
271                 if $base_present; then
272                         touch "$BACKUP"
273                         "$merge_tool_path" \
274                                 -base:"$BASE" -mine:"$LOCAL" \
275                                 -theirs:"$REMOTE" -merged:"$MERGED"
276                         check_unchanged
277                 else
278                         echo "TortoiseMerge cannot be used without a base" 1>&2
279                         status=1
280                 fi
281                 ;;
282         araxis)
283                 if merge_mode; then
284                         touch "$BACKUP"
285                         if $base_present; then
286                                 "$merge_tool_path" -wait -merge -3 -a1 \
287                                         "$BASE" "$LOCAL" "$REMOTE" "$MERGED" \
288                                         >/dev/null 2>&1
289                         else
290                                 "$merge_tool_path" -wait -2 \
291                                         "$LOCAL" "$REMOTE" "$MERGED" \
292                                         >/dev/null 2>&1
293                         fi
294                         check_unchanged
295                 else
296                         "$merge_tool_path" -wait -2 "$LOCAL" "$REMOTE" \
297                                 >/dev/null 2>&1
298                 fi
299                 ;;
300         *)
301                 merge_tool_cmd="$(get_merge_tool_cmd "$1")"
302                 if test -z "$merge_tool_cmd"; then
303                         if merge_mode; then
304                                 status=1
305                         fi
306                         break
307                 fi
308                 if merge_mode; then
309                         trust_exit_code="$(git config --bool \
310                                 mergetool."$1".trustExitCode || echo false)"
311                         if test "$trust_exit_code" = "false"; then
312                                 touch "$BACKUP"
313                                 ( eval $merge_tool_cmd )
314                                 check_unchanged
315                         else
316                                 ( eval $merge_tool_cmd )
317                                 status=$?
318                         fi
319                 else
320                         ( eval $merge_tool_cmd )
321                 fi
322                 ;;
323         esac
324         return $status
325 }
326
327 guess_merge_tool () {
328         if merge_mode; then
329                 tools="tortoisemerge"
330         else
331                 tools="kompare"
332         fi
333         if test -n "$DISPLAY"; then
334                 if test -n "$GNOME_DESKTOP_SESSION_ID" ; then
335                         tools="meld opendiff kdiff3 tkdiff xxdiff $tools"
336                 else
337                         tools="opendiff kdiff3 tkdiff xxdiff meld $tools"
338                 fi
339                 tools="$tools gvimdiff diffuse ecmerge p4merge araxis"
340         fi
341         case "${VISUAL:-$EDITOR}" in
342         *vim*)
343                 tools="$tools vimdiff emerge"
344                 ;;
345         *)
346                 tools="$tools emerge vimdiff"
347                 ;;
348         esac
349         echo >&2 "merge tool candidates: $tools"
350
351         # Loop over each candidate and stop when a valid merge tool is found.
352         for i in $tools
353         do
354                 merge_tool_path="$(translate_merge_tool_path "$i")"
355                 if type "$merge_tool_path" > /dev/null 2>&1; then
356                         echo "$i"
357                         return 0
358                 fi
359         done
360
361         echo >&2 "No known merge resolution program available."
362         return 1
363 }
364
365 get_configured_merge_tool () {
366         # Diff mode first tries diff.tool and falls back to merge.tool.
367         # Merge mode only checks merge.tool
368         if diff_mode; then
369                 merge_tool=$(git config diff.tool || git config merge.tool)
370         else
371                 merge_tool=$(git config merge.tool)
372         fi
373         if test -n "$merge_tool" && ! valid_tool "$merge_tool"; then
374                 echo >&2 "git config option $TOOL_MODE.tool set to unknown tool: $merge_tool"
375                 echo >&2 "Resetting to default..."
376                 return 1
377         fi
378         echo "$merge_tool"
379 }
380
381 get_merge_tool_path () {
382         # A merge tool has been set, so verify that it's valid.
383         if test -n "$1"; then
384                 merge_tool="$1"
385         else
386                 merge_tool="$(get_merge_tool)"
387         fi
388         if ! valid_tool "$merge_tool"; then
389                 echo >&2 "Unknown merge tool $merge_tool"
390                 exit 1
391         fi
392         if diff_mode; then
393                 merge_tool_path=$(git config difftool."$merge_tool".path ||
394                                   git config mergetool."$merge_tool".path)
395         else
396                 merge_tool_path=$(git config mergetool."$merge_tool".path)
397         fi
398         if test -z "$merge_tool_path"; then
399                 merge_tool_path="$(translate_merge_tool_path "$merge_tool")"
400         fi
401         if test -z "$(get_merge_tool_cmd "$merge_tool")" &&
402         ! type "$merge_tool_path" > /dev/null 2>&1; then
403                 echo >&2 "The $TOOL_MODE tool $merge_tool is not available as"\
404                          "'$merge_tool_path'"
405                 exit 1
406         fi
407         echo "$merge_tool_path"
408 }
409
410 get_merge_tool () {
411         # Check if a merge tool has been configured
412         merge_tool=$(get_configured_merge_tool)
413         # Try to guess an appropriate merge tool if no tool has been set.
414         if test -z "$merge_tool"; then
415                 merge_tool="$(guess_merge_tool)" || exit
416         fi
417         echo "$merge_tool"
418 }