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