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