Code

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