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