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 INT 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) 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*) 119iftest -n"$merge_tool_cmd";then 120(eval$merge_tool_cmd) 121fi 122;; 123esac 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)" 131test -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() { 137case"$1"in 138 kdiff3 | tkdiff | xxdiff | meld | opendiff | emerge | vimdiff | gvimdiff | ecmerge) 139;;# happy 140*) 141if! valid_custom_tool "$1" 142then 143return1 144fi 145;; 146esac 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) 153iftest -z"$merge_tool_path";then 154case"$1"in 155 emerge) 156 merge_tool_path=emacs 157;; 158*) 159 merge_tool_path="$1" 160;; 161esac 162fi 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. 170iftest -z"$merge_tool";then 171 merge_tool=$(git config merge.tool) 172iftest -n"$merge_tool"&& ! valid_tool "$merge_tool";then 173echo>&2"git config option merge.tool set to unknown tool:$merge_tool" 174echo>&2"Resetting to default..." 175unset merge_tool 176fi 177fi 178 179# Try to guess an appropriate merge tool if no tool has been set. 180iftest -z"$merge_tool";then 181 182# We have a $DISPLAY so try some common UNIX merge tools 183iftest -n"$DISPLAY";then 184 merge_tool_candidates="kdiff3 tkdiff xxdiff meld gvimdiff" 185# If gnome then prefer meld 186iftest -n"$GNOME_DESKTOP_SESSION_ID";then 187 merge_tool_candidates="meld$merge_tool_candidates" 188fi 189# If KDE then prefer kdiff3 190iftest"$KDE_FULL_SESSION"="true";then 191 merge_tool_candidates="kdiff3$merge_tool_candidates" 192fi 193fi 194 195# $EDITOR is emacs so add emerge as a candidate 196ifecho"${VISUAL:-$EDITOR}"|grep'emacs'> /dev/null 2>&1;then 197 merge_tool_candidates="$merge_tool_candidatesemerge" 198fi 199 200# $EDITOR is vim so add vimdiff as a candidate 201ifecho"${VISUAL:-$EDITOR}"|grep'vim'> /dev/null 2>&1;then 202 merge_tool_candidates="$merge_tool_candidatesvimdiff" 203fi 204 205 merge_tool_candidates="$merge_tool_candidatesopendiff emerge vimdiff" 206echo"merge tool candidates:$merge_tool_candidates" 207 208# Loop over each candidate and stop when a valid merge tool is found. 209for i in$merge_tool_candidates 210do 211 init_merge_tool_path $i 212iftype"$merge_tool_path"> /dev/null 2>&1;then 213 merge_tool=$i 214break 215fi 216done 217 218iftest -z"$merge_tool";then 219echo"No known merge resolution program available." 220exit1 221fi 222 223else 224# A merge tool has been set, so verify that it's valid. 225if! valid_tool "$merge_tool";then 226echo>&2"Unknown merge tool$merge_tool" 227exit1 228fi 229 230 init_merge_tool_path "$merge_tool" 231 232iftest -z"$merge_tool_cmd"&& !type"$merge_tool_path"> /dev/null 2>&1;then 233echo"The merge tool$merge_toolis not available as '$merge_tool_path'" 234exit1 235fi 236fi 237 238 239# Launch the merge tool on each path provided by 'git diff' 240whiletest$#-gt6 241do 242 launch_merge_tool "$1""$2""$5" 243shift7 244done