git-mergetool--lib.sh 6.22 KB
Newer Older
1
#!/bin/sh
2
# git-mergetool--lib is a library for common merge tool functions
3 4
MERGE_TOOLS_DIR=$(git --exec-path)/mergetools

5 6 7 8 9 10 11 12 13
diff_mode() {
	test "$TOOL_MODE" = diff
}

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

translate_merge_tool_path () {
14
	echo "$1"
15 16 17
}

check_unchanged () {
18 19
	if test "$MERGED" -nt "$BACKUP"
	then
20 21
		status=0
	else
22 23
		while true
		do
24 25
			echo "$MERGED seems unchanged."
			printf "Was the merge successful? [y/n] "
26
			read answer || return 1
27 28 29 30 31 32 33 34 35
			case "$answer" in
			y*|Y*) status=0; break ;;
			n*|N*) status=1; break ;;
			esac
		done
	fi
}

valid_tool () {
36 37 38
	setup_tool "$1" && return 0
	cmd=$(get_merge_tool_cmd "$1")
	test -n "$cmd"
39 40 41
}

setup_tool () {
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
	tool="$1"

	# Fallback definitions, to be overriden by tools.
	can_merge () {
		return 0
	}

	can_diff () {
		return 0
	}

	diff_cmd () {
		status=1
		return $status
	}

	merge_cmd () {
		status=1
		return $status
	}
62

63 64 65 66 67
	translate_merge_tool_path () {
		echo "$1"
	}

	if ! test -f "$MERGE_TOOLS_DIR/$tool"
68
	then
69 70 71 72 73
		# Use a special return code for this case since we want to
		# source "defaults" even when an explicit tool path is
		# configured since the user can use that to override the
		# default path in the scriptlet.
		return 2
74 75 76
	fi

	# Load the redefined functions
77
	. "$MERGE_TOOLS_DIR/$tool"
78 79 80 81

	if merge_mode && ! can_merge
	then
		echo "error: '$tool' can not be used to resolve merges" >&2
82
		return 1
83 84 85
	elif diff_mode && ! can_diff
	then
		echo "error: '$tool' can only be used to resolve merges" >&2
86
		return 1
87 88
	fi
	return 0
89 90 91
}

get_merge_tool_cmd () {
92
	merge_tool="$1"
93 94
	if diff_mode
	then
95 96
		git config "difftool.$merge_tool.cmd" ||
		git config "mergetool.$merge_tool.cmd"
97
	else
98
		git config "mergetool.$merge_tool.cmd"
99
	fi
100 101
}

102
# Entry point for running tools
103
run_merge_tool () {
104 105 106 107 108
	# 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

109
	merge_tool_path=$(get_merge_tool_path "$1") || exit
110 111 112
	base_present="$2"
	status=0

113 114
	# Bring tool-specific functions into scope
	setup_tool "$1"
115 116 117 118 119 120 121 122 123 124 125 126 127
	exitcode=$?
	case $exitcode in
	0)
		:
		;;
	2)
		# The configured tool is not a built-in tool.
		test -n "$merge_tool_path" || return 1
		;;
	*)
		return $exitcode
		;;
	esac
128 129 130

	if merge_mode
	then
131
		run_merge_cmd "$1"
132
	else
133
		run_diff_cmd "$1"
134
	fi
135 136 137
	return $status
}

138 139
# Run a either a configured or built-in diff tool
run_diff_cmd () {
140
	merge_tool_cmd=$(get_merge_tool_cmd "$1")
141 142 143 144 145 146 147 148 149 150 151 152
	if test -n "$merge_tool_cmd"
	then
		( eval $merge_tool_cmd )
		status=$?
		return $status
	else
		diff_cmd "$1"
	fi
}

# Run a either a configured or built-in merge tool
run_merge_cmd () {
153
	merge_tool_cmd=$(get_merge_tool_cmd "$1")
154 155
	if test -n "$merge_tool_cmd"
	then
156 157
		trust_exit_code=$(git config --bool \
			"mergetool.$1.trustExitCode" || echo false)
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
		if test "$trust_exit_code" = "false"
		then
			touch "$BACKUP"
			( eval $merge_tool_cmd )
			status=$?
			check_unchanged
		else
			( eval $merge_tool_cmd )
			status=$?
		fi
		return $status
	else
		merge_cmd "$1"
	fi
}

