git-mergetool--lib.sh 9.46 KB
Newer Older
1
#!/bin/sh
2 3 4 5 6 7 8 9 10 11
# git-mergetool--lib is a library for common merge tool functions
diff_mode() {
	test "$TOOL_MODE" = diff
}

merge_mode() {
	test "$TOOL_MODE" = merge
}

translate_merge_tool_path () {
12
	case "$1" in
13 14
	araxis)
		echo compare
15
		;;
16 17
	bc3)
		echo bcompare
18 19 20 21
		;;
	emerge)
		echo emacs
		;;
22 23 24 25 26
	gvimdiff|gvimdiff2)
		echo gvim
		;;
	vimdiff|vimdiff2)
		echo vim
27
		;;
28 29 30 31
	*)
		echo "$1"
		;;
	esac
32 33 34
}

check_unchanged () {
35 36
	if test "$MERGED" -nt "$BACKUP"
	then
37 38
		status=0
	else
39 40
		while true
		do
41 42
			echo "$MERGED seems unchanged."
			printf "Was the merge successful? [y/n] "
43
			read answer
44 45 46 47 48 49 50 51 52 53
			case "$answer" in
			y*|Y*) status=0; break ;;
			n*|N*) status=1; break ;;
			esac
		done
	fi
}

valid_tool () {
	case "$1" in
54
	araxis | bc3 | diffuse | ecmerge | emerge | gvimdiff | gvimdiff2 | \
55
	kdiff3 | meld | opendiff | p4merge | tkdiff | vimdiff | vimdiff2 | xxdiff)
56
		;; # happy
57
	kompare)
58 59
		if ! diff_mode
		then
60 61 62
			return 1
		fi
		;;
63
	tortoisemerge)
64 65
		if ! merge_mode
		then
66 67 68 69
			return 1
		fi
		;;
	*)
70 71
		if test -z "$(get_merge_tool_cmd "$1")"
		then
72 73 74 75 76 77 78
			return 1
		fi
		;;
	esac
}

get_merge_tool_cmd () {
79
	# Prints the custom command for a merge tool
80 81
	if test -n "$1"
	then
82 83 84 85
		merge_tool="$1"
	else
		merge_tool="$(get_merge_tool)"
	fi
86 87
	if diff_mode
	then
88
		echo "$(git config difftool.$merge_tool.cmd ||
89
			git config mergetool.$merge_tool.cmd)"
90 91 92
	else
		echo "$(git config mergetool.$merge_tool.cmd)"
	fi
93 94 95
}

run_merge_tool () {
96 97 98 99 100
	# If GIT_PREFIX is empty then we cannot use it in tools
	# that expect to be able to chdir() to its value.
	GIT_PREFIX=${GIT_PREFIX:-.}
	export GIT_PREFIX

101
	merge_tool_path="$(get_merge_tool_path "$1")" || exit
102 103 104 105
	base_present="$2"
	status=0

	case "$1" in
106
	araxis)
107 108
		if merge_mode
		then
109
			touch "$BACKUP"
110 111
			if $base_present
			then
112 113 114
				"$merge_tool_path" -wait -merge -3 -a1 \
					"$BASE" "$LOCAL" "$REMOTE" "$MERGED" \
					>/dev/null 2>&1
115
			else
116 117 118
				"$merge_tool_path" -wait -2 \
					"$LOCAL" "$REMOTE" "$MERGED" \
					>/dev/null 2>&1
119
			fi
120
			check_unchanged
121
		else
122 123
			"$merge_tool_path" -wait -2 "$LOCAL" "$REMOTE" \
				>/dev/null 2>&1
124 125
		fi
		;;
126
	bc3)
127 128
		if merge_mode
		then
129
			touch "$BACKUP"
130 131
			if $base_present
			then
132 133
				"$merge_tool_path" "$LOCAL" "$REMOTE" "$BASE" \
					-mergeoutput="$MERGED"
134
			else
135 136
				"$merge_tool_path" "$LOCAL" "$REMOTE" \
					-mergeoutput="$MERGED"
137
			fi
