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