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