1# git-mergetool--lib is a shell library for common merge tool functions 2 3: ${MERGE_TOOLS_DIR=$(git --exec-path)/mergetools} 4 5mode_ok () { 6 if diff_mode 7 then 8 can_diff 9 elif merge_mode 10 then 11 can_merge 12 else 13 false 14 fi 15} 16 17is_available () { 18 merge_tool_path=$(translate_merge_tool_path "$1") && 19 type "$merge_tool_path" >/dev/null 2>&1 20} 21 22list_config_tools () { 23 section=$1 24 line_prefix=${2:-} 25 26 git config --get-regexp $section'\..*\.cmd' | 27 while read -r key value 28 do 29 toolname=${key#$section.} 30 toolname=${toolname%.cmd} 31 32 printf "%s%s\n" "$line_prefix" "$toolname" 33 done 34} 35 36show_tool_names () { 37 condition=${1:-true} per_line_prefix=${2:-} preamble=${3:-} 38 not_found_msg=${4:-} 39 extra_content=${5:-} 40 41 shown_any= 42 ( cd "$MERGE_TOOLS_DIR" && ls ) | { 43 while read toolname 44 do 45 if setup_tool "$toolname" 2>/dev/null && 46 (eval "$condition" "$toolname") 47 then 48 if test -n "$preamble" 49 then 50 printf "%s\n" "$preamble" 51 preamble= 52 fi 53 shown_any=yes 54 printf "%s%s\n" "$per_line_prefix" "$toolname" 55 fi 56 done 57 58 if test -n "$extra_content" 59 then 60 if test -n "$preamble" 61 then 62 # Note: no '\n' here since we don't want a 63 # blank line if there is no initial content. 64 printf "%s" "$preamble" 65 preamble= 66 fi 67 shown_any=yes 68 printf "\n%s\n" "$extra_content" 69 fi 70 71 if test -n "$preamble" && test -n "$not_found_msg" 72 then 73 printf "%s\n" "$not_found_msg" 74 fi 75 76 test -n "$shown_any" 77 } 78} 79 80diff_mode() { 81 test "$TOOL_MODE" = diff 82} 83 84merge_mode() { 85 test "$TOOL_MODE" = merge 86} 87 88translate_merge_tool_path () { 89 echo "$1" 90} 91 92check_unchanged () { 93 if test "$MERGED" -nt "$BACKUP" 94 then 95 return 0 96 else 97 while true 98 do 99 echo "$MERGED seems unchanged." 100 printf "Was the merge successful? [y/n] " 101 read answer || return 1 102 case "$answer" in 103 y*|Y*) return 0 ;; 104 n*|N*) return 1 ;; 105 esac 106 done 107 fi 108} 109 110valid_tool () { 111 setup_tool "$1" && return 0 112 cmd=$(get_merge_tool_cmd "$1") 113 test -n "$cmd" 114} 115 116setup_user_tool () { 117 merge_tool_cmd=$(get_merge_tool_cmd "$tool") 118 test -n "$merge_tool_cmd" || return 1 119 120 diff_cmd () { 121 ( eval $merge_tool_cmd ) 122 } 123 124 merge_cmd () { 125 trust_exit_code=$(git config --bool \ 126 "mergetool.$1.trustExitCode" || echo false) 127 if test "$trust_exit_code" = "false" 128 then 129 touch "$BACKUP" 130 ( eval $merge_tool_cmd ) 131 check_unchanged 132 else 133 ( eval $merge_tool_cmd ) 134 fi 135 } 136} 137 138setup_tool () { 139 tool="$1" 140 141 # Fallback definitions, to be overridden by tools. 142 can_merge () { 143 return 0 144 } 145 146 can_diff () { 147 return 0 148 } 149 150 diff_cmd () { 151 return 1 152 } 153 154 merge_cmd () { 155 return 1 156 } 157 158 translate_merge_tool_path () { 159 echo "$1" 160 } 161 162 if ! test -f "$MERGE_TOOLS_DIR/$tool" 163 then 164 setup_user_tool 165 return $? 166 fi 167 168 # Load the redefined functions 169 . "$MERGE_TOOLS_DIR/$tool" 170 # Now let the user override the default command for the tool. If 171 # they have not done so then this will return 1 which we ignore. 172 setup_user_tool 173 174 if merge_mode && ! can_merge 175 then 176 echo "error: '$tool' can not be used to resolve merges" >&2 177 return 1 178 elif diff_mode && ! can_diff 179 then 180 echo "error: '$tool' can only be used to resolve merges" >&2 181 return 1 182 fi 183 return 0 184} 185 186get_merge_tool_cmd () { 187 merge_tool="$1" 188 if diff_mode 189 then 190 git config "difftool.$merge_tool.cmd" || 191 git config "mergetool.$merge_tool.cmd" 192 else 193 git config "mergetool.$merge_tool.cmd" 194 fi 195} 196 197# Entry point for running tools 198run_merge_tool () { 199 # If GIT_PREFIX is empty then we cannot use it in tools 200 # that expect to be able to chdir() to its value. 201 GIT_PREFIX=${GIT_PREFIX:-.} 202 export GIT_PREFIX 203 204 merge_tool_path=$(get_merge_tool_path "$1") || exit 205 base_present="$2" 206 207 # Bring tool-specific functions into scope 208 setup_tool "$1" || return 1 209 210 if merge_mode 211 then 212 run_merge_cmd "$1" 213 else 214 run_diff_cmd "$1" 215 fi 216} 217 218# Run a either a configured or built-in diff tool 219run_diff_cmd () { 220 diff_cmd "$1" 221} 222 223# Run a either a configured or built-in merge tool 224run_merge_cmd () { 225 merge_cmd "$1" 226} 227 228list_merge_tool_candidates () { 229 if merge_mode 230 then 231 tools="tortoisemerge" 232 else 233 tools="kompare" 234 fi 235 if test -n "$DISPLAY" 236 then 237 if test -n "$GNOME_DESKTOP_SESSION_ID" 238 then 239 tools="meld opendiff kdiff3 tkdiff xxdiff $tools" 240 else 241 tools="opendiff kdiff3 tkdiff xxdiff meld $tools" 242 fi 243 tools="$tools gvimdiff diffuse diffmerge ecmerge" 244 tools="$tools p4merge araxis bc codecompare" 245 fi 246 case "${VISUAL:-$EDITOR}" in 247 *vim*) 248 tools="$tools vimdiff emerge" 249 ;; 250 *) 251 tools="$tools emerge vimdiff" 252 ;; 253 esac 254} 255 256show_tool_help () { 257 tool_opt="'git ${TOOL_MODE}tool --tool=<tool>'" 258 259 tab=' ' 260 LF=' 261' 262 any_shown=no 263 264 cmd_name=${TOOL_MODE}tool 265 config_tools=$({ 266 diff_mode && list_config_tools difftool "$tab$tab" 267 list_config_tools mergetool "$tab$tab" 268 } | sort) 269 extra_content= 270 if test -n "$config_tools" 271 then 272 extra_content="${tab}user-defined:${LF}$config_tools" 273 fi 274 275 show_tool_names 'mode_ok && is_available' "$tab$tab" \ 276 "$tool_opt may be set to one of the following:" \ 277 "No suitable tool for 'git $cmd_name --tool=<tool>' found." \ 278 "$extra_content" && 279 any_shown=yes 280 281 show_tool_names 'mode_ok && ! is_available' "$tab$tab" \ 282 "${LF}The following tools are valid, but not currently available:" && 283 any_shown=yes 284 285 if test "$any_shown" = yes 286 then 287 echo 288 echo "Some of the tools listed above only work in a windowed" 289 echo "environment. If run in a terminal-only session, they will fail." 290 fi 291 exit 0 292} 293 294guess_merge_tool () { 295 list_merge_tool_candidates 296 cat >&2 <<-EOF 297 298 This message is displayed because '$TOOL_MODE.tool' is not configured. 299 See 'git ${TOOL_MODE}tool --tool-help' or 'git help config' for more details. 300 'git ${TOOL_MODE}tool' will now attempt to use one of the following tools: 301 $tools 302 EOF 303 304 # Loop over each candidate and stop when a valid merge tool is found. 305 for tool in $tools 306 do 307 is_available "$tool" && echo "$tool" && return 0 308 done 309 310 echo >&2 "No known ${TOOL_MODE} tool is available." 311 return 1 312} 313 314get_configured_merge_tool () { 315 # Diff mode first tries diff.tool and falls back to merge.tool. 316 # Merge mode only checks merge.tool 317 if diff_mode 318 then 319 merge_tool=$(git config diff.tool || git config merge.tool) 320 else 321 merge_tool=$(git config merge.tool) 322 fi 323 if test -n "$merge_tool" && ! valid_tool "$merge_tool" 324 then 325 echo >&2 "git config option $TOOL_MODE.tool set to unknown tool: $merge_tool" 326 echo >&2 "Resetting to default..." 327 return 1 328 fi 329 echo "$merge_tool" 330} 331 332get_merge_tool_path () { 333 # A merge tool has been set, so verify that it's valid. 334 merge_tool="$1" 335 if ! valid_tool "$merge_tool" 336 then 337 echo >&2 "Unknown merge tool $merge_tool" 338 exit 1 339 fi 340 if diff_mode 341 then 342 merge_tool_path=$(git config difftool."$merge_tool".path || 343 git config mergetool."$merge_tool".path) 344 else 345 merge_tool_path=$(git config mergetool."$merge_tool".path) 346 fi 347 if test -z "$merge_tool_path" 348 then 349 merge_tool_path=$(translate_merge_tool_path "$merge_tool") 350 fi 351 if test -z "$(get_merge_tool_cmd "$merge_tool")" && 352 ! type "$merge_tool_path" >/dev/null 2>&1 353 then 354 echo >&2 "The $TOOL_MODE tool $merge_tool is not available as"\ 355 "'$merge_tool_path'" 356 exit 1 357 fi 358 echo "$merge_tool_path" 359} 360 361get_merge_tool () { 362 # Check if a merge tool has been configured 363 merge_tool=$(get_configured_merge_tool) 364 # Try to guess an appropriate merge tool if no tool has been set. 365 if test -z "$merge_tool" 366 then 367 merge_tool=$(guess_merge_tool) || exit 368 fi 369 echo "$merge_tool" 370}