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