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