b84ac582f957a4e77af32f6f6285fd5106219804
   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                        if $base_present; then
 176                                "$merge_tool_path" -f -d -c "wincmd J" \
 177                                        "$MERGED" "$LOCAL" "$BASE" "$REMOTE"
 178                        else
 179                                "$merge_tool_path" -f -d -c "wincmd l" \
 180                                        "$LOCAL" "$MERGED" "$REMOTE"
 181                        fi
 182                        check_unchanged
 183                else
 184                        "$merge_tool_path" -f -d -c "wincmd l" \
 185                                "$LOCAL" "$REMOTE"
 186                fi
 187                ;;
 188        xxdiff)
 189                if merge_mode; then
 190                        touch "$BACKUP"
 191                        if $base_present; then
 192                                "$merge_tool_path" -X --show-merged-pane \
 193                                        -R 'Accel.SaveAsMerged: "Ctrl-S"' \
 194                                        -R 'Accel.Search: "Ctrl+F"' \
 195                                        -R 'Accel.SearchForward: "Ctrl-G"' \
 196                                        --merged-file "$MERGED" \
 197                                        "$LOCAL" "$BASE" "$REMOTE"
 198                        else
 199                                "$merge_tool_path" -X $extra \
 200                                        -R 'Accel.SaveAsMerged: "Ctrl-S"' \
 201                                        -R 'Accel.Search: "Ctrl+F"' \
 202                                        -R 'Accel.SearchForward: "Ctrl-G"' \
 203                                        --merged-file "$MERGED" \
 204                                        "$LOCAL" "$REMOTE"
 205                        fi
 206                        check_unchanged
 207                else
 208                        "$merge_tool_path" \
 209                                -R 'Accel.Search: "Ctrl+F"' \
 210                                -R 'Accel.SearchForward: "Ctrl-G"' \
 211                                "$LOCAL" "$REMOTE"
 212                fi
 213                ;;
 214        opendiff)
 215                if merge_mode; then
 216                        touch "$BACKUP"
 217                        if $base_present; then
 218                                "$merge_tool_path" "$LOCAL" "$REMOTE" \
 219                                        -ancestor "$BASE" \
 220                                        -merge "$MERGED" | cat
 221                        else
 222                                "$merge_tool_path" "$LOCAL" "$REMOTE" \
 223                                        -merge "$MERGED" | cat
 224                        fi
 225                        check_unchanged
 226                else
 227                        "$merge_tool_path" "$LOCAL" "$REMOTE" | cat
 228                fi
 229                ;;
 230        ecmerge)
 231                if merge_mode; then
 232                        touch "$BACKUP"
 233                        if $base_present; then
 234                                "$merge_tool_path" "$BASE" "$LOCAL" "$REMOTE" \
 235                                        --default --mode=merge3 --to="$MERGED"
 236                        else
 237                                "$merge_tool_path" "$LOCAL" "$REMOTE" \
 238                                        --default --mode=merge2 --to="$MERGED"
 239                        fi
 240                        check_unchanged
 241                else
 242                        "$merge_tool_path" --default --mode=diff2 \
 243                                "$LOCAL" "$REMOTE"
 244                fi
 245                ;;
 246        emerge)
 247                if merge_mode; then
 248                        if $base_present; then
 249                                "$merge_tool_path" \
 250                                        -f emerge-files-with-ancestor-command \
 251                                        "$LOCAL" "$REMOTE" "$BASE" \
 252                                        "$(basename "$MERGED")"
 253                        else
 254                                "$merge_tool_path" \
 255                                        -f emerge-files-command \
 256                                        "$LOCAL" "$REMOTE" \
 257                                        "$(basename "$MERGED")"
 258                        fi
 259                        status=$?
 260                else
 261                        "$merge_tool_path" -f emerge-files-command \
 262                                "$LOCAL" "$REMOTE"
 263                fi
 264                ;;
 265        tortoisemerge)
 266                if $base_present; then
 267                        touch "$BACKUP"
 268                        "$merge_tool_path" \
 269                                -base:"$BASE" -mine:"$LOCAL" \
 270                                -theirs:"$REMOTE" -merged:"$MERGED"
 271                        check_unchanged
 272                else
 273                        echo "TortoiseMerge cannot be used without a base" 1>&2
 274                        status=1
 275                fi
 276                ;;
 277        araxis)
 278                if merge_mode; then
 279                        touch "$BACKUP"
 280                        if $base_present; then
 281                                "$merge_tool_path" -wait -merge -3 -a1 \
 282                                        "$BASE" "$LOCAL" "$REMOTE" "$MERGED" \
 283                                        >/dev/null 2>&1
 284                        else
 285                                "$merge_tool_path" -wait -2 \
 286                                        "$LOCAL" "$REMOTE" "$MERGED" \
 287                                        >/dev/null 2>&1
 288                        fi
 289                        check_unchanged
 290                else
 291                        "$merge_tool_path" -wait -2 "$LOCAL" "$REMOTE" \
 292                                >/dev/null 2>&1
 293                fi
 294                ;;
 295        *)
 296                merge_tool_cmd="$(get_merge_tool_cmd "$1")"
 297                if test -z "$merge_tool_cmd"; then
 298                        if merge_mode; then
 299                                status=1
 300                        fi
 301                        break
 302                fi
 303                if merge_mode; then
 304                        trust_exit_code="$(git config --bool \
 305                                mergetool."$1".trustExitCode || echo false)"
 306                        if test "$trust_exit_code" = "false"; then
 307                                touch "$BACKUP"
 308                                ( eval $merge_tool_cmd )
 309                                check_unchanged
 310                        else
 311                                ( eval $merge_tool_cmd )
 312                                status=$?
 313                        fi
 314                else
 315                        ( eval $merge_tool_cmd )
 316                fi
 317                ;;
 318        esac
 319        return $status
 320}
 321
 322guess_merge_tool () {
 323        if merge_mode; then
 324                tools="tortoisemerge"
 325        else
 326                tools="kompare"
 327        fi
 328        if test -n "$DISPLAY"; then
 329                if test -n "$GNOME_DESKTOP_SESSION_ID" ; then
 330                        tools="meld opendiff kdiff3 tkdiff xxdiff $tools"
 331                else
 332                        tools="opendiff kdiff3 tkdiff xxdiff meld $tools"
 333                fi
 334                tools="$tools gvimdiff diffuse ecmerge p4merge araxis"
 335        fi
 336        case "${VISUAL:-$EDITOR}" in
 337        *vim*)
 338                tools="$tools vimdiff emerge"
 339                ;;
 340        *)
 341                tools="$tools emerge vimdiff"
 342                ;;
 343        esac
 344        echo >&2 "merge tool candidates: $tools"
 345
 346        # Loop over each candidate and stop when a valid merge tool is found.
 347        for i in $tools
 348        do
 349                merge_tool_path="$(translate_merge_tool_path "$i")"
 350                if type "$merge_tool_path" > /dev/null 2>&1; then
 351                        echo "$i"
 352                        return 0
 353                fi
 354        done
 355
 356        echo >&2 "No known merge resolution program available."
 357        return 1
 358}
 359
 360get_configured_merge_tool () {
 361        # Diff mode first tries diff.tool and falls back to merge.tool.
 362        # Merge mode only checks merge.tool
 363        if diff_mode; then
 364                merge_tool=$(git config diff.tool || git config merge.tool)
 365        else
 366                merge_tool=$(git config merge.tool)
 367        fi
 368        if test -n "$merge_tool" && ! valid_tool "$merge_tool"; then
 369                echo >&2 "git config option $TOOL_MODE.tool set to unknown tool: $merge_tool"
 370                echo >&2 "Resetting to default..."
 371                return 1
 372        fi
 373        echo "$merge_tool"
 374}
 375
 376get_merge_tool_path () {
 377        # A merge tool has been set, so verify that it's valid.
 378        if test -n "$1"; then
 379                merge_tool="$1"
 380        else
 381                merge_tool="$(get_merge_tool)"
 382        fi
 383        if ! valid_tool "$merge_tool"; then
 384                echo >&2 "Unknown merge tool $merge_tool"
 385                exit 1
 386        fi
 387        if diff_mode; then
 388                merge_tool_path=$(git config difftool."$merge_tool".path ||
 389                                  git config mergetool."$merge_tool".path)
 390        else
 391                merge_tool_path=$(git config mergetool."$merge_tool".path)
 392        fi
 393        if test -z "$merge_tool_path"; then
 394                merge_tool_path="$(translate_merge_tool_path "$merge_tool")"
 395        fi
 396        if test -z "$(get_merge_tool_cmd "$merge_tool")" &&
 397        ! type "$merge_tool_path" > /dev/null 2>&1; then
 398                echo >&2 "The $TOOL_MODE tool $merge_tool is not available as"\
 399                         "'$merge_tool_path'"
 400                exit 1
 401        fi
 402        echo "$merge_tool_path"
 403}
 404
 405get_merge_tool () {
 406        # Check if a merge tool has been configured
 407        merge_tool=$(get_configured_merge_tool)
 408        # Try to guess an appropriate merge tool if no tool has been set.
 409        if test -z "$merge_tool"; then
 410                merge_tool="$(guess_merge_tool)" || exit
 411        fi
 412        echo "$merge_tool"
 413}