138
			check_unchanged
139 140 141 142
		else
			"$merge_tool_path" "$LOCAL" "$REMOTE"
		fi
		;;
143
	diffuse)
144 145
		if merge_mode
		then
146
			touch "$BACKUP"
147 148
			if $base_present
			then
149 150 151
				"$merge_tool_path" \
					"$LOCAL" "$MERGED" "$REMOTE" \
					"$BASE" | cat
152
			else
153
				"$merge_tool_path" \
154
					"$LOCAL" "$MERGED" "$REMOTE" | cat
155 156 157
			fi
			check_unchanged
		else
158
			"$merge_tool_path" "$LOCAL" "$REMOTE" | cat
159 160
		fi
		;;
161
	ecmerge)
162 163
		if merge_mode
		then
164
			touch "$BACKUP"
165 166
			if $base_present
			then
167 168
				"$merge_tool_path" "$BASE" "$LOCAL" "$REMOTE" \
					--default --mode=merge3 --to="$MERGED"
169
			else
170 171
				"$merge_tool_path" "$LOCAL" "$REMOTE" \
					--default --mode=merge2 --to="$MERGED"
172
			fi
173 174
			check_unchanged
		else
175 176
			"$merge_tool_path" --default --mode=diff2 \
				"$LOCAL" "$REMOTE"
177 178
		fi
		;;
179
	emerge)
180 181 182 183
		if merge_mode
		then
			if $base_present
			then
184
				"$merge_tool_path" \
185 186 187
					-f emerge-files-with-ancestor-command \
					"$LOCAL" "$REMOTE" "$BASE" \
					"$(basename "$MERGED")"
188 189
			else
				"$merge_tool_path" \
190 191 192
					-f emerge-files-command \
					"$LOCAL" "$REMOTE" \
					"$(basename "$MERGED")"
193
			fi
194
			status=$?
195
		else
196 197
			"$merge_tool_path" -f emerge-files-command \
				"$LOCAL" "$REMOTE"
198 199
		fi
		;;
200
	gvimdiff|vimdiff)
201 202
		if merge_mode
		then
203
			touch "$BACKUP"
204 205
			if $base_present
			then
206 207 208 209 210 211
				"$merge_tool_path" -f -d -c "wincmd J" \
					"$MERGED" "$LOCAL" "$BASE" "$REMOTE"
			else
				"$merge_tool_path" -f -d -c "wincmd l" \
					"$LOCAL" "$MERGED" "$REMOTE"
			fi
212 213
			check_unchanged
		else
214
			"$merge_tool_path" -R -f -d -c "wincmd l" \
215
				-c 'cd $GIT_PREFIX' \
216 217 218
				"$LOCAL" "$REMOTE"
		fi
		;;
219
	gvimdiff2|vimdiff2)
220 221
		if merge_mode
		then
222 223 224 225 226
			touch "$BACKUP"
			"$merge_tool_path" -f -d -c "wincmd l" \
				"$LOCAL" "$MERGED" "$REMOTE"
			check_unchanged
		else
227
			"$merge_tool_path" -R -f -d -c "wincmd l" \
228
				-c 'cd $GIT_PREFIX' \
229 230 231
				"$LOCAL" "$REMOTE"
		fi
		;;
232
	kdiff3)
233 234 235 236 237
		if merge_mode
		then
			if $base_present
			then
				"$merge_tool_path" --auto \
238 239 240 241 242
					--L1 "$MERGED (Base)" \
					--L2 "$MERGED (Local)" \
					--L3 "$MERGED (Remote)" \
					-o "$MERGED" \
					"$BASE" "$LOCAL" "$REMOTE" \
243
				>/dev/null 2>&1
244
			else
245
				"$merge_tool_path" --auto \
246 247 248 249
					--L1 "$MERGED (Local)" \
					--L2 "$MERGED (Remote)" \
					-o "$MERGED" \
					"$LOCAL" "$REMOTE" \
250
				>/dev/null 2>&1
251
			fi
252 253
			status=$?
		else
254
			"$merge_tool_path" --auto \
