123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485 |
- # 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
- variants="$variants$(list_tool_variants)\n"
- done
- variants="$(echo "$variants" | sort | uniq)"
- 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%s\n" "$per_line_prefix" "$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
- }
- gui_mode () {
- 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" && 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 )
- }
- }
- setup_tool () {
- tool="$1"
- # Fallback definitions, to be overridden by tools.
- can_merge () {
- return 0
- }
- can_diff () {
- return 0
- }
- diff_cmd () {
- return 1
- }
- merge_cmd () {
- return 1
- }
- 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
- return $?
- 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
- 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
- }
- # 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"
- # Bring tool-specific functions into scope
- setup_tool "$1" || return 1
- 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 merge 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)
- # 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"
- }
|