git-mergetool--lib.shon commit cherry-pick: handle root commits with external strategies (1618073)
   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                        $base_present || >"$BASE"
 263                        "$merge_tool_path" "$BASE" "$LOCAL" "$REMOTE" "$MERGED"
 264                        check_unchanged
 265                else
 266                        "$merge_tool_path" "$LOCAL" "$REMOTE"
 267                fi
 268                ;;
 269        tkdiff)
 270                if merge_mode; then
 271                        if $base_present; then
 272                                "$merge_tool_path" -a "$BASE" \
 273                                        -o "$MERGED" "$LOCAL" "$REMOTE"
 274                        else
 275                                "$merge_tool_path" \
 276                                        -o "$MERGED" "$LOCAL" "$REMOTE"
 277                        fi
 278                        status=$?
 279                else
 280                        "$merge_tool_path" "$LOCAL" "$REMOTE"
 281                fi
 282                ;;
 283        tortoisemerge)
 284                if $base_present; then
 285                        touch "$BACKUP"
 286                        "$merge_tool_path" \
 287                                -base:"$BASE" -mine:"$LOCAL" \
 288                                -theirs:"$REMOTE" -merged:"$MERGED"
 289                        check_unchanged
 290                else
 291                        echo "TortoiseMerge cannot be used without a base" 1>&2
 292                        status=1
 293                fi
 294                ;;
 295        xxdiff)
 296                if merge_mode; then
 297                        touch "$BACKUP"
 298                        if $base_present; then
 299                                "$merge_tool_path" -X --show-merged-pane \
 300                                        -R 'Accel.SaveAsMerged: "Ctrl-S"' \
 301                                        -R 'Accel.Search: "Ctrl+F"' \
 302                                        -R 'Accel.SearchForward: "Ctrl-G"' \
 303                                        --merged-file "$MERGED" \
 304                                        "$LOCAL" "$BASE" "$REMOTE"
 305                        else
 306                                "$merge_tool_path" -X $extra \
 307                                        -R 'Accel.SaveAsMerged: "Ctrl-S"' \
 308                                        -R 'Accel.Search: "Ctrl+F"' \
 309                                        -R 'Accel.SearchForward: "Ctrl-G"' \
 310                                        --merged-file "$MERGED" \
 311                                        "$LOCAL" "$REMOTE"
 312                        fi
 313                        check_unchanged
 314                else
 315                        "$merge_tool_path" \
 316                                -R 'Accel.Search: "Ctrl+F"' \
 317                                -R 'Accel.SearchForward: "Ctrl-G"' \
 318                                "$LOCAL" "$REMOTE"
 319                fi
 320                ;;
 321        *)
 322                merge_tool_cmd="$(get_merge_tool_cmd "$1")"
 323                if test -z "$merge_tool_cmd"; then
 324                        if merge_mode; then
 325                                status=1
 326                        fi
 327                        break
 328                fi
 329                if merge_mode; then
 330                        trust_exit_code="$(git config --bool \
 331                                mergetool."$1".trustExitCode || echo false)"
 332                        if test "$trust_exit_code" = "false"; then
 333                                touch "$BACKUP"
 334                                ( eval $merge_tool_cmd )
 335                                check_unchanged
 336                        else
 337                                ( eval $merge_tool_cmd )
 338                                status=$?
 339                        fi
 340                else
 341                        ( eval $merge_tool_cmd )
 342                fi
 343                ;;
 344        esac
 345        return $status
 346}
 347
 348guess_merge_tool () {
 349        if merge_mode; then
 350                tools="tortoisemerge"
 351        else
 352                tools="kompare"
 353        fi
 354        if test -n "$DISPLAY"; then
 355                if test -n "$GNOME_DESKTOP_SESSION_ID" ; then
 356                        tools="meld opendiff kdiff3 tkdiff xxdiff $tools"
 357                else
 358                        tools="opendiff kdiff3 tkdiff xxdiff meld $tools"
 359                fi
 360                tools="$tools gvimdiff diffuse ecmerge p4merge araxis bc3"
 361        fi
 362        case "${VISUAL:-$EDITOR}" in
 363        *vim*)
 364                tools="$tools vimdiff emerge"
 365                ;;
 366        *)
 367                tools="$tools emerge vimdiff"
 368                ;;
 369        esac
 370        echo >&2 "merge tool candidates: $tools"
 371
 372        # Loop over each candidate and stop when a valid merge tool is found.
 373        for i in $tools
 374        do
 375                merge_tool_path="$(translate_merge_tool_path "$i")"
 376                if type "$merge_tool_path" > /dev/null 2>&1; then
 377                        echo "$i"
 378                        return 0
 379                fi
 380        done
 381
 382        echo >&2 "No known merge resolution program available."
 383        return 1
 384}
 385
 386get_configured_merge_tool () {
 387        # Diff mode first tries diff.tool and falls back to merge.tool.
 388        # Merge mode only checks merge.tool
 389        if diff_mode; then
 390                merge_tool=$(git config diff.tool || git config merge.tool)
 391        else
 392                merge_tool=$(git config merge.tool)
 393        fi
 394        if test -n "$merge_tool" && ! valid_tool "$merge_tool"; then
 395                echo >&2 "git config option $TOOL_MODE.tool set to unknown tool: $merge_tool"
 396                echo >&2 "Resetting to default..."
 397                return 1
 398        fi
 399        echo "$merge_tool"
 400}
 401
 402get_merge_tool_path () {
 403        # A merge tool has been set, so verify that it's valid.
 404        if test -n "$1"; then
 405                merge_tool="$1"
 406        else
 407                merge_tool="$(get_merge_tool)"
 408        fi
 409        if ! valid_tool "$merge_tool"; then
 410                echo >&2 "Unknown merge tool $merge_tool"
 411                exit 1
 412        fi
 413        if diff_mode; then
 414                merge_tool_path=$(git config difftool."$merge_tool".path ||
 415                                  git config mergetool."$merge_tool".path)
 416        else
 417                merge_tool_path=$(git config mergetool."$merge_tool".path)
 418        fi
 419        if test -z "$merge_tool_path"; then
 420                merge_tool_path="$(translate_merge_tool_path "$merge_tool")"
 421        fi
 422        if test -z "$(get_merge_tool_cmd "$merge_tool")" &&
 423        ! type "$merge_tool_path" > /dev/null 2>&1; then
 424                echo >&2 "The $TOOL_MODE tool $merge_tool is not available as"\
 425                         "'$merge_tool_path'"
 426                exit 1
 427        fi
 428        echo "$merge_tool_path"
 429}
 430
 431get_merge_tool () {
 432        # Check if a merge tool has been configured
 433        merge_tool=$(get_configured_merge_tool)
 434        # Try to guess an appropriate merge tool if no tool has been set.
 435        if test -z "$merge_tool"; then
 436                merge_tool="$(guess_merge_tool)" || exit
 437        fi
 438        echo "$merge_tool"
 439}