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