Merge branch 'maint'
[git/spearce.git] / git-mergetool--lib.sh
blobbfb01f7842e6192e0b3fbc0e3f2ab96269578289
1 # git-mergetool--lib is a library for common merge tool functions
2 diff_mode() {
3 test "$TOOL_MODE" = diff
6 merge_mode() {
7 test "$TOOL_MODE" = merge
10 translate_merge_tool_path () {
11 case "$1" in
12 vimdiff)
13 echo vim
15 gvimdiff)
16 echo gvim
18 emerge)
19 echo emacs
21 araxis)
22 echo compare
25 echo "$1"
27 esac
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
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
56 kompare)
57 if ! diff_mode; then
58 return 1
62 if test -z "$(get_merge_tool_cmd "$1")"; then
63 return 1
66 esac
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)"
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)"
84 run_merge_tool () {
85 merge_tool_path="$(get_merge_tool_path "$1")" || exit
86 base_present="$2"
87 status=0
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)
108 status=$?
109 else
110 ("$merge_tool_path" --auto \
111 --L1 "$MERGED (A)" \
112 --L2 "$MERGED (B)" "$LOCAL" "$REMOTE" \
113 > /dev/null 2>&1)
116 kompare)
117 "$merge_tool_path" "$LOCAL" "$REMOTE"
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"
128 status=$?
129 else
130 "$merge_tool_path" "$LOCAL" "$REMOTE"
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"
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
153 check_unchanged
154 else
155 "$merge_tool_path" "$LOCAL" "$REMOTE" | cat
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"
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"
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"
198 check_unchanged
199 else
200 "$merge_tool_path" \
201 -R 'Accel.Search: "Ctrl+F"' \
202 -R 'Accel.SearchForward: "Ctrl-G"' \
203 "$LOCAL" "$REMOTE"
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
217 check_unchanged
218 else
219 "$merge_tool_path" "$LOCAL" "$REMOTE" | cat
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"
232 check_unchanged
233 else
234 "$merge_tool_path" --default --mode=diff2 \
235 "$LOCAL" "$REMOTE"
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")"
251 status=$?
252 else
253 "$merge_tool_path" -f emerge-files-command \
254 "$LOCAL" "$REMOTE"
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
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
281 check_unchanged
282 else
283 "$merge_tool_path" -wait -2 "$LOCAL" "$REMOTE" \
284 >/dev/null 2>&1
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
293 break
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=$?
306 else
307 ( eval $merge_tool_cmd )
310 esac
311 return $status
314 guess_merge_tool () {
315 if merge_mode; then
316 tools="tortoisemerge"
317 else
318 tools="kompare"
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"
326 tools="$tools gvimdiff diffuse ecmerge araxis"
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"
337 echo >&2 "merge tool candidates: $tools"
339 # Loop over each candidate and stop when a valid merge tool is found.
340 for i in $tools
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
347 done
349 echo >&2 "No known merge resolution program available."
350 return 1
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)
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
366 echo "$merge_tool"
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)"
376 if ! valid_tool "$merge_tool"; then
377 echo >&2 "Unknown merge tool $merge_tool"
378 exit 1
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)
386 if test -z "$merge_tool_path"; then
387 merge_tool_path="$(translate_merge_tool_path "$merge_tool")"
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
395 echo "$merge_tool_path"
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
405 echo "$merge_tool"