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