git-mergetool--lib.shon commit Merge branch 'maint' (50b68ae)
   1#!/bin/sh
   2# git-mergetool--lib is a library for common merge tool functions
   3diff_mode() {
   4        test "$TOOL_MODE" = diff
   5}
   6
   7merge_mode() {
   8        test "$TOOL_MODE" = merge
   9}
  10
  11translate_merge_tool_path () {
  12        case "$1" in
  13        araxis)
  14                echo compare
  15                ;;
  16        bc3)
  17                echo bcompare
  18                ;;
  19        emerge)
  20                echo emacs
  21                ;;
  22        gvimdiff|gvimdiff2)
  23                echo gvim
  24                ;;
  25        vimdiff|vimdiff2)
  26                echo vim
  27                ;;
  28        *)
  29                echo "$1"
  30                ;;
  31        esac
  32}
  33
  34check_unchanged () {
  35        if test "$MERGED" -nt "$BACKUP"; then
  36                status=0
  37        else
  38                while true; do
  39                        echo "$MERGED seems unchanged."
  40                        printf "Was the merge successful? [y/n] "
  41                        read answer
  42                        case "$answer" in
  43                        y*|Y*) status=0; break ;;
  44                        n*|N*) status=1; break ;;
  45                        esac
  46                done
  47        fi
  48}
  49
  50valid_tool () {
  51        case "$1" in
  52        araxis | bc3 | diffuse | ecmerge | emerge | gvimdiff | gvimdiff2 | \
  53        kdiff3 | meld | opendiff | p4merge | tkdiff | vimdiff | vimdiff2 | xxdiff)
  54                ;; # happy
  55        kompare)
  56                if ! diff_mode; then
  57                        return 1
  58                fi
  59                ;;
  60        tortoisemerge)
  61                if ! merge_mode; then
  62                        return 1
  63                fi
  64                ;;
  65        *)
  66                if test -z "$(get_merge_tool_cmd "$1")"; then
  67                        return 1
  68                fi
  69                ;;
  70        esac
  71}
  72
  73get_merge_tool_cmd () {
  74        # Prints the custom command for a merge tool
  75        if test -n "$1"; then
  76                merge_tool="$1"
  77        else
  78                merge_tool="$(get_merge_tool)"
  79        fi
  80        if diff_mode; then
  81                echo "$(git config difftool.$merge_tool.cmd ||
  82                        git config mergetool.$merge_tool.cmd)"
  83        else
  84                echo "$(git config mergetool.$merge_tool.cmd)"
  85        fi
  86}
  87
  88run_merge_tool () {
  89        # If GIT_PREFIX is empty then we cannot use it in tools
  90        # that expect to be able to chdir() to its value.
  91        GIT_PREFIX=${GIT_PREFIX:-.}
  92        export GIT_PREFIX
  93
  94        merge_tool_path="$(get_merge_tool_path "$1")" || exit
  95        base_present="$2"
  96        status=0
  97
  98        case "$1" in
  99        araxis)
 100                if merge_mode; then
 101                        touch "$BACKUP"
 102                        if $base_present; then
 103                                "$merge_tool_path" -wait -merge -3 -a1 \
 104                                        "$BASE" "$LOCAL" "$REMOTE" "$MERGED" \
 105                                        >/dev/null 2>&1
 106                        else
 107                                "$merge_tool_path" -wait -2 \
 108                                        "$LOCAL" "$REMOTE" "$MERGED" \
 109                                        >/dev/null 2>&1
 110                        fi
 111                        check_unchanged
 112                else
 113                        "$merge_tool_path" -wait -2 "$LOCAL" "$REMOTE" \
 114                                >/dev/null 2>&1
 115                fi
 116                ;;
 117        bc3)
 118                if merge_mode; then
 119                        touch "$BACKUP"
 120                        if $base_present; then
 121                                "$merge_tool_path" "$LOCAL" "$REMOTE" "$BASE" \
 122                                        -mergeoutput="$MERGED"
 123                        else
 124                                "$merge_tool_path" "$LOCAL" "$REMOTE" \
 125                                        -mergeoutput="$MERGED"
 126                        fi
 127                        check_unchanged
 128                else
 129                        "$merge_tool_path" "$LOCAL" "$REMOTE"
 130                fi
 131                ;;
 132        diffuse)
 133                if merge_mode; then
 134                        touch "$BACKUP"
 135                        if $base_present; then
 136                                "$merge_tool_path" \
 137                                        "$LOCAL" "$MERGED" "$REMOTE" \
 138                                        "$BASE" | cat
 139                        else
 140                                "$merge_tool_path" \
 141                                        "$LOCAL" "$MERGED" "$REMOTE" | cat
 142                        fi
 143                        check_unchanged
 144                else
 145                        "$merge_tool_path" "$LOCAL" "$REMOTE" | cat
 146                fi
 147                ;;
 148        ecmerge)
 149                if merge_mode; then
 150                        touch "$BACKUP"
 151                        if $base_present; then
 152                                "$merge_tool_path" "$BASE" "$LOCAL" "$REMOTE" \
 153                                        --default --mode=merge3 --to="$MERGED"
 154                        else
 155                                "$merge_tool_path" "$LOCAL" "$REMOTE" \
 156                                        --default --mode=merge2 --to="$MERGED"
 157                        fi
 158                        check_unchanged
 159                else
 160                        "$merge_tool_path" --default --mode=diff2 \
 161                                "$LOCAL" "$REMOTE"
 162                fi
 163                ;;
 164        emerge)
 165                if merge_mode; then
 166                        if $base_present; then
 167                                "$merge_tool_path" \
 168                                        -f emerge-files-with-ancestor-command \
 169                                        "$LOCAL" "$REMOTE" "$BASE" \
 170                                        "$(basename "$MERGED")"
 171                        else
 172                                "$merge_tool_path" \
 173                                        -f emerge-files-command \
 174                                        "$LOCAL" "$REMOTE" \
 175                                        "$(basename "$MERGED")"
 176                        fi
 177                        status=$?
 178                else
 179                        "$merge_tool_path" -f emerge-files-command \
 180                                "$LOCAL" "$REMOTE"
 181                fi
 182                ;;
 183        gvimdiff|vimdiff)
 184                if merge_mode; then
 185                        touch "$BACKUP"
 186                        if $base_present; then
 187                                "$merge_tool_path" -f -d -c "wincmd J" \
 188                                        "$MERGED" "$LOCAL" "$BASE" "$REMOTE"
 189                        else
 190                                "$merge_tool_path" -f -d -c "wincmd l" \
 191                                        "$LOCAL" "$MERGED" "$REMOTE"
 192                        fi
 193                        check_unchanged
 194                else
 195                        "$merge_tool_path" -R -f -d -c "wincmd l" \
 196                                -c 'cd $GIT_PREFIX' \
 197                                "$LOCAL" "$REMOTE"
 198                fi
 199                ;;
 200        gvimdiff2|vimdiff2)
 201                if merge_mode; then
 202                        touch "$BACKUP"
 203                        "$merge_tool_path" -f -d -c "wincmd l" \
 204                                "$LOCAL" "$MERGED" "$REMOTE"
 205                        check_unchanged
 206                else
 207                        "$merge_tool_path" -R -f -d -c "wincmd l" \
 208                                -c 'cd $GIT_PREFIX' \
 209                                "$LOCAL" "$REMOTE"
 210                fi
 211                ;;
 212        kdiff3)
 213                if merge_mode; then
 214                        if $base_present; then
 215                                ("$merge_tool_path" --auto \
 216                                        --L1 "$MERGED (Base)" \
 217                                        --L2 "$MERGED (Local)" \
 218                                        --L3 "$MERGED (Remote)" \
 219                                        -o "$MERGED" \
 220                                        "$BASE" "$LOCAL" "$REMOTE" \
 221                                > /dev/null 2>&1)
 222                        else
 223                                ("$merge_tool_path" --auto \
 224                                        --L1 "$MERGED (Local)" \
 225                                        --L2 "$MERGED (Remote)" \
 226                                        -o "$MERGED" \
 227                                        "$LOCAL" "$REMOTE" \
 228                                > /dev/null 2>&1)
 229                        fi
 230                        status=$?
 231                else
 232                        ("$merge_tool_path" --auto \
 233                                --L1 "$MERGED (A)" \
 234                                --L2 "$MERGED (B)" "$LOCAL" "$REMOTE" \
 235                        > /dev/null 2>&1)
 236                fi
 237                ;;
 238        kompare)
 239                "$merge_tool_path" "$LOCAL" "$REMOTE"
 240                ;;
 241        meld)
 242                if merge_mode; then
 243                        touch "$BACKUP"
 244                        "$merge_tool_path" "$LOCAL" "$MERGED" "$REMOTE"
 245                        check_unchanged
 246                else
 247                        "$merge_tool_path" "$LOCAL" "$REMOTE"
 248                fi
 249                ;;
 250        opendiff)
 251                if merge_mode; then
 252                        touch "$BACKUP"
 253                        if $base_present; then
 254                                "$merge_tool_path" "$LOCAL" "$REMOTE" \
 255                                        -ancestor "$BASE" \
 256                                        -merge "$MERGED" | cat
 257                        else
 258                                "$merge_tool_path" "$LOCAL" "$REMOTE" \
 259                                        -merge "$MERGED" | cat
 260                        fi
 261                        check_unchanged
 262                else
 263                        "$merge_tool_path" "$LOCAL" "$REMOTE" | cat
 264                fi
 265                ;;
 266        p4merge)
 267                if merge_mode; then
 268                        touch "$BACKUP"
 269                        $base_present || >"$BASE"
 270                        "$merge_tool_path" "$BASE" "$LOCAL" "$REMOTE" "$MERGED"
 271                        check_unchanged
 272                else
 273                        "$merge_tool_path" "$LOCAL" "$REMOTE"
 274                fi
 275                ;;
 276        tkdiff)
 277                if merge_mode; then
 278                        if $base_present; then
 279                                "$merge_tool_path" -a "$BASE" \
 280                                        -o "$MERGED" "$LOCAL" "$REMOTE"
 281                        else
 282                                "$merge_tool_path" \
 283                                        -o "$MERGED" "$LOCAL" "$REMOTE"
 284                        fi
 285                        status=$?
 286                else
 287                        "$merge_tool_path" "$LOCAL" "$REMOTE"
 288                fi
 289                ;;
 290        tortoisemerge)
 291                if $base_present; then
 292                        touch "$BACKUP"
 293                        "$merge_tool_path" \
 294                                -base:"$BASE" -mine:"$LOCAL" \
 295                                -theirs:"$REMOTE" -merged:"$MERGED"
 296                        check_unchanged
 297                else
 298                        echo "TortoiseMerge cannot be used without a base" 1>&2
 299                        status=1
 300                fi
 301                ;;
 302        xxdiff)
 303                if merge_mode; then
 304                        touch "$BACKUP"
 305                        if $base_present; then
 306                                "$merge_tool_path" -X --show-merged-pane \
 307                                        -R 'Accel.SaveAsMerged: "Ctrl-S"' \
 308                                        -R 'Accel.Search: "Ctrl+F"' \
 309                                        -R 'Accel.SearchForward: "Ctrl-G"' \
 310                                        --merged-file "$MERGED" \
 311                                        "$LOCAL" "$BASE" "$REMOTE"
 312                        else
 313                                "$merge_tool_path" -X $extra \
 314                                        -R 'Accel.SaveAsMerged: "Ctrl-S"' \
 315                                        -R 'Accel.Search: "Ctrl+F"' \
 316                                        -R 'Accel.SearchForward: "Ctrl-G"' \
 317                                        --merged-file "$MERGED" \
 318                                        "$LOCAL" "$REMOTE"
 319                        fi
 320                        check_unchanged
 321                else
 322                        "$merge_tool_path" \
 323                                -R 'Accel.Search: "Ctrl+F"' \
 324                                -R 'Accel.SearchForward: "Ctrl-G"' \
 325                                "$LOCAL" "$REMOTE"
 326                fi
 327                ;;
 328        *)
 329                merge_tool_cmd="$(get_merge_tool_cmd "$1")"
 330                if test -z "$merge_tool_cmd"; then
 331                        if merge_mode; then
 332                                status=1
 333                        fi
 334                        break
 335                fi
 336                if merge_mode; then
 337                        trust_exit_code="$(git config --bool \
 338                                mergetool."$1".trustExitCode || echo false)"
 339                        if test "$trust_exit_code" = "false"; then
 340                                touch "$BACKUP"
 341                                ( eval $merge_tool_cmd )
 342                                check_unchanged
 343                        else
 344                                ( eval $merge_tool_cmd )
 345                                status=$?
 346                        fi
 347                else
 348                        ( eval $merge_tool_cmd )
 349                fi
 350                ;;
 351        esac
 352        return $status
 353}
 354
 355guess_merge_tool () {
 356        if merge_mode; then
 357                tools="tortoisemerge"
 358        else
 359                tools="kompare"
 360        fi
 361        if test -n "$DISPLAY"; then
 362                if test -n "$GNOME_DESKTOP_SESSION_ID" ; then
 363                        tools="meld opendiff kdiff3 tkdiff xxdiff $tools"
 364                else
 365                        tools="opendiff kdiff3 tkdiff xxdiff meld $tools"
 366                fi
 367                tools="$tools gvimdiff diffuse ecmerge p4merge araxis bc3"
 368        fi
 369        case "${VISUAL:-$EDITOR}" in
 370        *vim*)
 371                tools="$tools vimdiff emerge"
 372                ;;
 373        *)
 374                tools="$tools emerge vimdiff"
 375                ;;
 376        esac
 377        echo >&2 "merge tool candidates: $tools"
 378
 379        # Loop over each candidate and stop when a valid merge tool is found.
 380        for i in $tools
 381        do
 382                merge_tool_path="$(translate_merge_tool_path "$i")"
 383                if type "$merge_tool_path" > /dev/null 2>&1; then
 384                        echo "$i"
 385                        return 0
 386                fi
 387        done
 388
 389        echo >&2 "No known merge resolution program available."
 390        return 1
 391}
 392
 393get_configured_merge_tool () {
 394        # Diff mode first tries diff.tool and falls back to merge.tool.
 395        # Merge mode only checks merge.tool
 396        if diff_mode; then
 397                merge_tool=$(git config diff.tool || git config merge.tool)
 398        else
 399                merge_tool=$(git config merge.tool)
 400        fi
 401        if test -n "$merge_tool" && ! valid_tool "$merge_tool"; then
 402                echo >&2 "git config option $TOOL_MODE.tool set to unknown tool: $merge_tool"
 403                echo >&2 "Resetting to default..."
 404                return 1
 405        fi
 406        echo "$merge_tool"
 407}
 408
 409get_merge_tool_path () {
 410        # A merge tool has been set, so verify that it's valid.
 411        if test -n "$1"; then
 412                merge_tool="$1"
 413        else
 414                merge_tool="$(get_merge_tool)"
 415        fi
 416        if ! valid_tool "$merge_tool"; then
 417                echo >&2 "Unknown merge tool $merge_tool"
 418                exit 1
 419        fi
 420        if diff_mode; then
 421                merge_tool_path=$(git config difftool."$merge_tool".path ||
 422                                  git config mergetool."$merge_tool".path)
 423        else
 424                merge_tool_path=$(git config mergetool."$merge_tool".path)
 425        fi
 426        if test -z "$merge_tool_path"; then
 427                merge_tool_path="$(translate_merge_tool_path "$merge_tool")"
 428        fi
 429        if test -z "$(get_merge_tool_cmd "$merge_tool")" &&
 430        ! type "$merge_tool_path" > /dev/null 2>&1; then
 431                echo >&2 "The $TOOL_MODE tool $merge_tool is not available as"\
 432                         "'$merge_tool_path'"
 433                exit 1
 434        fi
 435        echo "$merge_tool_path"
 436}
 437
 438get_merge_tool () {
 439        # Check if a merge tool has been configured
 440        merge_tool=$(get_configured_merge_tool)
 441        # Try to guess an appropriate merge tool if no tool has been set.
 442        if test -z "$merge_tool"; then
 443                merge_tool="$(guess_merge_tool)" || exit
 444        fi
 445        echo "$merge_tool"
 446}