# git-mergetool--lib is a shell library for common merge tool functions : $(MERGE_TOOLS_DIR=$(git --exec-path)/mergetools) setglobal IFS = '' '' proc mode_ok { if diff_mode { can_diff } elif merge_mode { can_merge } else { false } } proc is_available { setglobal merge_tool_path = $[translate_merge_tool_path $1] && type $merge_tool_path >/dev/null !2 > !1 } proc list_config_tools { setglobal section = $1 setglobal line_prefix = $(2:-) git config --get-regexp $section'\..*\.cmd' | while read -r key value { setglobal toolname = $(key#$section.) setglobal toolname = $(toolname%.cmd) printf "%s%s\n" $line_prefix $toolname } } proc show_tool_names { setglobal condition = $(1:-true), per_line_prefix = $(2:-), preamble = $(3:-) setglobal not_found_msg = $(4:-) setglobal extra_content = $(5:-) setglobal shown_any = '' shell { cd $MERGE_TOOLS_DIR && ls } | do { while read toolname { if setup_tool $toolname !2 >/dev/null && shell {eval $condition $toolname} { if test -n $preamble { printf "%s\n" $preamble setglobal preamble = '' } setglobal shown_any = 'yes' printf "%s%s\n" $per_line_prefix $toolname } } if test -n $extra_content { if test -n $preamble { # Note: no '\n' here since we don't want a # blank line if there is no initial content. printf "%s" $preamble setglobal preamble = '' } setglobal shown_any = 'yes' printf "\n%s\n" $extra_content } if test -n $preamble && test -n $not_found_msg { printf "%s\n" $not_found_msg } test -n $shown_any } } proc diff_mode { test $TOOL_MODE = diff } proc merge_mode { test $TOOL_MODE = merge } proc translate_merge_tool_path { echo $1 } proc check_unchanged { if test $MERGED -nt $BACKUP { return 0 } else { while true { echo "$MERGED seems unchanged." printf "Was the merge successful [y/n]? " read answer || return 1 match $answer { with y*|Y* return 0 with n*|N* return 1 } } } } proc valid_tool { setup_tool $1 && return 0 setglobal cmd = $[get_merge_tool_cmd $1] test -n $cmd } proc setup_user_tool { setglobal merge_tool_cmd = $[get_merge_tool_cmd $tool] test -n $merge_tool_cmd || return 1 proc diff_cmd { shell { eval $merge_tool_cmd } } proc merge_cmd { setglobal trust_exit_code = $[git config --bool \ "mergetool.$1.trustExitCode" || echo false] if test $trust_exit_code = "false" { touch $BACKUP shell { eval $merge_tool_cmd } check_unchanged } else { shell { eval $merge_tool_cmd } } } } proc setup_tool { setglobal tool = $1 # Fallback definitions, to be overridden by tools. proc can_merge { return 0 } proc can_diff { return 0 } proc diff_cmd { return 1 } proc merge_cmd { return 1 } proc translate_merge_tool_path { echo $1 } if ! test -f "$MERGE_TOOLS_DIR/$tool" { setup_user_tool return $? } # Load the redefined functions source "$MERGE_TOOLS_DIR/$tool" # 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 merge_mode && ! can_merge { echo "error: '$tool' can not be used to resolve merges" > !2 return 1 } elif diff_mode && ! can_diff { echo "error: '$tool' can only be used to resolve merges" > !2 return 1 } return 0 } proc get_merge_tool_cmd { setglobal merge_tool = $1 if diff_mode { git config "difftool.$merge_tool.cmd" || git config "mergetool.$merge_tool.cmd" } else { git config "mergetool.$merge_tool.cmd" } } # Entry point for running tools proc 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. setglobal GIT_PREFIX = $(GIT_PREFIX:-.) export GIT_PREFIX setglobal merge_tool_path = $[get_merge_tool_path $1] || exit setglobal base_present = $2 # Bring tool-specific functions into scope setup_tool $1 || return 1 if merge_mode { run_merge_cmd $1 } else { run_diff_cmd $1 } } # Run a either a configured or built-in diff tool proc run_diff_cmd { diff_cmd $1 } # Run a either a configured or built-in merge tool proc run_merge_cmd { merge_cmd $1 } proc list_merge_tool_candidates { if merge_mode { setglobal tools = '"tortoisemerge'" } else { setglobal tools = '"kompare'" } if test -n $DISPLAY { if test -n $GNOME_DESKTOP_SESSION_ID { setglobal tools = ""meld opendiff kdiff3 tkdiff xxdiff $tools"" } else { setglobal tools = ""opendiff kdiff3 tkdiff xxdiff meld $tools"" } setglobal tools = ""$tools gvimdiff diffuse diffmerge ecmerge"" setglobal tools = ""$tools p4merge araxis bc codecompare"" } match $(VISUAL:-$EDITOR) { with *vim* setglobal tools = ""$tools vimdiff emerge"" with * setglobal tools = ""$tools emerge vimdiff"" } } proc show_tool_help { setglobal tool_opt = ""'git $(TOOL_MODE)tool --tool='"" setglobal tab = '' '' setglobal LF = '' '' setglobal any_shown = 'no' setglobal cmd_name = "$(TOOL_MODE)tool" setglobal config_tools = $[do { diff_mode && list_config_tools difftool "$tab$tab" list_config_tools mergetool "$tab$tab" } | sort] setglobal extra_content = '' if test -n $config_tools { setglobal extra_content = ""$(tab)user-defined:$(LF)$config_tools"" } 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=' found." \ $extra_content && setglobal any_shown = 'yes' show_tool_names 'mode_ok && ! is_available' "$tab$tab" \ "$(LF)The following tools are valid, but not currently available:" && setglobal any_shown = 'yes' if test $any_shown = yes { 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." } exit 0 } proc guess_merge_tool { list_merge_tool_candidates cat > !2 << """ 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 """ # Loop over each candidate and stop when a valid merge tool is found. setglobal IFS = '' '' for tool in [$tools] { is_available $tool && echo $tool && return 0 } echo >&2 "No known $(TOOL_MODE) tool is available.> !2 "No known ${TOOL_MODE} tool is available." return 1 } proc 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 { setglobal merge_tool = $[git config diff.tool || git config merge.tool] } else { setglobal merge_tool = $[git config merge.tool] } if test -n $merge_tool && ! valid_tool $merge_tool { echo >&2 "git config option $TOOL_MODE.tool set to unknown tool: $merge_tool> !2 "git config option $TOOL_MODE.tool set to unknown tool: $merge_tool" echo >&2 "Resetting to default...> !2 "Resetting to default..." return 1 } echo $merge_tool } proc get_merge_tool_path { # A merge tool has been set, so verify that it's valid. setglobal merge_tool = $1 if ! valid_tool $merge_tool { echo >&2 "Unknown merge tool $merge_tool> !2 "Unknown merge tool $merge_tool" exit 1 } if diff_mode { setglobal merge_tool_path = $[git config difftool."$merge_tool".path || git config mergetool."$merge_tool".path] } else { setglobal merge_tool_path = $[git config mergetool."$merge_tool".path] } if test -z $merge_tool_path { setglobal merge_tool_path = $[translate_merge_tool_path $merge_tool] } if test -z $[get_merge_tool_cmd $merge_tool] && ! type $merge_tool_path >/dev/null !2 > !1 { echo >&2 "The $TOOL_MODE tool $merge_tool is not available as"\ "'$merge_tool_path'> !2 "The $TOOL_MODE tool $merge_tool is not available as"\ "'$merge_tool_path'" exit 1 } echo $merge_tool_path } proc get_merge_tool { # Check if a merge tool has been configured setglobal merge_tool = $[get_configured_merge_tool] # Try to guess an appropriate merge tool if no tool has been set. if test -z $merge_tool { setglobal merge_tool = $[guess_merge_tool] || exit } echo $merge_tool } proc mergetool_find_win32_cmd { setglobal executable = $1 setglobal sub_directory = $2 # Use $executable if it exists in $PATH if type -p $executable >/dev/null !2 > !1 { printf '%s' $executable return } # Look for executable in the typical locations for directory in [$[env | grep -Ei '^PROGRAM(FILES(\(X86\))?|W6432)=' | cut -d '=' -f 2- | sort -u]] { if test -n $directory && test -x "$directory/$sub_directory/$executable" { printf '%s' "$directory/$sub_directory/$executable" return } } printf '%s' $executable }