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