174
list_merge_tool_candidates () {
175 176
	if merge_mode
	then
177 178 179 180
		tools="tortoisemerge"
	else
		tools="kompare"
	fi
181 182 183 184
	if test -n "$DISPLAY"
	then
		if test -n "$GNOME_DESKTOP_SESSION_ID"
		then
185 186 187 188
			tools="meld opendiff kdiff3 tkdiff xxdiff $tools"
		else
			tools="opendiff kdiff3 tkdiff xxdiff meld $tools"
		fi
189
		tools="$tools gvimdiff diffuse ecmerge p4merge araxis bc3 codecompare"
190
	fi
191 192
	case "${VISUAL:-$EDITOR}" in
	*vim*)
193
		tools="$tools vimdiff emerge"
194 195
		;;
	*)
196
		tools="$tools emerge vimdiff"
197 198
		;;
	esac
199 200
}

201 202 203
show_tool_help () {
	unavailable= available= LF='
'
204
	for i in "$MERGE_TOOLS_DIR"/*
205
	do
206 207
		tool=$(basename "$i")
		setup_tool "$tool" 2>/dev/null || continue
208 209

		merge_tool_path=$(translate_merge_tool_path "$tool")
210 211
		if type "$merge_tool_path" >/dev/null 2>&1
		then
212
			available="$available$tool$LF"
213
		else
214
			unavailable="$unavailable$tool$LF"
215 216
		fi
	done
217 218

	cmd_name=${TOOL_MODE}tool
219 220
	if test -n "$available"
	then
221
		echo "'git $cmd_name --tool=<tool>' may be set to one of the following:"
222 223
		echo "$available" | sort | sed -e 's/^/	/'
	else
224
		echo "No suitable tool for 'git $cmd_name --tool=<tool>' found."
225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240
	fi
	if test -n "$unavailable"
	then
		echo
		echo 'The following tools are valid, but not currently available:'
		echo "$unavailable" | sort | sed -e 's/^/	/'
	fi
	if test -n "$unavailable$available"
	then
		echo
		echo "Some of the tools listed above only work in a windowed"
		echo "environment. If run in a terminal-only session, they will fail."
	fi
	exit 0
}

241 242
guess_merge_tool () {
	list_merge_tool_candidates
243 244 245 246 247
	echo >&2 "merge tool candidates: $tools"

	# Loop over each candidate and stop when a valid merge tool is found.
	for i in $tools
	do
248
		merge_tool_path=$(translate_merge_tool_path "$i")
249 250
		if type "$merge_tool_path" >/dev/null 2>&1
		then
251 252
			echo "$i"
			return 0
253 254 255
		fi
	done

256 257
	echo >&2 "No known merge resolution program available."
	return 1
258 259 260 261 262
}

get_configured_merge_tool () {
	# Diff mode first tries diff.tool and falls back to merge.tool.
	# Merge mode only checks merge.tool
263 264
	if diff_mode
	then
265 266 267
		merge_tool=$(git config diff.tool || git config merge.tool)
	else
		merge_tool=$(git config merge.tool)
268
	fi
269 270
	if test -n "$merge_tool" && ! valid_tool "$merge_tool"
	then
271 272 273 274
		echo >&2 "git config option $TOOL_MODE.tool set to unknown tool: $merge_tool"
		echo >&2 "Resetting to default..."
		return 1
	fi
275
	echo "$merge_tool"
276 277 278 279
}

get_merge_tool_path () {
	# A merge tool has been set, so verify that it's valid.
280
	merge_tool="$1"
281 282
	if ! valid_tool "$merge_tool"
	then
283 284 285
		echo >&2 "Unknown merge tool $merge_tool"
		exit 1
	fi
286 287
	if diff_mode
	then
288
		merge_tool_path=$(git config difftool."$merge_tool".path ||
289
				  git config mergetool."$merge_tool".path)
290 291
	else
		merge_tool_path=$(git config mergetool."$merge_tool".path)
292
	fi
293 294
	if test -z "$merge_tool_path"
	then
295
		merge_tool_path=$(translate_merge_tool_path "$merge_tool")
296
	fi
297
	if test -z "$(get_merge_tool_cmd "$merge_tool")" &&
298 299
		! type "$merge_tool_path" >/dev/null 2>&1
	then
300
		echo >&2 "The $TOOL_MODE tool $merge_tool is not available as"\
301
			 "'$merge_tool_path'"
302 303 304 305 306 307 308
		exit 1
	fi
	echo "$merge_tool_path"
}

get_merge_tool () {
	# Check if a merge tool has been configured
309
	merge_tool=$(get_configured_merge_tool)
310
	# Try to guess an appropriate merge tool if no tool has been set.
311 312
	if test -z "$merge_tool"
	then
313
		merge_tool=$(guess_merge_tool) || exit
314 315 316
	fi
	echo "$merge_tool"
}