git-mergetool--lib.sh 9.33 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 35 36 37 38 39 40
}

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

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

get_merge_tool_cmd () {
74 75 76 77 78 79 80 81 82 83 84 85
	# Prints the custom command for a merge tool
	if test -n "$1"; then
		merge_tool="$1"
	else
		merge_tool="$(get_merge_tool)"
	fi
	if diff_mode; then
		echo "$(git config difftool.$merge_tool.cmd ||
		        git config mergetool.$merge_tool.cmd)"
	else
		echo "$(git config mergetool.$merge_tool.cmd)"
	fi
86 87 88
}

run_merge_tool () {
89
	merge_tool_path="$(get_merge_tool_path "$1")" || exit
90 91 92 93
	base_present="$2"
	status=0

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

385 386
	echo >&2 "No known merge resolution program available."
	return 1
387 388 389 390 391 392
}

get_configured_merge_tool () {
	# Diff mode first tries diff.tool and falls back to merge.tool.
	# Merge mode only checks merge.tool
	if diff_mode; then
393 394 395
		merge_tool=$(git config diff.tool || git config merge.tool)
	else
		merge_tool=$(git config merge.tool)
396 397 398 399 400 401
	fi
	if test -n "$merge_tool" && ! valid_tool "$merge_tool"; then
		echo >&2 "git config option $TOOL_MODE.tool set to unknown tool: $merge_tool"
		echo >&2 "Resetting to default..."
		return 1
	fi
402
	echo "$merge_tool"
403 404 405 406
}

get_merge_tool_path () {
	# A merge tool has been set, so verify that it's valid.
407 408 409 410 411
	if test -n "$1"; then
		merge_tool="$1"
	else
		merge_tool="$(get_merge_tool)"
	fi
412 413 414 415 416
	if ! valid_tool "$merge_tool"; then
		echo >&2 "Unknown merge tool $merge_tool"
		exit 1
	fi
	if diff_mode; then
417 418 419 420
		merge_tool_path=$(git config difftool."$merge_tool".path ||
		                  git config mergetool."$merge_tool".path)
	else
		merge_tool_path=$(git config mergetool."$merge_tool".path)
421 422
	fi
	if test -z "$merge_tool_path"; then
423
		merge_tool_path="$(translate_merge_tool_path "$merge_tool")"
424
	fi
425 426 427 428
	if test -z "$(get_merge_tool_cmd "$merge_tool")" &&
	! type "$merge_tool_path" > /dev/null 2>&1; then
		echo >&2 "The $TOOL_MODE tool $merge_tool is not available as"\
		         "'$merge_tool_path'"
429 430 431 432 433 434 435
		exit 1
	fi
	echo "$merge_tool_path"
}

get_merge_tool () {
	# Check if a merge tool has been configured
436
	merge_tool=$(get_configured_merge_tool)
437 438
	# Try to guess an appropriate merge tool if no tool has been set.
	if test -z "$merge_tool"; then
439
		merge_tool="$(guess_merge_tool)" || exit
440 441 442
	fi
	echo "$merge_tool"
}