|  | # git-mergetool--lib is a shell library for common merge tool functions | 
|  |  | 
|  | : ${MERGE_TOOLS_DIR=$(git --exec-path)/mergetools} | 
|  |  | 
|  | IFS=' | 
|  | ' | 
|  |  | 
|  | mode_ok () { | 
|  | if diff_mode | 
|  | then | 
|  | can_diff | 
|  | elif merge_mode | 
|  | then | 
|  | can_merge | 
|  | else | 
|  | false | 
|  | fi | 
|  | } | 
|  |  | 
|  | is_available () { | 
|  | merge_tool_path=$(translate_merge_tool_path "$1") && | 
|  | type "$merge_tool_path" >/dev/null 2>&1 | 
|  | } | 
|  |  | 
|  | list_config_tools () { | 
|  | section=$1 | 
|  | line_prefix=${2:-} | 
|  |  | 
|  | git config --get-regexp $section'\..*\.cmd' | | 
|  | while read -r key value | 
|  | do | 
|  | toolname=${key#$section.} | 
|  | toolname=${toolname%.cmd} | 
|  |  | 
|  | printf "%s%s\n" "$line_prefix" "$toolname" | 
|  | done | 
|  | } | 
|  |  | 
|  | show_tool_names () { | 
|  | condition=${1:-true} per_line_prefix=${2:-} preamble=${3:-} | 
|  | not_found_msg=${4:-} | 
|  | extra_content=${5:-} | 
|  |  | 
|  | shown_any= | 
|  | ( cd "$MERGE_TOOLS_DIR" && ls ) | { | 
|  | while read scriptname | 
|  | do | 
|  | setup_tool "$scriptname" 2>/dev/null | 
|  | # We need an actual line feed here | 
|  | variants="$variants | 
|  | $(list_tool_variants)" | 
|  | done | 
|  | variants="$(echo "$variants" | sort -u)" | 
|  |  | 
|  | for toolname in $variants | 
|  | do | 
|  | if setup_tool "$toolname" 2>/dev/null && | 
|  | (eval "$condition" "$toolname") | 
|  | then | 
|  | if test -n "$preamble" | 
|  | then | 
|  | printf "%s\n" "$preamble" | 
|  | preamble= | 
|  | fi | 
|  | shown_any=yes | 
|  | printf "%s%-15s  %s\n" "$per_line_prefix" "$toolname" $(diff_mode && diff_cmd_help "$toolname" || merge_cmd_help "$toolname") | 
|  | fi | 
|  | done | 
|  |  | 
|  | if test -n "$extra_content" | 
|  | then | 
|  | if test -n "$preamble" | 
|  | then | 
|  | # Note: no '\n' here since we don't want a | 
|  | # blank line if there is no initial content. | 
|  | printf "%s" "$preamble" | 
|  | preamble= | 
|  | fi | 
|  | shown_any=yes | 
|  | printf "\n%s\n" "$extra_content" | 
|  | fi | 
|  |  | 
|  | if test -n "$preamble" && test -n "$not_found_msg" | 
|  | then | 
|  | printf "%s\n" "$not_found_msg" | 
|  | fi | 
|  |  | 
|  | test -n "$shown_any" | 
|  | } | 
|  | } | 
|  |  | 
|  | diff_mode () { | 
|  | test "$TOOL_MODE" = diff | 
|  | } | 
|  |  | 
|  | merge_mode () { | 
|  | test "$TOOL_MODE" = merge | 
|  | } | 
|  |  | 
|  | get_gui_default () { | 
|  | if diff_mode | 
|  | then | 
|  | GUI_DEFAULT_KEY="difftool.guiDefault" | 
|  | else | 
|  | GUI_DEFAULT_KEY="mergetool.guiDefault" | 
|  | fi | 
|  | GUI_DEFAULT_CONFIG_LCASE=$(git config --default false --get "$GUI_DEFAULT_KEY" | tr 'A-Z' 'a-z') | 
|  | if test "$GUI_DEFAULT_CONFIG_LCASE" = "auto" | 
|  | then | 
|  | if test -n "$DISPLAY" | 
|  | then | 
|  | GUI_DEFAULT=true | 
|  | else | 
|  | GUI_DEFAULT=false | 
|  | fi | 
|  | else | 
|  | GUI_DEFAULT=$(git config --default false --bool --get "$GUI_DEFAULT_KEY") | 
|  | subshell_exit_status=$? | 
|  | if test $subshell_exit_status -ne 0 | 
|  | then | 
|  | exit $subshell_exit_status | 
|  | fi | 
|  | fi | 
|  | echo $GUI_DEFAULT | 
|  | } | 
|  |  | 
|  | gui_mode () { | 
|  | if test -z "$GIT_MERGETOOL_GUI" | 
|  | then | 
|  | GIT_MERGETOOL_GUI=$(get_gui_default) | 
|  | if test $? -ne 0 | 
|  | then | 
|  | exit 2 | 
|  | fi | 
|  | fi | 
|  | test "$GIT_MERGETOOL_GUI" = true | 
|  | } | 
|  |  | 
|  | translate_merge_tool_path () { | 
|  | echo "$1" | 
|  | } | 
|  |  | 
|  | check_unchanged () { | 
|  | if test "$MERGED" -nt "$BACKUP" | 
|  | then | 
|  | return 0 | 
|  | else | 
|  | while true | 
|  | do | 
|  | echo "$MERGED seems unchanged." | 
|  | printf "Was the merge successful [y/n]? " | 
|  | read answer || return 1 | 
|  | case "$answer" in | 
|  | y*|Y*) return 0 ;; | 
|  | n*|N*) return 1 ;; | 
|  | esac | 
|  | done | 
|  | fi | 
|  | } | 
|  |  | 
|  | valid_tool () { | 
|  | setup_tool "$1" 2>/dev/null && return 0 | 
|  | cmd=$(get_merge_tool_cmd "$1") | 
|  | test -n "$cmd" | 
|  | } | 
|  |  | 
|  | setup_user_tool () { | 
|  | merge_tool_cmd=$(get_merge_tool_cmd "$tool") | 
|  | test -n "$merge_tool_cmd" || return 1 | 
|  |  | 
|  | diff_cmd () { | 
|  | ( eval $merge_tool_cmd ) | 
|  | } | 
|  |  | 
|  | merge_cmd () { | 
|  | ( eval $merge_tool_cmd ) | 
|  | } | 
|  |  | 
|  | list_tool_variants () { | 
|  | echo "$tool" | 
|  | } | 
|  | } | 
|  |  | 
|  | setup_tool () { | 
|  | tool="$1" | 
|  |  | 
|  | # Fallback definitions, to be overridden by tools. | 
|  | can_merge () { | 
|  | return 0 | 
|  | } | 
|  |  | 
|  | can_diff () { | 
|  | return 0 | 
|  | } | 
|  |  | 
|  | diff_cmd () { | 
|  | return 1 | 
|  | } | 
|  |  | 
|  | diff_cmd_help () { | 
|  | return 0 | 
|  | } | 
|  |  | 
|  | merge_cmd () { | 
|  | return 1 | 
|  | } | 
|  |  | 
|  | merge_cmd_help () { | 
|  | return 0 | 
|  | } | 
|  |  | 
|  | hide_resolved_enabled () { | 
|  | return 0 | 
|  | } | 
|  |  | 
|  | translate_merge_tool_path () { | 
|  | echo "$1" | 
|  | } | 
|  |  | 
|  | list_tool_variants () { | 
|  | echo "$tool" | 
|  | } | 
|  |  | 
|  | # Most tools' exit codes cannot be trusted, so By default we ignore | 
|  | # their exit code and check the merged file's modification time in | 
|  | # check_unchanged() to determine whether or not the merge was | 
|  | # successful.  The return value from run_merge_cmd, by default, is | 
|  | # determined by check_unchanged(). | 
|  | # | 
|  | # When a tool's exit code can be trusted then the return value from | 
|  | # run_merge_cmd is simply the tool's exit code, and check_unchanged() | 
|  | # is not called. | 
|  | # | 
|  | # The return value of exit_code_trustable() tells us whether or not we | 
|  | # can trust the tool's exit code. | 
|  | # | 
|  | # User-defined and built-in tools default to false. | 
|  | # Built-in tools advertise that their exit code is trustable by | 
|  | # redefining exit_code_trustable() to true. | 
|  |  | 
|  | exit_code_trustable () { | 
|  | false | 
|  | } | 
|  |  | 
|  | if test -f "$MERGE_TOOLS_DIR/$tool" | 
|  | then | 
|  | . "$MERGE_TOOLS_DIR/$tool" | 
|  | elif test -f "$MERGE_TOOLS_DIR/${tool%[0-9]}" | 
|  | then | 
|  | . "$MERGE_TOOLS_DIR/${tool%[0-9]}" | 
|  | else | 
|  | setup_user_tool | 
|  | rc=$? | 
|  | if test $rc -ne 0 | 
|  | then | 
|  | echo >&2 "error: ${TOOL_MODE}tool.$tool.cmd not set for tool '$tool'" | 
|  | fi | 
|  | return $rc | 
|  | fi | 
|  |  | 
|  | # Now let the user override the default command for the tool.  If | 
|  | # they have not done so then this will return 1 which we ignore. | 
|  | setup_user_tool | 
|  |  | 
|  | if ! list_tool_variants | grep -q "^$tool$" | 
|  | then | 
|  | echo "error: unknown tool variant '$tool'" >&2 | 
|  | return 1 | 
|  | fi | 
|  |  | 
|  | if merge_mode && ! can_merge | 
|  | then | 
|  | echo "error: '$tool' can not be used to resolve merges" >&2 | 
|  | return 1 | 
|  | elif diff_mode && ! can_diff | 
|  | then | 
|  | echo "error: '$tool' can only be used to resolve merges" >&2 | 
|  | return 1 | 
|  | fi | 
|  | return 0 | 
|  | } | 
|  |  | 
|  | get_merge_tool_cmd () { | 
|  | merge_tool="$1" | 
|  | if diff_mode | 
|  | then | 
|  | git config "difftool.$merge_tool.cmd" || | 
|  | git config "mergetool.$merge_tool.cmd" | 
|  | else | 
|  | git config "mergetool.$merge_tool.cmd" | 
|  | fi | 
|  | } | 
|  |  | 
|  | trust_exit_code () { | 
|  | if git config --bool "mergetool.$1.trustExitCode" | 
|  | then | 
|  | :; # OK | 
|  | elif exit_code_trustable | 
|  | then | 
|  | echo true | 
|  | else | 
|  | echo false | 
|  | fi | 
|  | } | 
|  |  | 
|  | initialize_merge_tool () { | 
|  | # Bring tool-specific functions into scope | 
|  | setup_tool "$1" || return 1 | 
|  | } | 
|  |  | 
|  | # Entry point for running tools | 
|  | run_merge_tool () { | 
|  | # 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 | 
|  |  | 
|  | merge_tool_path=$(get_merge_tool_path "$1") || exit | 
|  | base_present="$2" | 
|  |  | 
|  | if merge_mode | 
|  | then | 
|  | run_merge_cmd "$1" | 
|  | else | 
|  | run_diff_cmd "$1" | 
|  | fi | 
|  | } | 
|  |  | 
|  | # Run a either a configured or built-in diff tool | 
|  | run_diff_cmd () { | 
|  | diff_cmd "$1" | 
|  | } | 
|  |  | 
|  | # Run a either a configured or built-in merge tool | 
|  | run_merge_cmd () { | 
|  | mergetool_trust_exit_code=$(trust_exit_code "$1") | 
|  | if test "$mergetool_trust_exit_code" = "true" | 
|  | then | 
|  | merge_cmd "$1" | 
|  | else | 
|  | touch "$BACKUP" | 
|  | merge_cmd "$1" | 
|  | check_unchanged | 
|  | fi | 
|  | } | 
|  |  | 
|  | list_merge_tool_candidates () { | 
|  | 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 | 
|  | tools="$tools gvimdiff diffuse diffmerge ecmerge" | 
|  | tools="$tools p4merge araxis bc codecompare" | 
|  | tools="$tools smerge" | 
|  | fi | 
|  | case "${VISUAL:-$EDITOR}" in | 
|  | *nvim*) | 
|  | tools="$tools nvimdiff vimdiff emerge" | 
|  | ;; | 
|  | *vim*) | 
|  | tools="$tools vimdiff nvimdiff emerge" | 
|  | ;; | 
|  | *) | 
|  | tools="$tools emerge vimdiff nvimdiff" | 
|  | ;; | 
|  | esac | 
|  | } | 
|  |  | 
|  | show_tool_help () { | 
|  | tool_opt="'git ${TOOL_MODE}tool --tool=<tool>'" | 
|  |  | 
|  | tab='	' | 
|  | LF=' | 
|  | ' | 
|  | any_shown=no | 
|  |  | 
|  | cmd_name=${TOOL_MODE}tool | 
|  | config_tools=$({ | 
|  | diff_mode && list_config_tools difftool "$tab$tab" | 
|  | list_config_tools mergetool "$tab$tab" | 
|  | } | sort) | 
|  | extra_content= | 
|  | if test -n "$config_tools" | 
|  | then | 
|  | extra_content="${tab}user-defined:${LF}$config_tools" | 
|  | fi | 
|  |  | 
|  | show_tool_names 'mode_ok && is_available' "$tab$tab" \ | 
|  | "$tool_opt may be set to one of the following:" \ | 
|  | "No suitable tool for 'git $cmd_name --tool=<tool>' found." \ | 
|  | "$extra_content" && | 
|  | any_shown=yes | 
|  |  | 
|  | show_tool_names 'mode_ok && ! is_available' "$tab$tab" \ | 
|  | "${LF}The following tools are valid, but not currently available:" && | 
|  | any_shown=yes | 
|  |  | 
|  | if test "$any_shown" = yes | 
|  | 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 | 
|  | } | 
|  |  | 
|  | guess_merge_tool () { | 
|  | list_merge_tool_candidates | 
|  | cat >&2 <<-EOF | 
|  |  | 
|  | This message is displayed because '$TOOL_MODE.tool' is not configured. | 
|  | See 'git ${TOOL_MODE}tool --tool-help' or 'git help config' for more details. | 
|  | 'git ${TOOL_MODE}tool' will now attempt to use one of the following tools: | 
|  | $tools | 
|  | EOF | 
|  |  | 
|  | # Loop over each candidate and stop when a valid merge tool is found. | 
|  | IFS=' ' | 
|  | for tool in $tools | 
|  | do | 
|  | is_available "$tool" && echo "$tool" && return 0 | 
|  | done | 
|  |  | 
|  | echo >&2 "No known ${TOOL_MODE} tool is available." | 
|  | return 1 | 
|  | } | 
|  |  | 
|  | get_configured_merge_tool () { | 
|  | keys= | 
|  | if diff_mode | 
|  | then | 
|  | if gui_mode | 
|  | then | 
|  | keys="diff.guitool merge.guitool diff.tool merge.tool" | 
|  | else | 
|  | keys="diff.tool merge.tool" | 
|  | fi | 
|  | else | 
|  | if gui_mode | 
|  | then | 
|  | keys="merge.guitool merge.tool" | 
|  | else | 
|  | keys="merge.tool" | 
|  | fi | 
|  | fi | 
|  |  | 
|  | merge_tool=$( | 
|  | IFS=' ' | 
|  | for key in $keys | 
|  | do | 
|  | selected=$(git config $key) | 
|  | if test -n "$selected" | 
|  | then | 
|  | echo "$selected" | 
|  | return | 
|  | fi | 
|  | done) | 
|  |  | 
|  | if test -n "$merge_tool" && ! valid_tool "$merge_tool" | 
|  | then | 
|  | echo >&2 "git config option $TOOL_MODE.${gui_prefix}tool set to unknown tool: $merge_tool" | 
|  | echo >&2 "Resetting to default..." | 
|  | return 1 | 
|  | fi | 
|  | echo "$merge_tool" | 
|  | } | 
|  |  | 
|  | get_merge_tool_path () { | 
|  | # A merge tool has been set, so verify that it's valid. | 
|  | merge_tool="$1" | 
|  | if ! valid_tool "$merge_tool" | 
|  | then | 
|  | echo >&2 "Unknown $TOOL_MODE tool $merge_tool" | 
|  | exit 1 | 
|  | fi | 
|  | if diff_mode | 
|  | then | 
|  | 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) | 
|  | fi | 
|  | if test -z "$merge_tool_path" | 
|  | then | 
|  | merge_tool_path=$(translate_merge_tool_path "$merge_tool") | 
|  | fi | 
|  | 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'" | 
|  | exit 1 | 
|  | fi | 
|  | echo "$merge_tool_path" | 
|  | } | 
|  |  | 
|  | get_merge_tool () { | 
|  | is_guessed=false | 
|  | # Check if a merge tool has been configured | 
|  | merge_tool=$(get_configured_merge_tool) | 
|  | subshell_exit_status=$? | 
|  | if test $subshell_exit_status -gt "1" | 
|  | then | 
|  | exit $subshell_exit_status | 
|  | fi | 
|  | # Try to guess an appropriate merge tool if no tool has been set. | 
|  | if test -z "$merge_tool" | 
|  | then | 
|  | merge_tool=$(guess_merge_tool) || exit | 
|  | is_guessed=true | 
|  | fi | 
|  | echo "$merge_tool" | 
|  | test "$is_guessed" = false | 
|  | } | 
|  |  | 
|  | mergetool_find_win32_cmd () { | 
|  | executable=$1 | 
|  | sub_directory=$2 | 
|  |  | 
|  | # Use $executable if it exists in $PATH | 
|  | if type -p "$executable" >/dev/null 2>&1 | 
|  | then | 
|  | printf '%s' "$executable" | 
|  | return | 
|  | fi | 
|  |  | 
|  | # Look for executable in the typical locations | 
|  | for directory in $(env | grep -Ei '^PROGRAM(FILES(\(X86\))?|W6432)=' | | 
|  | cut -d '=' -f 2- | sort -u) | 
|  | do | 
|  | if test -n "$directory" && test -x "$directory/$sub_directory/$executable" | 
|  | then | 
|  | printf '%s' "$directory/$sub_directory/$executable" | 
|  | return | 
|  | fi | 
|  | done | 
|  |  | 
|  | printf '%s' "$executable" | 
|  | } |