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