contrib/difftool: add support for Kompare
[git/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
9
10 # Set GIT_DIFFTOOL_NO_PROMPT to bypass the per-file prompt.
11 should_prompt () {
12 ! test -n "$GIT_DIFFTOOL_NO_PROMPT"
13 }
14
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 }
20
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 }
32
33 # This is called when users Ctrl-C out of git-difftool-helper
34 sigint_handler () {
35 cleanup_temp_files
36 exit 1
37 }
38
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"
52
53 # Create and ensure that we clean up $BACKUP
54 test -f "$MERGED" && cp -- "$MERGED" "$BACKUP"
55 trap 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.
59 if should_prompt; then
60 printf "\nViewing: '$MERGED'\n"
61 printf "Hit return to launch '%s': " "$merge_tool"
62 read ans
63 fi
64
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 ;;
75
76 kompare)
77 "$merge_tool_path" "$LOCAL" "$REMOTE"
78 ;;
79
80 tkdiff)
81 "$merge_tool_path" -o "$MERGED" "$LOCAL" "$REMOTE"
82 ;;
83
84 meld)
85 "$merge_tool_path" "$LOCAL" "$REMOTE"
86 ;;
87
88 vimdiff)
89 "$merge_tool_path" -c "wincmd l" "$LOCAL" "$REMOTE"
90 ;;
91
92 gvimdiff)
93 "$merge_tool_path" -c "wincmd l" -f "$LOCAL" "$REMOTE"
94 ;;
95
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 ;;
105
106 opendiff)
107 "$merge_tool_path" "$LOCAL" "$REMOTE" \
108 -merge "$MERGED" | cat
109 ;;
110
111 ecmerge)
112 "$merge_tool_path" "$LOCAL" "$REMOTE" \
113 --default --mode=merge2 --to="$MERGED"
114 ;;
115
116 emerge)
117 "$merge_tool_path" -f emerge-files-command \
118 "$LOCAL" "$REMOTE" "$(basename "$MERGED")"
119 ;;
120
121 *)
122 if test -n "$merge_tool_cmd"; then
123 ( eval $merge_tool_cmd )
124 fi
125 ;;
126 esac
127
128 cleanup_temp_files
129 }
130
131 # Verifies that mergetool.<tool>.cmd exists
132 valid_custom_tool() {
133 merge_tool_cmd="$(git config mergetool.$1.cmd)"
134 test -n "$merge_tool_cmd"
135 }
136
137 # Verifies that the chosen merge tool is properly setup.
138 # Built-in merge tools are always valid.
139 valid_tool() {
140 case "$1" in
141 kdiff3 | kompare | tkdiff | xxdiff | meld | opendiff | emerge | vimdiff | gvimdiff | ecmerge)
142 ;; # happy
143 *)
144 if ! valid_custom_tool "$1"
145 then
146 return 1
147 fi
148 ;;
149 esac
150 }
151
152 # Sets up the merge_tool_path variable.
153 # This handles the mergetool.<tool>.path configuration.
154 init_merge_tool_path() {
155 merge_tool_path=$(git config mergetool."$1".path)
156 if test -z "$merge_tool_path"; then
157 case "$1" in
158 emerge)
159 merge_tool_path=emacs
160 ;;
161 *)
162 merge_tool_path="$1"
163 ;;
164 esac
165 fi
166 }
167
168 # Allow the GIT_MERGE_TOOL variable to provide a default value
169 test -n "$GIT_MERGE_TOOL" && merge_tool="$GIT_MERGE_TOOL"
170
171 # If not merge tool was specified then use the merge.tool
172 # configuration variable. If that's invalid then reset merge_tool.
173 if test -z "$merge_tool"; then
174 merge_tool=$(git config merge.tool)
175 if test -n "$merge_tool" && ! valid_tool "$merge_tool"; then
176 echo >&2 "git config option merge.tool set to unknown tool: $merge_tool"
177 echo >&2 "Resetting to default..."
178 unset merge_tool
179 fi
180 fi
181
182 # Try to guess an appropriate merge tool if no tool has been set.
183 if test -z "$merge_tool"; then
184
185 # We have a $DISPLAY so try some common UNIX merge tools
186 if test -n "$DISPLAY"; then
187 merge_tool_candidates="kdiff3 kompare tkdiff xxdiff meld gvimdiff"
188 # If gnome then prefer meld
189 if test -n "$GNOME_DESKTOP_SESSION_ID"; then
190 merge_tool_candidates="meld $merge_tool_candidates"
191 fi
192 # If KDE then prefer kdiff3 or kompare
193 if test "$KDE_FULL_SESSION" = "true"; then
194 merge_tool_candidates="kdiff3 kompare $merge_tool_candidates"
195 fi
196 fi
197
198 # $EDITOR is emacs so add emerge as a candidate
199 if echo "${VISUAL:-$EDITOR}" | grep 'emacs' > /dev/null 2>&1; then
200 merge_tool_candidates="$merge_tool_candidates emerge"
201 fi
202
203 # $EDITOR is vim so add vimdiff as a candidate
204 if echo "${VISUAL:-$EDITOR}" | grep 'vim' > /dev/null 2>&1; then
205 merge_tool_candidates="$merge_tool_candidates vimdiff"
206 fi
207
208 merge_tool_candidates="$merge_tool_candidates opendiff emerge vimdiff"
209 echo "merge tool candidates: $merge_tool_candidates"
210
211 # Loop over each candidate and stop when a valid merge tool is found.
212 for i in $merge_tool_candidates
213 do
214 init_merge_tool_path $i
215 if type "$merge_tool_path" > /dev/null 2>&1; then
216 merge_tool=$i
217 break
218 fi
219 done
220
221 if test -z "$merge_tool" ; then
222 echo "No known merge resolution program available."
223 exit 1
224 fi
225
226 else
227 # A merge tool has been set, so verify that it's valid.
228 if ! valid_tool "$merge_tool"; then
229 echo >&2 "Unknown merge tool $merge_tool"
230 exit 1
231 fi
232
233 init_merge_tool_path "$merge_tool"
234
235 if test -z "$merge_tool_cmd" && ! type "$merge_tool_path" > /dev/null 2>&1; then
236 echo "The merge tool $merge_tool is not available as '$merge_tool_path'"
237 exit 1
238 fi
239 fi
240
241
242 # Launch the merge tool on each path provided by 'git diff'
243 while test $# -gt 6
244 do
245 launch_merge_tool "$1" "$2" "$5"
246 shift 7
247 done