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