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