255 256
				--L1 "$MERGED (A)" \
				--L2 "$MERGED (B)" "$LOCAL" "$REMOTE" \
257
			>/dev/null 2>&1
258 259 260 261 262 263
		fi
		;;
	kompare)
		"$merge_tool_path" "$LOCAL" "$REMOTE"
		;;
	meld)
264 265
		if merge_mode
		then
266 267
			touch "$BACKUP"
			"$merge_tool_path" "$LOCAL" "$MERGED" "$REMOTE"
268 269
			check_unchanged
		else
270
			"$merge_tool_path" "$LOCAL" "$REMOTE"
271 272 273
		fi
		;;
	opendiff)
274 275
		if merge_mode
		then
276
			touch "$BACKUP"
277 278
			if $base_present
			then
279 280 281 282 283 284 285 286 287 288 289 290
				"$merge_tool_path" "$LOCAL" "$REMOTE" \
					-ancestor "$BASE" \
					-merge "$MERGED" | cat
			else
				"$merge_tool_path" "$LOCAL" "$REMOTE" \
					-merge "$MERGED" | cat
			fi
			check_unchanged
		else
			"$merge_tool_path" "$LOCAL" "$REMOTE" | cat
		fi
		;;
291
	p4merge)
292 293
		if merge_mode
		then
294 295 296
			touch "$BACKUP"
			$base_present || >"$BASE"
			"$merge_tool_path" "$BASE" "$LOCAL" "$REMOTE" "$MERGED"
297 298
			check_unchanged
		else
299
			"$merge_tool_path" "$LOCAL" "$REMOTE"
300 301
		fi
		;;
302
	tkdiff)
303 304 305 306
		if merge_mode
		then
			if $base_present
			then
307 308
				"$merge_tool_path" -a "$BASE" \
					-o "$MERGED" "$LOCAL" "$REMOTE"
309 310
			else
				"$merge_tool_path" \
311
					-o "$MERGED" "$LOCAL" "$REMOTE"
312 313 314
			fi
			status=$?
		else
315
			"$merge_tool_path" "$LOCAL" "$REMOTE"
316 317 318
		fi
		;;
	tortoisemerge)
319 320
		if $base_present
		then
321 322 323 324 325 326 327 328 329 330
			touch "$BACKUP"
			"$merge_tool_path" \
				-base:"$BASE" -mine:"$LOCAL" \
				-theirs:"$REMOTE" -merged:"$MERGED"
			check_unchanged
		else
			echo "TortoiseMerge cannot be used without a base" 1>&2
			status=1
		fi
		;;
331
	xxdiff)
332 333
		if merge_mode
		then
334
			touch "$BACKUP"
335 336
			if $base_present
			then
337 338 339 340 341 342
				"$merge_tool_path" -X --show-merged-pane \
					-R 'Accel.SaveAsMerged: "Ctrl-S"' \
					-R 'Accel.Search: "Ctrl+F"' \
					-R 'Accel.SearchForward: "Ctrl-G"' \
					--merged-file "$MERGED" \
					"$LOCAL" "$BASE" "$REMOTE"
343
			else
344 345 346 347 348 349
				"$merge_tool_path" -X $extra \
					-R 'Accel.SaveAsMerged: "Ctrl-S"' \
					-R 'Accel.Search: "Ctrl+F"' \
					-R 'Accel.SearchForward: "Ctrl-G"' \
					--merged-file "$MERGED" \
					"$LOCAL" "$REMOTE"
350 351 352
			fi
			check_unchanged
		else
353 354 355 356
			"$merge_tool_path" \
				-R 'Accel.Search: "Ctrl+F"' \
				-R 'Accel.SearchForward: "Ctrl-G"' \
				"$LOCAL" "$REMOTE"
357 358
		fi
		;;
359
	*)
360
		merge_tool_cmd="$(get_merge_tool_cmd "$1")"
361 362 363 364
		if test -z "$merge_tool_cmd"
		then
			if merge_mode
			then
365 366 367 368
				status=1
			fi
			break
		fi
369 370
		if merge_mode
		then
