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 () { 18test"$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 () { 24iftest -n"$MERGED";then 25if keep_backup &&test"$MERGED"-nt"$BACKUP";then 26test -f"$BACKUP"&&mv--"$BACKUP""$MERGED.orig" 27else 28rm-f --"$BACKUP" 29fi 30fi 31} 32 33# This is called when users Ctrl-C out of git-difftool-helper 34sigint_handler () { 35echo 36 cleanup_temp_files 37exit1 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 55test -f"$MERGED"&&cp--"$MERGED""$BACKUP" 56trap 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. 60if should_prompt;then 61printf"\nViewing: '$MERGED'\n" 62printf"Hit return to launch '%s': ""$merge_tool" 63read ans 64fi 65 66# Run the appropriate merge tool command 67case"$merge_tool"in 68 kdiff3) 69basename=$(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|vimdiff) 82"$merge_tool_path""$LOCAL""$REMOTE" 83;; 84 85 gvimdiff) 86"$merge_tool_path"-f"$LOCAL""$REMOTE" 87;; 88 89 xxdiff) 90"$merge_tool_path" \ 91-X \ 92-R'Accel.SaveAsMerged: "Ctrl-S"' \ 93-R'Accel.Search: "Ctrl+F"' \ 94-R'Accel.SearchForward: "Ctrl-G"' \ 95--merged-file"$MERGED" \ 96"$LOCAL""$REMOTE" 97;; 98 99 opendiff) 100"$merge_tool_path""$LOCAL""$REMOTE" \ 101-merge"$MERGED"|cat 102;; 103 104 ecmerge) 105"$merge_tool_path""$LOCAL""$REMOTE" \ 106--default --mode=merge2 --to="$MERGED" 107;; 108 109 emerge) 110"$merge_tool_path"-f emerge-files-command \ 111"$LOCAL""$REMOTE""$(basename "$MERGED")" 112;; 113 114*) 115iftest -n"$merge_tool_cmd";then 116(eval$merge_tool_cmd) 117fi 118;; 119esac 120 121 cleanup_temp_files 122} 123 124# Verifies that mergetool.<tool>.cmd exists 125valid_custom_tool() { 126 merge_tool_cmd="$(git config mergetool.$1.cmd)" 127test -n"$merge_tool_cmd" 128} 129 130# Verifies that the chosen merge tool is properly setup. 131# Built-in merge tools are always valid. 132valid_tool() { 133case"$1"in 134 kdiff3 | tkdiff | xxdiff | meld | opendiff | emerge | vimdiff | gvimdiff | ecmerge) 135;;# happy 136*) 137if! valid_custom_tool "$1" 138then 139return1 140fi 141;; 142esac 143} 144 145# Sets up the merge_tool_path variable. 146# This handles the mergetool.<tool>.path configuration. 147init_merge_tool_path() { 148 merge_tool_path=$(git config mergetool."$1".path) 149iftest -z"$merge_tool_path";then 150case"$1"in 151 emerge) 152 merge_tool_path=emacs 153;; 154*) 155 merge_tool_path="$1" 156;; 157esac 158fi 159} 160 161# Allow the GIT_MERGE_TOOL variable to provide a default value 162test -n"$GIT_MERGE_TOOL"&& merge_tool="$GIT_MERGE_TOOL" 163 164# If not merge tool was specified then use the merge.tool 165# configuration variable. If that's invalid then reset merge_tool. 166iftest -z"$merge_tool";then 167 merge_tool=$(git config merge.tool) 168iftest -n"$merge_tool"&& ! valid_tool "$merge_tool";then 169echo>&2"git config option merge.tool set to unknown tool:$merge_tool" 170echo>&2"Resetting to default..." 171unset merge_tool 172fi 173fi 174 175# Try to guess an appropriate merge tool if no tool has been set. 176iftest -z"$merge_tool";then 177 178# We have a $DISPLAY so try some common UNIX merge tools 179iftest -n"$DISPLAY";then 180 merge_tool_candidates="kdiff3 tkdiff xxdiff meld gvimdiff" 181# If gnome then prefer meld 182iftest -n"$GNOME_DESKTOP_SESSION_ID";then 183 merge_tool_candidates="meld$merge_tool_candidates" 184fi 185# If KDE then prefer kdiff3 186iftest"$KDE_FULL_SESSION"="true";then 187 merge_tool_candidates="kdiff3$merge_tool_candidates" 188fi 189fi 190 191# $EDITOR is emacs so add emerge as a candidate 192ifecho"${VISUAL:-$EDITOR}"|grep'emacs'> /dev/null 2>&1;then 193 merge_tool_candidates="$merge_tool_candidatesemerge" 194fi 195 196# $EDITOR is vim so add vimdiff as a candidate 197ifecho"${VISUAL:-$EDITOR}"|grep'vim'> /dev/null 2>&1;then 198 merge_tool_candidates="$merge_tool_candidatesvimdiff" 199fi 200 201 merge_tool_candidates="$merge_tool_candidatesopendiff emerge vimdiff" 202echo"merge tool candidates:$merge_tool_candidates" 203 204# Loop over each candidate and stop when a valid merge tool is found. 205for i in$merge_tool_candidates 206do 207 init_merge_tool_path $i 208iftype"$merge_tool_path"> /dev/null 2>&1;then 209 merge_tool=$i 210break 211fi 212done 213 214iftest -z"$merge_tool";then 215echo"No known merge resolution program available." 216exit1 217fi 218 219else 220# A merge tool has been set, so verify that it's valid. 221if! valid_tool "$merge_tool";then 222echo>&2"Unknown merge tool$merge_tool" 223exit1 224fi 225 226 init_merge_tool_path "$merge_tool" 227 228iftest -z"$merge_tool_cmd"&& !type"$merge_tool_path"> /dev/null 2>&1;then 229echo"The merge tool$merge_toolis not available as '$merge_tool_path'" 230exit1 231fi 232fi 233 234 235# Launch the merge tool on each path provided by 'git diff' 236whiletest$#-gt6 237do 238 launch_merge_tool "$1""$2""$5" 239shift7 240done