f3c27d86adf3ba158de4749d45c4ac54e7887e76
   1#!/bin/sh
   2# git-difftool--helper is a GIT_EXTERNAL_DIFF-compatible diff tool launcher.
   3# This script is typically launched by using the 'git difftool'
   4# convenience command.
   5#
   6# Copyright (c) 2009 David Aguilar
   7
   8# difftool.prompt controls the default prompt/no-prompt behavior
   9# and is overridden with $GIT_DIFFTOOL*_PROMPT.
  10should_prompt () {
  11        prompt=$(git config --bool difftool.prompt || echo true)
  12        if test "$prompt" = true; then
  13                test -z "$GIT_DIFFTOOL_NO_PROMPT"
  14        else
  15                test -n "$GIT_DIFFTOOL_PROMPT"
  16        fi
  17}
  18
  19# This function prepares temporary files and launches the appropriate
  20# merge tool.
  21launch_merge_tool () {
  22        # Merged is the filename as it appears in the work tree
  23        # Local is the contents of a/filename
  24        # Remote is the contents of b/filename
  25        # Custom merge tool commands might use $BASE so we provide it
  26        MERGED="$1"
  27        LOCAL="$2"
  28        REMOTE="$3"
  29        BASE="$1"
  30
  31        # $LOCAL and $REMOTE are temporary files so prompt
  32        # the user with the real $MERGED name before launching $merge_tool.
  33        if should_prompt; then
  34                printf "\nViewing: '$MERGED'\n"
  35                printf "Hit return to launch '%s': " "$merge_tool"
  36                read ans
  37        fi
  38
  39        # Run the appropriate merge tool command
  40        case "$merge_tool" in
  41        kdiff3)
  42                basename=$(basename "$MERGED")
  43                "$merge_tool_path" --auto \
  44                        --L1 "$basename (A)" \
  45                        --L2 "$basename (B)" \
  46                        "$LOCAL" "$REMOTE" \
  47                        > /dev/null 2>&1
  48                ;;
  49
  50        kompare)
  51                "$merge_tool_path" "$LOCAL" "$REMOTE"
  52                ;;
  53
  54        tkdiff)
  55                "$merge_tool_path" "$LOCAL" "$REMOTE"
  56                ;;
  57
  58        meld)
  59                "$merge_tool_path" "$LOCAL" "$REMOTE"
  60                ;;
  61
  62        diffuse)
  63                "$merge_tool_path" "$LOCAL" "$REMOTE" | cat
  64                ;;
  65
  66        vimdiff)
  67                "$merge_tool_path" -d -c "wincmd l" "$LOCAL" "$REMOTE"
  68                ;;
  69
  70        gvimdiff)
  71                "$merge_tool_path" -d -c "wincmd l" -f "$LOCAL" "$REMOTE"
  72                ;;
  73
  74        xxdiff)
  75                "$merge_tool_path" \
  76                        -R 'Accel.Search: "Ctrl+F"' \
  77                        -R 'Accel.SearchForward: "Ctrl-G"' \
  78                        "$LOCAL" "$REMOTE"
  79                ;;
  80
  81        opendiff)
  82                "$merge_tool_path" "$LOCAL" "$REMOTE" | cat
  83                ;;
  84
  85        ecmerge)
  86                "$merge_tool_path" "$LOCAL" "$REMOTE" \
  87                        --default --mode=merge2 --to="$MERGED"
  88                ;;
  89
  90        emerge)
  91                "$merge_tool_path" -f emerge-files-command \
  92                        "$LOCAL" "$REMOTE" "$(basename "$MERGED")"
  93                ;;
  94
  95        *)
  96                if test -n "$merge_tool_cmd"; then
  97                        ( eval $merge_tool_cmd )
  98                fi
  99                ;;
 100        esac
 101}
 102
 103# Verifies that (difftool|mergetool).<tool>.cmd exists
 104valid_custom_tool() {
 105        merge_tool_cmd="$(git config difftool.$1.cmd)"
 106        test -z "$merge_tool_cmd" &&
 107        merge_tool_cmd="$(git config mergetool.$1.cmd)"
 108        test -n "$merge_tool_cmd"
 109}
 110
 111# Verifies that the chosen merge tool is properly setup.
 112# Built-in merge tools are always valid.
 113valid_tool() {
 114        case "$1" in
 115        kdiff3 | kompare | tkdiff | xxdiff | meld | opendiff | emerge | vimdiff | gvimdiff | ecmerge)
 116                ;; # happy
 117        *)
 118                if ! valid_custom_tool "$1"
 119                then
 120                        return 1
 121                fi
 122                ;;
 123        esac
 124}
 125
 126# Sets up the merge_tool_path variable.
 127# This handles the difftool.<tool>.path configuration.
 128# This also falls back to mergetool defaults.
 129init_merge_tool_path() {
 130        merge_tool_path=$(git config difftool."$1".path)
 131        test -z "$merge_tool_path" &&
 132        merge_tool_path=$(git config mergetool."$1".path)
 133        if test -z "$merge_tool_path"; then
 134                case "$1" in
 135                vimdiff)
 136                        merge_tool_path=vim
 137                        ;;
 138                gvimdiff)
 139                        merge_tool_path=gvim
 140                        ;;
 141                emerge)
 142                        merge_tool_path=emacs
 143                        ;;
 144                *)
 145                        merge_tool_path="$1"
 146                        ;;
 147                esac
 148        fi
 149}
 150
 151# Allow GIT_DIFF_TOOL and GIT_MERGE_TOOL to provide default values
 152test -n "$GIT_MERGE_TOOL" && merge_tool="$GIT_MERGE_TOOL"
 153test -n "$GIT_DIFF_TOOL" && merge_tool="$GIT_DIFF_TOOL"
 154
 155# If merge tool was not specified then use the diff.tool
 156# configuration variable.  If that's invalid then reset merge_tool.
 157# Fallback to merge.tool.
 158if test -z "$merge_tool"; then
 159        merge_tool=$(git config diff.tool)
 160        test -z "$merge_tool" &&
 161        merge_tool=$(git config merge.tool)
 162        if test -n "$merge_tool" && ! valid_tool "$merge_tool"; then
 163                echo >&2 "git config option diff.tool set to unknown tool: $merge_tool"
 164                echo >&2 "Resetting to default..."
 165                unset merge_tool
 166        fi
 167fi
 168
 169# Try to guess an appropriate merge tool if no tool has been set.
 170if test -z "$merge_tool"; then
 171        # We have a $DISPLAY so try some common UNIX merge tools
 172        if test -n "$DISPLAY"; then
 173                # If gnome then prefer meld, otherwise, prefer kdiff3 or kompare
 174                if test -n "$GNOME_DESKTOP_SESSION_ID" ; then
 175                        merge_tool_candidates="meld kdiff3 kompare tkdiff xxdiff gvimdiff diffuse"
 176                else
 177                        merge_tool_candidates="kdiff3 kompare tkdiff xxdiff meld gvimdiff diffuse"
 178                fi
 179        fi
 180        if echo "${VISUAL:-$EDITOR}" | grep 'emacs' > /dev/null 2>&1; then
 181                # $EDITOR is emacs so add emerge as a candidate
 182                merge_tool_candidates="$merge_tool_candidates emerge opendiff vimdiff"
 183        elif echo "${VISUAL:-$EDITOR}" | grep 'vim' > /dev/null 2>&1; then
 184                # $EDITOR is vim so add vimdiff as a candidate
 185                merge_tool_candidates="$merge_tool_candidates vimdiff opendiff emerge"
 186        else
 187                merge_tool_candidates="$merge_tool_candidates opendiff emerge vimdiff"
 188        fi
 189        echo "merge tool candidates: $merge_tool_candidates"
 190
 191        # Loop over each candidate and stop when a valid merge tool is found.
 192        for i in $merge_tool_candidates
 193        do
 194                init_merge_tool_path $i
 195                if type "$merge_tool_path" > /dev/null 2>&1; then
 196                        merge_tool=$i
 197                        break
 198                fi
 199        done
 200
 201        if test -z "$merge_tool" ; then
 202                echo "No known merge resolution program available."
 203                exit 1
 204        fi
 205
 206else
 207        # A merge tool has been set, so verify that it's valid.
 208        if ! valid_tool "$merge_tool"; then
 209                echo >&2 "Unknown merge tool $merge_tool"
 210                exit 1
 211        fi
 212
 213        init_merge_tool_path "$merge_tool"
 214
 215        if test -z "$merge_tool_cmd" && ! type "$merge_tool_path" > /dev/null 2>&1; then
 216                echo "The merge tool $merge_tool is not available as '$merge_tool_path'"
 217                exit 1
 218        fi
 219fi
 220
 221
 222# Launch the merge tool on each path provided by 'git diff'
 223while test $# -gt 6
 224do
 225        launch_merge_tool "$1" "$2" "$5"
 226        shift 7
 227done