371 372
			trust_exit_code="$(git config --bool \
				mergetool."$1".trustExitCode || echo false)"
373 374
			if test "$trust_exit_code" = "false"
			then
375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390
				touch "$BACKUP"
				( eval $merge_tool_cmd )
				check_unchanged
			else
				( eval $merge_tool_cmd )
				status=$?
			fi
		else
			( eval $merge_tool_cmd )
		fi
		;;
	esac
	return $status
}

guess_merge_tool () {
391 392
	if merge_mode
	then
393 394 395 396
		tools="tortoisemerge"
	else
		tools="kompare"
	fi
397 398 399 400
	if test -n "$DISPLAY"
	then
		if test -n "$GNOME_DESKTOP_SESSION_ID"
		then
401 402 403 404
			tools="meld opendiff kdiff3 tkdiff xxdiff $tools"
		else
			tools="opendiff kdiff3 tkdiff xxdiff meld $tools"
		fi
405
		tools="$tools gvimdiff diffuse ecmerge p4merge araxis bc3"
406
	fi
407 408
	case "${VISUAL:-$EDITOR}" in
	*vim*)
409
		tools="$tools vimdiff emerge"
410 411
		;;
	*)
412
		tools="$tools emerge vimdiff"
413 414
		;;
	esac
415 416 417 418 419 420
	echo >&2 "merge tool candidates: $tools"

	# Loop over each candidate and stop when a valid merge tool is found.
	for i in $tools
	do
		merge_tool_path="$(translate_merge_tool_path "$i")"
421 422
		if type "$merge_tool_path" >/dev/null 2>&1
		then
423 424
			echo "$i"
			return 0
425 426 427
		fi
	done

428 429
	echo >&2 "No known merge resolution program available."
	return 1
430 431 432 433 434
}

get_configured_merge_tool () {
	# Diff mode first tries diff.tool and falls back to merge.tool.
	# Merge mode only checks merge.tool
435 436
	if diff_mode
	then
437 438 439
		merge_tool=$(git config diff.tool || git config merge.tool)
	else
		merge_tool=$(git config merge.tool)
440
	fi
441 442
	if test -n "$merge_tool" && ! valid_tool "$merge_tool"
	then
443 444 445 446
		echo >&2 "git config option $TOOL_MODE.tool set to unknown tool: $merge_tool"
		echo >&2 "Resetting to default..."
		return 1
	fi
447
	echo "$merge_tool"
448 449 450 451
}

get_merge_tool_path () {
	# A merge tool has been set, so verify that it's valid.
452 453
	if test -n "$1"
	then
454 455 456 457
		merge_tool="$1"
	else
		merge_tool="$(get_merge_tool)"
	fi
458 459
	if ! valid_tool "$merge_tool"
	then
460 461 462
		echo >&2 "Unknown merge tool $merge_tool"
		exit 1
	fi
463 464
	if diff_mode
	then
465
		merge_tool_path=$(git config difftool."$merge_tool".path ||
466
				  git config mergetool."$merge_tool".path)
467 468
	else
		merge_tool_path=$(git config mergetool."$merge_tool".path)
469
	fi
470 471
	if test -z "$merge_tool_path"
	then
472
		merge_tool_path="$(translate_merge_tool_path "$merge_tool")"
473
	fi
474
	if test -z "$(get_merge_tool_cmd "$merge_tool")" &&
475 476
		! type "$merge_tool_path" >/dev/null 2>&1
	then
477
		echo >&2 "The $TOOL_MODE tool $merge_tool is not available as"\
478
			 "'$merge_tool_path'"
479 480 481 482 483 484 485
		exit 1
	fi
	echo "$merge_tool_path"
}

get_merge_tool () {
	# Check if a merge tool has been configured
486
	merge_tool=$(get_configured_merge_tool)
487
	# Try to guess an appropriate merge tool if no tool has been set.
488 489
	if test -z "$merge_tool"
	then
490
		merge_tool="$(guess_merge_tool)" || exit
491 492 493
	fi
	echo "$merge_tool"
}