Merge branch 'jl/maint-pull-tags-doc'
[alt-git.git] / git-mergetool--lib.sh
blob77d4aee20ee5b8b01f9a0e3d1f8dbc9f7fdf3eb9
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|vimdiff2)
14 echo vim
16 gvimdiff|gvimdiff2)
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 vimdiff | gvimdiff | vimdiff2 | gvimdiff2 | \
51 emerge | ecmerge | diffuse | araxis | p4merge)
52 ;; # happy
53 tortoisemerge)
54 if ! merge_mode; then
55 return 1
58 kompare)
59 if ! diff_mode; then
60 return 1
64 if test -z "$(get_merge_tool_cmd "$1")"; then
65 return 1
68 esac
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)"
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)"
86 run_merge_tool () {
87 merge_tool_path="$(get_merge_tool_path "$1")" || exit
88 base_present="$2"
89 status=0
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)
110 status=$?
111 else
112 ("$merge_tool_path" --auto \
113 --L1 "$MERGED (A)" \
114 --L2 "$MERGED (B)" "$LOCAL" "$REMOTE" \
115 > /dev/null 2>&1)
118 kompare)
119 "$merge_tool_path" "$LOCAL" "$REMOTE"
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"
130 status=$?
131 else
132 "$merge_tool_path" "$LOCAL" "$REMOTE"
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"
143 check_unchanged
144 else
145 "$merge_tool_path" "$LOCAL" "$REMOTE"
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"
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
168 check_unchanged
169 else
170 "$merge_tool_path" "$LOCAL" "$REMOTE" | cat
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"
183 check_unchanged
184 else
185 "$merge_tool_path" -f -d -c "wincmd l" \
186 "$LOCAL" "$REMOTE"
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"
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"
218 check_unchanged
219 else
220 "$merge_tool_path" \
221 -R 'Accel.Search: "Ctrl+F"' \
222 -R 'Accel.SearchForward: "Ctrl-G"' \
223 "$LOCAL" "$REMOTE"
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
237 check_unchanged
238 else
239 "$merge_tool_path" "$LOCAL" "$REMOTE" | cat
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"
252 check_unchanged
253 else
254 "$merge_tool_path" --default --mode=diff2 \
255 "$LOCAL" "$REMOTE"
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")"
271 status=$?
272 else
273 "$merge_tool_path" -f emerge-files-command \
274 "$LOCAL" "$REMOTE"
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
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
301 check_unchanged
302 else
303 "$merge_tool_path" -wait -2 "$LOCAL" "$REMOTE" \
304 >/dev/null 2>&1
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
313 break
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=$?
326 else
327 ( eval $merge_tool_cmd )
330 esac
331 return $status
334 guess_merge_tool () {
335 if merge_mode; then
336 tools="tortoisemerge"
337 else
338 tools="kompare"
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"
346 tools="$tools gvimdiff diffuse ecmerge p4merge araxis"
348 case "${VISUAL:-$EDITOR}" in
349 *vim*)
350 tools="$tools vimdiff emerge"
353 tools="$tools emerge vimdiff"
355 esac
356 echo >&2 "merge tool candidates: $tools"
358 # Loop over each candidate and stop when a valid merge tool is found.
359 for i in $tools
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
366 done
368 echo >&2 "No known merge resolution program available."
369 return 1
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)
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
385 echo "$merge_tool"
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)"
395 if ! valid_tool "$merge_tool"; then
396 echo >&2 "Unknown merge tool $merge_tool"
397 exit 1
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)
405 if test -z "$merge_tool_path"; then
406 merge_tool_path="$(translate_merge_tool_path "$merge_tool")"
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
414 echo "$merge_tool_path"
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
424 echo "$merge_tool"