combined diff: correctly handle truncated file
[git/git-svn.git] / git-mergetool--lib.sh
blob51dd0d67ba951873df4f26b1707a1e8a713082b9
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 < /dev/tty
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)
173 if merge_mode; then
174 touch "$BACKUP"
175 "$merge_tool_path" -d -c "wincmd l" \
176 "$LOCAL" "$MERGED" "$REMOTE"
177 check_unchanged
178 else
179 "$merge_tool_path" -d -c "wincmd l" \
180 "$LOCAL" "$REMOTE"
183 gvimdiff)
184 if merge_mode; then
185 touch "$BACKUP"
186 "$merge_tool_path" -d -c "wincmd l" -f \
187 "$LOCAL" "$MERGED" "$REMOTE"
188 check_unchanged
189 else
190 "$merge_tool_path" -d -c "wincmd l" -f \
191 "$LOCAL" "$REMOTE"
194 xxdiff)
195 if merge_mode; then
196 touch "$BACKUP"
197 if $base_present; then
198 "$merge_tool_path" -X --show-merged-pane \
199 -R 'Accel.SaveAsMerged: "Ctrl-S"' \
200 -R 'Accel.Search: "Ctrl+F"' \
201 -R 'Accel.SearchForward: "Ctrl-G"' \
202 --merged-file "$MERGED" \
203 "$LOCAL" "$BASE" "$REMOTE"
204 else
205 "$merge_tool_path" -X $extra \
206 -R 'Accel.SaveAsMerged: "Ctrl-S"' \
207 -R 'Accel.Search: "Ctrl+F"' \
208 -R 'Accel.SearchForward: "Ctrl-G"' \
209 --merged-file "$MERGED" \
210 "$LOCAL" "$REMOTE"
212 check_unchanged
213 else
214 "$merge_tool_path" \
215 -R 'Accel.Search: "Ctrl+F"' \
216 -R 'Accel.SearchForward: "Ctrl-G"' \
217 "$LOCAL" "$REMOTE"
220 opendiff)
221 if merge_mode; then
222 touch "$BACKUP"
223 if $base_present; then
224 "$merge_tool_path" "$LOCAL" "$REMOTE" \
225 -ancestor "$BASE" \
226 -merge "$MERGED" | cat
227 else
228 "$merge_tool_path" "$LOCAL" "$REMOTE" \
229 -merge "$MERGED" | cat
231 check_unchanged
232 else
233 "$merge_tool_path" "$LOCAL" "$REMOTE" | cat
236 ecmerge)
237 if merge_mode; then
238 touch "$BACKUP"
239 if $base_present; then
240 "$merge_tool_path" "$BASE" "$LOCAL" "$REMOTE" \
241 --default --mode=merge3 --to="$MERGED"
242 else
243 "$merge_tool_path" "$LOCAL" "$REMOTE" \
244 --default --mode=merge2 --to="$MERGED"
246 check_unchanged
247 else
248 "$merge_tool_path" --default --mode=diff2 \
249 "$LOCAL" "$REMOTE"
252 emerge)
253 if merge_mode; then
254 if $base_present; then
255 "$merge_tool_path" \
256 -f emerge-files-with-ancestor-command \
257 "$LOCAL" "$REMOTE" "$BASE" \
258 "$(basename "$MERGED")"
259 else
260 "$merge_tool_path" \
261 -f emerge-files-command \
262 "$LOCAL" "$REMOTE" \
263 "$(basename "$MERGED")"
265 status=$?
266 else
267 "$merge_tool_path" -f emerge-files-command \
268 "$LOCAL" "$REMOTE"
271 tortoisemerge)
272 if $base_present; then
273 touch "$BACKUP"
274 "$merge_tool_path" \
275 -base:"$BASE" -mine:"$LOCAL" \
276 -theirs:"$REMOTE" -merged:"$MERGED"
277 check_unchanged
278 else
279 echo "TortoiseMerge cannot be used without a base" 1>&2
280 status=1
283 araxis)
284 if merge_mode; then
285 touch "$BACKUP"
286 if $base_present; then
287 "$merge_tool_path" -wait -merge -3 -a1 \
288 "$BASE" "$LOCAL" "$REMOTE" "$MERGED" \
289 >/dev/null 2>&1
290 else
291 "$merge_tool_path" -wait -2 \
292 "$LOCAL" "$REMOTE" "$MERGED" \
293 >/dev/null 2>&1
295 check_unchanged
296 else
297 "$merge_tool_path" -wait -2 "$LOCAL" "$REMOTE" \
298 >/dev/null 2>&1
302 merge_tool_cmd="$(get_merge_tool_cmd "$1")"
303 if test -z "$merge_tool_cmd"; then
304 if merge_mode; then
305 status=1
307 break
309 if merge_mode; then
310 trust_exit_code="$(git config --bool \
311 mergetool."$1".trustExitCode || echo false)"
312 if test "$trust_exit_code" = "false"; then
313 touch "$BACKUP"
314 ( eval $merge_tool_cmd )
315 check_unchanged
316 else
317 ( eval $merge_tool_cmd )
318 status=$?
320 else
321 ( eval $merge_tool_cmd )
324 esac
325 return $status
328 guess_merge_tool () {
329 if merge_mode; then
330 tools="tortoisemerge"
331 else
332 tools="kompare"
334 if test -n "$DISPLAY"; then
335 if test -n "$GNOME_DESKTOP_SESSION_ID" ; then
336 tools="meld opendiff kdiff3 tkdiff xxdiff $tools"
337 else
338 tools="opendiff kdiff3 tkdiff xxdiff meld $tools"
340 tools="$tools gvimdiff diffuse ecmerge p4merge araxis"
342 case "${VISUAL:-$EDITOR}" in
343 *vim*)
344 tools="$tools vimdiff emerge"
347 tools="$tools emerge vimdiff"
349 esac
350 echo >&2 "merge tool candidates: $tools"
352 # Loop over each candidate and stop when a valid merge tool is found.
353 for i in $tools
355 merge_tool_path="$(translate_merge_tool_path "$i")"
356 if type "$merge_tool_path" > /dev/null 2>&1; then
357 echo "$i"
358 return 0
360 done
362 echo >&2 "No known merge resolution program available."
363 return 1
366 get_configured_merge_tool () {
367 # Diff mode first tries diff.tool and falls back to merge.tool.
368 # Merge mode only checks merge.tool
369 if diff_mode; then
370 merge_tool=$(git config diff.tool || git config merge.tool)
371 else
372 merge_tool=$(git config merge.tool)
374 if test -n "$merge_tool" && ! valid_tool "$merge_tool"; then
375 echo >&2 "git config option $TOOL_MODE.tool set to unknown tool: $merge_tool"
376 echo >&2 "Resetting to default..."
377 return 1
379 echo "$merge_tool"
382 get_merge_tool_path () {
383 # A merge tool has been set, so verify that it's valid.
384 if test -n "$1"; then
385 merge_tool="$1"
386 else
387 merge_tool="$(get_merge_tool)"
389 if ! valid_tool "$merge_tool"; then
390 echo >&2 "Unknown merge tool $merge_tool"
391 exit 1
393 if diff_mode; then
394 merge_tool_path=$(git config difftool."$merge_tool".path ||
395 git config mergetool."$merge_tool".path)
396 else
397 merge_tool_path=$(git config mergetool."$merge_tool".path)
399 if test -z "$merge_tool_path"; then
400 merge_tool_path="$(translate_merge_tool_path "$merge_tool")"
402 if test -z "$(get_merge_tool_cmd "$merge_tool")" &&
403 ! type "$merge_tool_path" > /dev/null 2>&1; then
404 echo >&2 "The $TOOL_MODE tool $merge_tool is not available as"\
405 "'$merge_tool_path'"
406 exit 1
408 echo "$merge_tool_path"
411 get_merge_tool () {
412 # Check if a merge tool has been configured
413 merge_tool=$(get_configured_merge_tool)
414 # Try to guess an appropriate merge tool if no tool has been set.
415 if test -z "$merge_tool"; then
416 merge_tool="$(guess_merge_tool)" || exit
418 echo "$merge_tool"