Code

Merge branch 'js/windows-tests'
[git.git] / contrib / difftool / git-difftool-helper
1 #!/bin/sh
2 # git-difftool-helper is a GIT_EXTERNAL_DIFF-compatible diff tool launcher.
3 # It supports kdiff3, kompare, tkdiff, xxdiff, meld, opendiff,
4 # emerge, ecmerge, 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
10 # Set GIT_DIFFTOOL_NO_PROMPT to bypass the per-file prompt.
11 should_prompt () {
12         ! test -n "$GIT_DIFFTOOL_NO_PROMPT"
13 }
15 # Should we keep the backup .orig file?
16 keep_backup_mode="$(git config --bool merge.keepBackup || echo true)"
17 keep_backup () {
18         test "$keep_backup_mode" = "true"
19 }
21 # This function manages the backup .orig file.
22 # A backup $MERGED.orig file is created if changes are detected.
23 cleanup_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 }
33 # This is called when users Ctrl-C out of git-difftool-helper
34 sigint_handler () {
35         cleanup_temp_files
36         exit 1
37 }
39 # This function prepares temporary files and launches the appropriate
40 # merge tool.
41 launch_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"
53         # Create and ensure that we clean up $BACKUP
54         test -f "$MERGED" && cp -- "$MERGED" "$BACKUP"
55         trap sigint_handler INT
57         # $LOCAL and $REMOTE are temporary files so prompt
58         # the user with the real $MERGED name before launching $merge_tool.
59         if should_prompt; then
60                 printf "\nViewing: '$MERGED'\n"
61                 printf "Hit return to launch '%s': " "$merge_tool"
62                 read ans
63         fi
65         # Run the appropriate merge tool command
66         case "$merge_tool" in
67         kdiff3)
68                 basename=$(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                 ;;
76         kompare)
77                 "$merge_tool_path" "$LOCAL" "$REMOTE"
78                 ;;
80         tkdiff)
81                 "$merge_tool_path" -o "$MERGED" "$LOCAL" "$REMOTE"
82                 ;;
84         meld)
85                 "$merge_tool_path" "$LOCAL" "$REMOTE"
86                 ;;
88         vimdiff)
89                 "$merge_tool_path" -c "wincmd l" "$LOCAL" "$REMOTE"
90                 ;;
92         gvimdiff)
93                 "$merge_tool_path" -c "wincmd l" -f "$LOCAL" "$REMOTE"
94                 ;;
96         xxdiff)
97                 "$merge_tool_path" \
98                         -X \
99                         -R 'Accel.SaveAsMerged: "Ctrl-S"' \
100                         -R 'Accel.Search: "Ctrl+F"' \
101                         -R 'Accel.SearchForward: "Ctrl-G"' \
102                         --merged-file "$MERGED" \
103                         "$LOCAL" "$REMOTE"
104                 ;;
106         opendiff)
107                 "$merge_tool_path" "$LOCAL" "$REMOTE" \
108                         -merge "$MERGED" | cat
109                 ;;
111         ecmerge)
112                 "$merge_tool_path" "$LOCAL" "$REMOTE" \
113                         --default --mode=merge2 --to="$MERGED"
114                 ;;
116         emerge)
117                 "$merge_tool_path" -f emerge-files-command \
118                         "$LOCAL" "$REMOTE" "$(basename "$MERGED")"
119                 ;;
121         *)
122                 if test -n "$merge_tool_cmd"; then
123                         ( eval $merge_tool_cmd )
124                 fi
125                 ;;
126         esac
128         cleanup_temp_files
131 # Verifies that (difftool|mergetool).<tool>.cmd exists
132 valid_custom_tool() {
133         merge_tool_cmd="$(git config difftool.$1.cmd)"
134         test -z "$merge_tool_cmd" &&
135         merge_tool_cmd="$(git config mergetool.$1.cmd)"
136         test -n "$merge_tool_cmd"
139 # Verifies that the chosen merge tool is properly setup.
140 # Built-in merge tools are always valid.
141 valid_tool() {
142         case "$1" in
143         kdiff3 | kompare | tkdiff | xxdiff | meld | opendiff | emerge | vimdiff | gvimdiff | ecmerge)
144                 ;; # happy
145         *)
146                 if ! valid_custom_tool "$1"
147                 then
148                         return 1
149                 fi
150                 ;;
151         esac
154 # Sets up the merge_tool_path variable.
155 # This handles the difftool.<tool>.path configuration.
156 # This also falls back to mergetool defaults.
157 init_merge_tool_path() {
158         merge_tool_path=$(git config difftool."$1".path)
159         test -z "$merge_tool_path" &&
160         merge_tool_path=$(git config mergetool."$1".path)
161         if test -z "$merge_tool_path"; then
162                 case "$1" in
163                 emerge)
164                         merge_tool_path=emacs
165                         ;;
166                 *)
167                         merge_tool_path="$1"
168                         ;;
169                 esac
170         fi
173 # Allow GIT_DIFF_TOOL and GIT_MERGE_TOOL to provide default values
174 test -n "$GIT_MERGE_TOOL" && merge_tool="$GIT_MERGE_TOOL"
175 test -n "$GIT_DIFF_TOOL" && merge_tool="$GIT_DIFF_TOOL"
177 # If merge tool was not specified then use the diff.tool
178 # configuration variable.  If that's invalid then reset merge_tool.
179 # Fallback to merge.tool.
180 if test -z "$merge_tool"; then
181         merge_tool=$(git config diff.tool)
182         test -z "$merge_tool" &&
183         merge_tool=$(git config merge.tool)
184         if test -n "$merge_tool" && ! valid_tool "$merge_tool"; then
185                 echo >&2 "git config option diff.tool set to unknown tool: $merge_tool"
186                 echo >&2 "Resetting to default..."
187                 unset merge_tool
188         fi
189 fi
191 # Try to guess an appropriate merge tool if no tool has been set.
192 if test -z "$merge_tool"; then
193         # We have a $DISPLAY so try some common UNIX merge tools
194         if test -n "$DISPLAY"; then
195                 # If gnome then prefer meld, otherwise, prefer kdiff3 or kompare
196                 if test -n "$GNOME_DESKTOP_SESSION_ID" ; then
197                         merge_tool_candidates="meld kdiff3 kompare tkdiff xxdiff gvimdiff"
198                 else
199                         merge_tool_candidates="kdiff3 kompare tkdiff xxdiff meld gvimdiff"
200                 fi
201         fi
202         if echo "${VISUAL:-$EDITOR}" | grep 'emacs' > /dev/null 2>&1; then
203                 # $EDITOR is emacs so add emerge as a candidate
204                 merge_tool_candidates="$merge_tool_candidates emerge opendiff vimdiff"
205         elif echo "${VISUAL:-$EDITOR}" | grep 'vim' > /dev/null 2>&1; then
206                 # $EDITOR is vim so add vimdiff as a candidate
207                 merge_tool_candidates="$merge_tool_candidates vimdiff opendiff emerge"
208         else
209                 merge_tool_candidates="$merge_tool_candidates opendiff emerge vimdiff"
210         fi
211         echo "merge tool candidates: $merge_tool_candidates"
213         # Loop over each candidate and stop when a valid merge tool is found.
214         for i in $merge_tool_candidates
215         do
216                 init_merge_tool_path $i
217                 if type "$merge_tool_path" > /dev/null 2>&1; then
218                         merge_tool=$i
219                         break
220                 fi
221         done
223         if test -z "$merge_tool" ; then
224                 echo "No known merge resolution program available."
225                 exit 1
226         fi
228 else
229         # A merge tool has been set, so verify that it's valid.
230         if ! valid_tool "$merge_tool"; then
231                 echo >&2 "Unknown merge tool $merge_tool"
232                 exit 1
233         fi
235         init_merge_tool_path "$merge_tool"
237         if test -z "$merge_tool_cmd" && ! type "$merge_tool_path" > /dev/null 2>&1; then
238                 echo "The merge tool $merge_tool is not available as '$merge_tool_path'"
239                 exit 1
240         fi
241 fi
244 # Launch the merge tool on each path provided by 'git diff'
245 while test $# -gt 6
246 do
247         launch_merge_tool "$1" "$2" "$5"
248         shift 7
249 done