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