Merge branch 'dm/mergetool-vimdiff' into next
[git/mjg.git] / git-mergetool--lib.sh
blobb84ac582f957a4e77af32f6f6285fd5106219804
1 #!/bin/sh
2 # git-mergetool--lib is a library for common merge tool functions
3 diff_mode() {
4 test "$TOOL_MODE" = diff
7 merge_mode() {
8 test "$TOOL_MODE" = merge
11 translate_merge_tool_path () {
12 case "$1" in
13 vimdiff)
14 echo vim
16 gvimdiff)
17 echo gvim
19 emerge)
20 echo emacs
22 araxis)
23 echo compare
26 echo "$1"
28 esac
31 check_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
47 valid_tool () {
48 case "$1" in
49 kdiff3 | tkdiff | xxdiff | meld | opendiff | \
50 emerge | vimdiff | gvimdiff | ecmerge | diffuse | araxis | p4merge)
51 ;; # happy
52 tortoisemerge)
53 if ! merge_mode; then
54 return 1
57 kompare)
58 if ! diff_mode; then
59 return 1
63 if test -z "$(get_merge_tool_cmd "$1")"; then
64 return 1
67 esac
70 get_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)"
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)"
85 run_merge_tool () {
86 merge_tool_path="$(get_merge_tool_path "$1")" || exit
87 base_present="$2"
88 status=0
90 case "$1" in
91 kdiff3)
92 if merge_mode; then
93 if $base_present; then
94 ("$merge_tool_path" --auto \
95 --L1 "$MERGED (Base)" \
96 --L2 "$MERGED (Local)" \
97 --L3 "$MERGED (Remote)" \
98 -o "$MERGED" \
99 "$BASE" "$LOCAL" "$REMOTE" \
100 > /dev/null 2>&1)
101 else
102 ("$merge_tool_path" --auto \
103 --L1 "$MERGED (Local)" \
104 --L2 "$MERGED (Remote)" \
105 -o "$MERGED" \
106 "$LOCAL" "$REMOTE" \
107 > /dev/null 2>&1)
109 status=$?
110 else
111 ("$merge_tool_path" --auto \
112 --L1 "$MERGED (A)" \
113 --L2 "$MERGED (B)" "$LOCAL" "$REMOTE" \
114 > /dev/null 2>&1)
117 kompare)
118 "$merge_tool_path" "$LOCAL" "$REMOTE"
120 tkdiff)
121 if merge_mode; then
122 if $base_present; then
123 "$merge_tool_path" -a "$BASE" \
124 -o "$MERGED" "$LOCAL" "$REMOTE"
125 else
126 "$merge_tool_path" \
127 -o "$MERGED" "$LOCAL" "$REMOTE"
129 status=$?
130 else
131 "$merge_tool_path" "$LOCAL" "$REMOTE"
134 p4merge)
135 if merge_mode; then
136 touch "$BACKUP"
137 if $base_present; then
138 "$merge_tool_path" "$BASE" "$LOCAL" "$REMOTE" "$MERGED"
139 else
140 "$merge_tool_path" "$LOCAL" "$LOCAL" "$REMOTE" "$MERGED"
142 check_unchanged
143 else
144 "$merge_tool_path" "$LOCAL" "$REMOTE"
147 meld)
148 if merge_mode; then
149 touch "$BACKUP"
150 "$merge_tool_path" "$LOCAL" "$MERGED" "$REMOTE"
151 check_unchanged
152 else
153 "$merge_tool_path" "$LOCAL" "$REMOTE"
156 diffuse)
157 if merge_mode; then
158 touch "$BACKUP"
159 if $base_present; then
160 "$merge_tool_path" \
161 "$LOCAL" "$MERGED" "$REMOTE" \
162 "$BASE" | cat
163 else
164 "$merge_tool_path" \
165 "$LOCAL" "$MERGED" "$REMOTE" | cat
167 check_unchanged
168 else
169 "$merge_tool_path" "$LOCAL" "$REMOTE" | cat
172 vimdiff|gvimdiff)
173 if merge_mode; then
174 touch "$BACKUP"
175 if $base_present; then
176 "$merge_tool_path" -f -d -c "wincmd J" \
177 "$MERGED" "$LOCAL" "$BASE" "$REMOTE"
178 else
179 "$merge_tool_path" -f -d -c "wincmd l" \
180 "$LOCAL" "$MERGED" "$REMOTE"
182 check_unchanged
183 else
184 "$merge_tool_path" -f -d -c "wincmd l" \
185 "$LOCAL" "$REMOTE"
188 xxdiff)
189 if merge_mode; then
190 touch "$BACKUP"
191 if $base_present; then
192 "$merge_tool_path" -X --show-merged-pane \
193 -R 'Accel.SaveAsMerged: "Ctrl-S"' \
194 -R 'Accel.Search: "Ctrl+F"' \
195 -R 'Accel.SearchForward: "Ctrl-G"' \
196 --merged-file "$MERGED" \
197 "$LOCAL" "$BASE" "$REMOTE"
198 else
199 "$merge_tool_path" -X $extra \
200 -R 'Accel.SaveAsMerged: "Ctrl-S"' \
201 -R 'Accel.Search: "Ctrl+F"' \
202 -R 'Accel.SearchForward: "Ctrl-G"' \
203 --merged-file "$MERGED" \
204 "$LOCAL" "$REMOTE"
206 check_unchanged
207 else
208 "$merge_tool_path" \
209 -R 'Accel.Search: "Ctrl+F"' \
210 -R 'Accel.SearchForward: "Ctrl-G"' \
211 "$LOCAL" "$REMOTE"
214 opendiff)
215 if merge_mode; then
216 touch "$BACKUP"
217 if $base_present; then
218 "$merge_tool_path" "$LOCAL" "$REMOTE" \
219 -ancestor "$BASE" \
220 -merge "$MERGED" | cat
221 else
222 "$merge_tool_path" "$LOCAL" "$REMOTE" \
223 -merge "$MERGED" | cat
225 check_unchanged
226 else
227 "$merge_tool_path" "$LOCAL" "$REMOTE" | cat
230 ecmerge)
231 if merge_mode; then
232 touch "$BACKUP"
233 if $base_present; then
234 "$merge_tool_path" "$BASE" "$LOCAL" "$REMOTE" \
235 --default --mode=merge3 --to="$MERGED"
236 else
237 "$merge_tool_path" "$LOCAL" "$REMOTE" \
238 --default --mode=merge2 --to="$MERGED"
240 check_unchanged
241 else
242 "$merge_tool_path" --default --mode=diff2 \
243 "$LOCAL" "$REMOTE"
246 emerge)
247 if merge_mode; then
248 if $base_present; then
249 "$merge_tool_path" \
250 -f emerge-files-with-ancestor-command \
251 "$LOCAL" "$REMOTE" "$BASE" \
252 "$(basename "$MERGED")"
253 else
254 "$merge_tool_path" \
255 -f emerge-files-command \
256 "$LOCAL" "$REMOTE" \
257 "$(basename "$MERGED")"
259 status=$?
260 else
261 "$merge_tool_path" -f emerge-files-command \
262 "$LOCAL" "$REMOTE"
265 tortoisemerge)
266 if $base_present; then
267 touch "$BACKUP"
268 "$merge_tool_path" \
269 -base:"$BASE" -mine:"$LOCAL" \
270 -theirs:"$REMOTE" -merged:"$MERGED"
271 check_unchanged
272 else
273 echo "TortoiseMerge cannot be used without a base" 1>&2
274 status=1
277 araxis)
278 if merge_mode; then
279 touch "$BACKUP"
280 if $base_present; then
281 "$merge_tool_path" -wait -merge -3 -a1 \
282 "$BASE" "$LOCAL" "$REMOTE" "$MERGED" \
283 >/dev/null 2>&1
284 else
285 "$merge_tool_path" -wait -2 \
286 "$LOCAL" "$REMOTE" "$MERGED" \
287 >/dev/null 2>&1
289 check_unchanged
290 else
291 "$merge_tool_path" -wait -2 "$LOCAL" "$REMOTE" \
292 >/dev/null 2>&1
296 merge_tool_cmd="$(get_merge_tool_cmd "$1")"
297 if test -z "$merge_tool_cmd"; then
298 if merge_mode; then
299 status=1
301 break
303 if merge_mode; then
304 trust_exit_code="$(git config --bool \
305 mergetool."$1".trustExitCode || echo false)"
306 if test "$trust_exit_code" = "false"; then
307 touch "$BACKUP"
308 ( eval $merge_tool_cmd )
309 check_unchanged
310 else
311 ( eval $merge_tool_cmd )
312 status=$?
314 else
315 ( eval $merge_tool_cmd )
318 esac
319 return $status
322 guess_merge_tool () {
323 if merge_mode; then
324 tools="tortoisemerge"
325 else
326 tools="kompare"
328 if test -n "$DISPLAY"; then
329 if test -n "$GNOME_DESKTOP_SESSION_ID" ; then
330 tools="meld opendiff kdiff3 tkdiff xxdiff $tools"
331 else
332 tools="opendiff kdiff3 tkdiff xxdiff meld $tools"
334 tools="$tools gvimdiff diffuse ecmerge p4merge araxis"
336 case "${VISUAL:-$EDITOR}" in
337 *vim*)
338 tools="$tools vimdiff emerge"
341 tools="$tools emerge vimdiff"
343 esac
344 echo >&2 "merge tool candidates: $tools"
346 # Loop over each candidate and stop when a valid merge tool is found.
347 for i in $tools
349 merge_tool_path="$(translate_merge_tool_path "$i")"
350 if type "$merge_tool_path" > /dev/null 2>&1; then
351 echo "$i"
352 return 0
354 done
356 echo >&2 "No known merge resolution program available."
357 return 1
360 get_configured_merge_tool () {
361 # Diff mode first tries diff.tool and falls back to merge.tool.
362 # Merge mode only checks merge.tool
363 if diff_mode; then
364 merge_tool=$(git config diff.tool || git config merge.tool)
365 else
366 merge_tool=$(git config merge.tool)
368 if test -n "$merge_tool" && ! valid_tool "$merge_tool"; then
369 echo >&2 "git config option $TOOL_MODE.tool set to unknown tool: $merge_tool"
370 echo >&2 "Resetting to default..."
371 return 1
373 echo "$merge_tool"
376 get_merge_tool_path () {
377 # A merge tool has been set, so verify that it's valid.
378 if test -n "$1"; then
379 merge_tool="$1"
380 else
381 merge_tool="$(get_merge_tool)"
383 if ! valid_tool "$merge_tool"; then
384 echo >&2 "Unknown merge tool $merge_tool"
385 exit 1
387 if diff_mode; then
388 merge_tool_path=$(git config difftool."$merge_tool".path ||
389 git config mergetool."$merge_tool".path)
390 else
391 merge_tool_path=$(git config mergetool."$merge_tool".path)
393 if test -z "$merge_tool_path"; then
394 merge_tool_path="$(translate_merge_tool_path "$merge_tool")"
396 if test -z "$(get_merge_tool_cmd "$merge_tool")" &&
397 ! type "$merge_tool_path" > /dev/null 2>&1; then
398 echo >&2 "The $TOOL_MODE tool $merge_tool is not available as"\
399 "'$merge_tool_path'"
400 exit 1
402 echo "$merge_tool_path"
405 get_merge_tool () {
406 # Check if a merge tool has been configured
407 merge_tool=$(get_configured_merge_tool)
408 # Try to guess an appropriate merge tool if no tool has been set.
409 if test -z "$merge_tool"; then
410 merge_tool="$(guess_merge_tool)" || exit
412 echo "$merge_tool"