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 status=$? 225 return $status 226} 227 228# Run a either a configured or built-in diff tool 229run_diff_cmd () { 230 diff_cmd "$1" 231} 232 233# Run a either a configured or built-in merge tool 234run_merge_cmd () { 235 merge_cmd "$1" 236} 237 238list_merge_tool_candidates () { 239 if merge_mode 240 then 241 tools="tortoisemerge" 242 else 243 tools="kompare" 244 fi 245 if test -n "$DISPLAY" 246 then 247 if test -n "$GNOME_DESKTOP_SESSION_ID" 248 then 249 tools="meld opendiff kdiff3 tkdiff xxdiff $tools" 250 else 251 tools="opendiff kdiff3 tkdiff xxdiff meld $tools" 252 fi 253 tools="$tools gvimdiff diffuse diffmerge ecmerge" 254 tools="$tools p4merge araxis bc codecompare" 255 fi 256 case "${VISUAL:-$EDITOR}" in 257 *vim*) 258 tools="$tools vimdiff emerge" 259 ;; 260 *) 261 tools="$tools emerge vimdiff" 262 ;; 263 esac 264} 265 266show_tool_help () { 267 tool_opt="'git ${TOOL_MODE}tool --tool=<tool>'" 268 269 tab=' ' 270 LF=' 271' 272 any_shown=no 273 274 cmd_name=${TOOL_MODE}tool 275 config_tools=$({ 276 diff_mode && list_config_tools difftool "$tab$tab" 277 list_config_tools mergetool "$tab$tab" 278 } | sort) 279 extra_content= 280 if test -n "$config_tools" 281 then 282 extra_content="${tab}user-defined:${LF}$config_tools" 283 fi 284 285 show_tool_names 'mode_ok && is_available' "$tab$tab" \ 286 "$tool_opt may be set to one of the following:" \ 287 "No suitable tool for 'git $cmd_name --tool=<tool>' found." \ 288 "$extra_content" && 289 any_shown=yes 290 291 show_tool_names 'mode_ok && ! is_available' "$tab$tab" \ 292 "${LF}The following tools are valid, but not currently available:" && 293 any_shown=yes 294 295 if test "$any_shown" = yes 296 then 297 echo 298 echo "Some of the tools listed above only work in a windowed" 299 echo "environment. If run in a terminal-only session, they will fail." 300 fi 301 exit 0 302} 303 304guess_merge_tool () { 305 list_merge_tool_candidates 306 cat >&2 <<-EOF 307 308 This message is displayed because '$TOOL_MODE.tool' is not configured. 309 See 'git ${TOOL_MODE}tool --tool-help' or 'git help config' for more details. 310 'git ${TOOL_MODE}tool' will now attempt to use one of the following tools: 311 $tools 312 EOF 313 314 # Loop over each candidate and stop when a valid merge tool is found. 315 for tool in $tools 316 do 317 is_available "$tool" && echo "$tool" && return 0 318 done 319 320 echo >&2 "No known ${TOOL_MODE} tool is available." 321 return 1 322} 323 324get_configured_merge_tool () { 325 # Diff mode first tries diff.tool and falls back to merge.tool. 326 # Merge mode only checks merge.tool 327 if diff_mode 328 then 329 merge_tool=$(git config diff.tool || git config merge.tool) 330 else 331 merge_tool=$(git config merge.tool) 332 fi 333 if test -n "$merge_tool" && ! valid_tool "$merge_tool" 334 then 335 echo >&2 "git config option $TOOL_MODE.tool set to unknown tool: $merge_tool" 336 echo >&2 "Resetting to default..." 337 return 1 338 fi 339 echo "$merge_tool" 340} 341 342get_merge_tool_path () { 343 # A merge tool has been set, so verify that it's valid. 344 merge_tool="$1" 345 if ! valid_tool "$merge_tool" 346 then 347 echo >&2 "Unknown merge tool $merge_tool" 348 exit 1 349 fi 350 if diff_mode 351 then 352 merge_tool_path=$(git config difftool."$merge_tool".path || 353 git config mergetool."$merge_tool".path) 354 else 355 merge_tool_path=$(git config mergetool."$merge_tool".path) 356 fi 357 if test -z "$merge_tool_path" 358 then 359 merge_tool_path=$(translate_merge_tool_path "$merge_tool") 360 fi 361 if test -z "$(get_merge_tool_cmd "$merge_tool")" && 362 ! type "$merge_tool_path" >/dev/null 2>&1 363 then 364 echo >&2 "The $TOOL_MODE tool $merge_tool is not available as"\ 365 "'$merge_tool_path'" 366 exit 1 367 fi 368 echo "$merge_tool_path" 369} 370 371get_merge_tool () { 372 # Check if a merge tool has been configured 373 merge_tool=$(get_configured_merge_tool) 374 # Try to guess an appropriate merge tool if no tool has been set. 375 if test -z "$merge_tool" 376 then 377 merge_tool=$(guess_merge_tool) || exit 378 fi 379 echo "$merge_tool" 380}