Add an option not to use link(src, dest) && unlink(src) when that is unreliable
[git/git-svn.git] / git-mergetool--lib.sh
bloba16a2795d70779365212c09b6d0dd42d1746842b
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
22 echo "$1"
24 esac
27 check_unchanged () {
28 if test "$MERGED" -nt "$BACKUP"; then
29 status=0
30 else
31 while true; do
32 echo "$MERGED seems unchanged."
33 printf "Was the merge successful? [y/n] "
34 read answer < /dev/tty
35 case "$answer" in
36 y*|Y*) status=0; break ;;
37 n*|N*) status=1; break ;;
38 esac
39 done
43 valid_tool () {
44 case "$1" in
45 kdiff3 | tkdiff | xxdiff | meld | opendiff | \
46 emerge | vimdiff | gvimdiff | ecmerge | diffuse)
47 ;; # happy
48 tortoisemerge)
49 if ! merge_mode; then
50 return 1
53 kompare)
54 if ! diff_mode; then
55 return 1
59 if test -z "$(get_merge_tool_cmd "$1")"; then
60 return 1
63 esac
66 get_merge_tool_cmd () {
67 # Prints the custom command for a merge tool
68 if test -n "$1"; then
69 merge_tool="$1"
70 else
71 merge_tool="$(get_merge_tool)"
73 if diff_mode; then
74 echo "$(git config difftool.$merge_tool.cmd ||
75 git config mergetool.$merge_tool.cmd)"
76 else
77 echo "$(git config mergetool.$merge_tool.cmd)"
81 run_merge_tool () {
82 merge_tool_path="$(get_merge_tool_path "$1")" || exit
83 base_present="$2"
84 status=0
86 case "$1" in
87 kdiff3)
88 if merge_mode; then
89 if $base_present; then
90 ("$merge_tool_path" --auto \
91 --L1 "$MERGED (Base)" \
92 --L2 "$MERGED (Local)" \
93 --L3 "$MERGED (Remote)" \
94 -o "$MERGED" \
95 "$BASE" "$LOCAL" "$REMOTE" \
96 > /dev/null 2>&1)
97 else
98 ("$merge_tool_path" --auto \
99 --L1 "$MERGED (Local)" \
100 --L2 "$MERGED (Remote)" \
101 -o "$MERGED" \
102 "$LOCAL" "$REMOTE" \
103 > /dev/null 2>&1)
105 status=$?
106 else
107 ("$merge_tool_path" --auto \
108 --L1 "$MERGED (A)" \
109 --L2 "$MERGED (B)" "$LOCAL" "$REMOTE" \
110 > /dev/null 2>&1)
113 kompare)
114 "$merge_tool_path" "$LOCAL" "$REMOTE"
116 tkdiff)
117 if merge_mode; then
118 if $base_present; then
119 "$merge_tool_path" -a "$BASE" \
120 -o "$MERGED" "$LOCAL" "$REMOTE"
121 else
122 "$merge_tool_path" \
123 -o "$MERGED" "$LOCAL" "$REMOTE"
125 status=$?
126 else
127 "$merge_tool_path" "$LOCAL" "$REMOTE"
130 meld)
131 if merge_mode; then
132 touch "$BACKUP"
133 "$merge_tool_path" "$LOCAL" "$MERGED" "$REMOTE"
134 check_unchanged
135 else
136 "$merge_tool_path" "$LOCAL" "$REMOTE"
139 diffuse)
140 if merge_mode; then
141 touch "$BACKUP"
142 if $base_present; then
143 "$merge_tool_path" \
144 "$LOCAL" "$MERGED" "$REMOTE" \
145 "$BASE" | cat
146 else
147 "$merge_tool_path" \
148 "$LOCAL" "$MERGED" "$REMOTE" | cat
150 check_unchanged
151 else
152 "$merge_tool_path" "$LOCAL" "$REMOTE" | cat
155 vimdiff)
156 if merge_mode; then
157 touch "$BACKUP"
158 "$merge_tool_path" -d -c "wincmd l" \
159 "$LOCAL" "$MERGED" "$REMOTE"
160 check_unchanged
161 else
162 "$merge_tool_path" -d -c "wincmd l" \
163 "$LOCAL" "$REMOTE"
166 gvimdiff)
167 if merge_mode; then
168 touch "$BACKUP"
169 "$merge_tool_path" -d -c "wincmd l" -f \
170 "$LOCAL" "$MERGED" "$REMOTE"
171 check_unchanged
172 else
173 "$merge_tool_path" -d -c "wincmd l" -f \
174 "$LOCAL" "$REMOTE"
177 xxdiff)
178 if merge_mode; then
179 touch "$BACKUP"
180 if $base_present; then
181 "$merge_tool_path" -X --show-merged-pane \
182 -R 'Accel.SaveAsMerged: "Ctrl-S"' \
183 -R 'Accel.Search: "Ctrl+F"' \
184 -R 'Accel.SearchForward: "Ctrl-G"' \
185 --merged-file "$MERGED" \
186 "$LOCAL" "$BASE" "$REMOTE"
187 else
188 "$merge_tool_path" -X $extra \
189 -R 'Accel.SaveAsMerged: "Ctrl-S"' \
190 -R 'Accel.Search: "Ctrl+F"' \
191 -R 'Accel.SearchForward: "Ctrl-G"' \
192 --merged-file "$MERGED" \
193 "$LOCAL" "$REMOTE"
195 check_unchanged
196 else
197 "$merge_tool_path" \
198 -R 'Accel.Search: "Ctrl+F"' \
199 -R 'Accel.SearchForward: "Ctrl-G"' \
200 "$LOCAL" "$REMOTE"
203 opendiff)
204 if merge_mode; then
205 touch "$BACKUP"
206 if $base_present; then
207 "$merge_tool_path" "$LOCAL" "$REMOTE" \
208 -ancestor "$BASE" \
209 -merge "$MERGED" | cat
210 else
211 "$merge_tool_path" "$LOCAL" "$REMOTE" \
212 -merge "$MERGED" | cat
214 check_unchanged
215 else
216 "$merge_tool_path" "$LOCAL" "$REMOTE" | cat
219 ecmerge)
220 if merge_mode; then
221 touch "$BACKUP"
222 if $base_present; then
223 "$merge_tool_path" "$BASE" "$LOCAL" "$REMOTE" \
224 --default --mode=merge3 --to="$MERGED"
225 else
226 "$merge_tool_path" "$LOCAL" "$REMOTE" \
227 --default --mode=merge2 --to="$MERGED"
229 check_unchanged
230 else
231 "$merge_tool_path" "$LOCAL" "$REMOTE" \
232 --default --mode=merge2 --to="$MERGED"
235 emerge)
236 if merge_mode; then
237 if $base_present; then
238 "$merge_tool_path" \
239 -f emerge-files-with-ancestor-command \
240 "$LOCAL" "$REMOTE" "$BASE" \
241 "$(basename "$MERGED")"
242 else
243 "$merge_tool_path" \
244 -f emerge-files-command \
245 "$LOCAL" "$REMOTE" \
246 "$(basename "$MERGED")"
248 status=$?
249 else
250 "$merge_tool_path" -f emerge-files-command \
251 "$LOCAL" "$REMOTE" "$(basename "$MERGED")"
254 tortoisemerge)
255 if $base_present; then
256 touch "$BACKUP"
257 "$merge_tool_path" \
258 -base:"$BASE" -mine:"$LOCAL" \
259 -theirs:"$REMOTE" -merged:"$MERGED"
260 check_unchanged
261 else
262 echo "TortoiseMerge cannot be used without a base" 1>&2
263 status=1
267 merge_tool_cmd="$(get_merge_tool_cmd "$1")"
268 if test -z "$merge_tool_cmd"; then
269 if merge_mode; then
270 status=1
272 break
274 if merge_mode; then
275 trust_exit_code="$(git config --bool \
276 mergetool."$1".trustExitCode || echo false)"
277 if test "$trust_exit_code" = "false"; then
278 touch "$BACKUP"
279 ( eval $merge_tool_cmd )
280 check_unchanged
281 else
282 ( eval $merge_tool_cmd )
283 status=$?
285 else
286 ( eval $merge_tool_cmd )
289 esac
290 return $status
293 guess_merge_tool () {
294 if merge_mode; then
295 tools="tortoisemerge"
296 else
297 tools="kompare"
299 if test -n "$DISPLAY"; then
300 if test -n "$GNOME_DESKTOP_SESSION_ID" ; then
301 tools="meld opendiff kdiff3 tkdiff xxdiff $tools"
302 else
303 tools="opendiff kdiff3 tkdiff xxdiff meld $tools"
305 tools="$tools gvimdiff diffuse ecmerge"
307 if echo "${VISUAL:-$EDITOR}" | grep emacs > /dev/null 2>&1; then
308 # $EDITOR is emacs so add emerge as a candidate
309 tools="$tools emerge vimdiff"
310 elif echo "${VISUAL:-$EDITOR}" | grep vim > /dev/null 2>&1; then
311 # $EDITOR is vim so add vimdiff as a candidate
312 tools="$tools vimdiff emerge"
313 else
314 tools="$tools emerge vimdiff"
316 echo >&2 "merge tool candidates: $tools"
318 # Loop over each candidate and stop when a valid merge tool is found.
319 for i in $tools
321 merge_tool_path="$(translate_merge_tool_path "$i")"
322 if type "$merge_tool_path" > /dev/null 2>&1; then
323 echo "$i"
324 return 0
326 done
328 echo >&2 "No known merge resolution program available."
329 return 1
332 get_configured_merge_tool () {
333 # Diff mode first tries diff.tool and falls back to merge.tool.
334 # Merge mode only checks merge.tool
335 if diff_mode; then
336 merge_tool=$(git config diff.tool || git config merge.tool)
337 else
338 merge_tool=$(git config merge.tool)
340 if test -n "$merge_tool" && ! valid_tool "$merge_tool"; then
341 echo >&2 "git config option $TOOL_MODE.tool set to unknown tool: $merge_tool"
342 echo >&2 "Resetting to default..."
343 return 1
345 echo "$merge_tool"
348 get_merge_tool_path () {
349 # A merge tool has been set, so verify that it's valid.
350 if test -n "$1"; then
351 merge_tool="$1"
352 else
353 merge_tool="$(get_merge_tool)"
355 if ! valid_tool "$merge_tool"; then
356 echo >&2 "Unknown merge tool $merge_tool"
357 exit 1
359 if diff_mode; then
360 merge_tool_path=$(git config difftool."$merge_tool".path ||
361 git config mergetool."$merge_tool".path)
362 else
363 merge_tool_path=$(git config mergetool."$merge_tool".path)
365 if test -z "$merge_tool_path"; then
366 merge_tool_path="$(translate_merge_tool_path "$merge_tool")"
368 if test -z "$(get_merge_tool_cmd "$merge_tool")" &&
369 ! type "$merge_tool_path" > /dev/null 2>&1; then
370 echo >&2 "The $TOOL_MODE tool $merge_tool is not available as"\
371 "'$merge_tool_path'"
372 exit 1
374 echo "$merge_tool_path"
377 get_merge_tool () {
378 # Check if a merge tool has been configured
379 merge_tool=$(get_configured_merge_tool)
380 # Try to guess an appropriate merge tool if no tool has been set.
381 if test -z "$merge_tool"; then
382 merge_tool="$(guess_merge_tool)" || exit
384 echo "$merge_tool"