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