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