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 status=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*) status=0; break ;; 104 n*|N*) status=1; break ;; 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 status=$? 123 return $status 124 } 125 126 merge_cmd () { 127 trust_exit_code=$(git config --bool \ 128 "mergetool.$1.trustExitCode" || echo false) 129 if test "$trust_exit_code" = "false" 130 then 131 touch "$BACKUP" 132 ( eval $merge_tool_cmd ) 133 status=$? 134 check_unchanged 135 else 136 ( eval $merge_tool_cmd ) 137 status=$? 138 fi 139 return $status 140 } 141} 142 143setup_tool () { 144 tool="$1" 145 146 # Fallback definitions, to be overridden by tools. 147 can_merge () { 148 return 0 149 } 150 151 can_diff () { 152 return 0 153 } 154 155 diff_cmd () { 156 status=1 157 return $status 158 } 159 160 merge_cmd () { 161 status=1 162 return $status 163 } 164 165 translate_merge_tool_path () { 166 echo "$1" 167 } 168 169 if ! test -f "$MERGE_TOOLS_DIR/$tool" 170 then 171 setup_user_tool 172 return $? 173 fi 174 175 # Load the redefined functions 176 . "$MERGE_TOOLS_DIR/$tool" 177 # Now let the user override the default command for the tool. If 178 # they have not done so then this will return 1 which we ignore. 179 setup_user_tool 180 181 if merge_mode && ! can_merge 182 then 183 echo "error: '$tool' can not be used to resolve merges" >&2 184 return 1 185 elif diff_mode && ! can_diff 186 then 187 echo "error: '$tool' can only be used to resolve merges" >&2 188 return 1 189 fi 190 return 0 191} 192 193get_merge_tool_cmd () { 194 merge_tool="$1" 195 if diff_mode 196 then 197 git config "difftool.$merge_tool.cmd" || 198 git config "mergetool.$merge_tool.cmd" 199 else 200 git config "mergetool.$merge_tool.cmd" 201 fi 202} 203 204# Entry point for running tools 205run_merge_tool () { 206 # If GIT_PREFIX is empty then we cannot use it in tools 207 # that expect to be able to chdir() to its value. 208 GIT_PREFIX=${GIT_PREFIX:-.} 209 export GIT_PREFIX 210 211 merge_tool_path=$(get_merge_tool_path "$1") || exit 212 base_present="$2" 213 status=0 214 215 # Bring tool-specific functions into scope 216 setup_tool "$1" || return 1 217 218 if merge_mode 219 then 220 run_merge_cmd "$1" 221 else 222 run_diff_cmd "$1" 223 fi 224 return $status 225} 226 227# Run a either a configured or built-in diff tool 228run_diff_cmd () { 229 diff_cmd "$1" 230} 231 232# Run a either a configured or built-in merge tool 233run_merge_cmd () { 234 merge_cmd "$1" 235} 236 237list_merge_tool_candidates () { 238 if merge_mode 239 then 240 tools="tortoisemerge" 241 else 242 tools="kompare" 243 fi 244 if test -n "$DISPLAY" 245 then 246 if test -n "$GNOME_DESKTOP_SESSION_ID" 247 then 248 tools="meld opendiff kdiff3 tkdiff xxdiff $tools" 249 else 250 tools="opendiff kdiff3 tkdiff xxdiff meld $tools" 251 fi 252 tools="$tools gvimdiff diffuse diffmerge ecmerge" 253 tools="$tools p4merge araxis bc3 codecompare" 254 fi 255 case "${VISUAL:-$EDITOR}" in 256 *vim*) 257 tools="$tools vimdiff emerge" 258 ;; 259 *) 260 tools="$tools emerge vimdiff" 261 ;; 262 esac 263} 264 265show_tool_help () { 266 tool_opt="'git ${TOOL_MODE}tool --tool=<tool>'" 267 268 tab=' ' 269 LF=' 270' 271 any_shown=no 272 273 cmd_name=${TOOL_MODE}tool 274 config_tools=$({ 275 diff_mode && list_config_tools difftool "$tab$tab" 276 list_config_tools mergetool "$tab$tab" 277 } | sort) 278 extra_content= 279 if test -n "$config_tools" 280 then 281 extra_content="${tab}user-defined:${LF}$config_tools" 282 fi 283 284 show_tool_names 'mode_ok && is_available' "$tab$tab" \ 285 "$tool_opt may be set to one of the following:" \ 286 "No suitable tool for 'git $cmd_name --tool=<tool>' found." \ 287 "$extra_content" && 288 any_shown=yes 289 290 show_tool_names 'mode_ok && ! is_available' "$tab$tab" \ 291 "${LF}The following tools are valid, but not currently available:" && 292 any_shown=yes 293 294 if test "$any_shown" = yes 295 then 296 echo 297 echo "Some of the tools listed above only work in a windowed" 298 echo "environment. If run in a terminal-only session, they will fail." 299 fi 300 exit 0 301} 302 303guess_merge_tool () { 304 list_merge_tool_candidates 305 cat >&2 <<-EOF 306 307 This message is displayed because '$TOOL_MODE.tool' is not configured. 308 See 'git ${TOOL_MODE}tool --tool-help' or 'git help config' for more details. 309 'git ${TOOL_MODE}tool' will now attempt to use one of the following tools: 310 $tools 311 EOF 312 313 # Loop over each candidate and stop when a valid merge tool is found. 314 for tool in $tools 315 do 316 is_available "$tool" && echo "$tool" && return 0 317 done 318 319 echo >&2 "No known ${TOOL_MODE} tool is available." 320 return 1 321} 322 323get_configured_merge_tool () { 324 # Diff mode first tries diff.tool and falls back to merge.tool. 325 # Merge mode only checks merge.tool 326 if diff_mode 327 then 328 merge_tool=$(git config diff.tool || git config merge.tool) 329 else 330 merge_tool=$(git config merge.tool) 331 fi 332 if test -n "$merge_tool" && ! valid_tool "$merge_tool" 333 then 334 echo >&2 "git config option $TOOL_MODE.tool set to unknown tool: $merge_tool" 335 echo >&2 "Resetting to default..." 336 return 1 337 fi 338 echo "$merge_tool" 339} 340 341get_merge_tool_path () { 342 # A merge tool has been set, so verify that it's valid. 343 merge_tool="$1" 344 if ! valid_tool "$merge_tool" 345 then 346 echo >&2 "Unknown merge tool $merge_tool" 347 exit 1 348 fi 349 if diff_mode 350 then 351 merge_tool_path=$(git config difftool."$merge_tool".path || 352 git config mergetool."$merge_tool".path) 353 else 354 merge_tool_path=$(git config mergetool."$merge_tool".path) 355 fi 356 if test -z "$merge_tool_path" 357 then 358 merge_tool_path=$(translate_merge_tool_path "$merge_tool") 359 fi 360 if test -z "$(get_merge_tool_cmd "$merge_tool")" && 361 ! type "$merge_tool_path" >/dev/null 2>&1 362 then 363 echo >&2 "The $TOOL_MODE tool $merge_tool is not available as"\ 364 "'$merge_tool_path'" 365 exit 1 366 fi 367 echo "$merge_tool_path" 368} 369 370get_merge_tool () { 371 # Check if a merge tool has been configured 372 merge_tool=$(get_configured_merge_tool) 373 # Try to guess an appropriate merge tool if no tool has been set. 374 if test -z "$merge_tool" 375 then 376 merge_tool=$(guess_merge_tool) || exit 377 fi 378 echo "$merge_tool" 379}