Code

0b266e3603fc32d886cc9018af49cc161a065d8b
[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, 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
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         echo
36         cleanup_temp_files
37         exit 1
38 }
40 # This function prepares temporary files and launches the appropriate
41 # merge tool.
42 launch_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"
54         # Create and ensure that we clean up $BACKUP
55         test -f "$MERGED" && cp -- "$MERGED" "$BACKUP"
56         trap sigint_handler SIGINT
58         # $LOCAL and $REMOTE are temporary files so prompt
59         # the user with the real $MERGED name before launching $merge_tool.
60         if should_prompt; then
61                 printf "\nViewing: '$MERGED'\n"
62                 printf "Hit return to launch '%s': " "$merge_tool"
63                 read ans
64         fi
66         # Run the appropriate merge tool command
67         case "$merge_tool" in
68         kdiff3)
69                 basename=$(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                 ;;
77         tkdiff)
78                 "$merge_tool_path" -o "$MERGED" "$LOCAL" "$REMOTE"
79                 ;;
81         meld|vimdiff)
82                 "$merge_tool_path" "$LOCAL" "$REMOTE"
83                 ;;
85         gvimdiff)
86                 "$merge_tool_path" -f "$LOCAL" "$REMOTE"
87                 ;;
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                 ;;
99         opendiff)
100                 "$merge_tool_path" "$LOCAL" "$REMOTE" \
101                         -merge "$MERGED" | cat
102                 ;;
104         ecmerge)
105                 "$merge_tool_path" "$LOCAL" "$REMOTE" \
106                         --default --mode=merge2 --to="$MERGED"
107                 ;;
109         emerge)
110                 "$merge_tool_path" -f emerge-files-command \
111                         "$LOCAL" "$REMOTE" "$(basename "$MERGED")"
112                 ;;
114         *)
115                 if test -n "$merge_tool_cmd"; then
116                         ( eval $merge_tool_cmd )
117                 fi
118                 ;;
119         esac
121         cleanup_temp_files
124 # Verifies that mergetool.<tool>.cmd exists
125 valid_custom_tool() {
126         merge_tool_cmd="$(git config mergetool.$1.cmd)"
127         test -n "$merge_tool_cmd"
130 # Verifies that the chosen merge tool is properly setup.
131 # Built-in merge tools are always valid.
132 valid_tool() {
133         case "$1" in
134         kdiff3 | tkdiff | xxdiff | meld | opendiff | emerge | vimdiff | gvimdiff | ecmerge)
135                 ;; # happy
136         *)
137                 if ! valid_custom_tool "$1"
138                 then
139                         return 1
140                 fi
141                 ;;
142         esac
145 # Sets up the merge_tool_path variable.
146 # This handles the mergetool.<tool>.path configuration.
147 init_merge_tool_path() {
148         merge_tool_path=$(git config mergetool."$1".path)
149         if test -z "$merge_tool_path"; then
150                 case "$1" in
151                 emerge)
152                         merge_tool_path=emacs
153                         ;;
154                 *)
155                         merge_tool_path="$1"
156                         ;;
157                 esac
158         fi
161 # Allow the GIT_MERGE_TOOL variable to provide a default value
162 test -n "$GIT_MERGE_TOOL" && merge_tool="$GIT_MERGE_TOOL"
164 # If not merge tool was specified then use the merge.tool
165 # configuration variable.  If that's invalid then reset merge_tool.
166 if test -z "$merge_tool"; then
167         merge_tool=$(git config merge.tool)
168         if test -n "$merge_tool" && ! valid_tool "$merge_tool"; then
169                 echo >&2 "git config option merge.tool set to unknown tool: $merge_tool"
170                 echo >&2 "Resetting to default..."
171                 unset merge_tool
172         fi
173 fi
175 # Try to guess an appropriate merge tool if no tool has been set.
176 if test -z "$merge_tool"; then
178         # We have a $DISPLAY so try some common UNIX merge tools
179         if test -n "$DISPLAY"; then
180                 merge_tool_candidates="kdiff3 tkdiff xxdiff meld gvimdiff"
181                 # If gnome then prefer meld
182                 if test -n "$GNOME_DESKTOP_SESSION_ID"; then
183                         merge_tool_candidates="meld $merge_tool_candidates"
184                 fi
185                 # If KDE then prefer kdiff3
186                 if test "$KDE_FULL_SESSION" = "true"; then
187                         merge_tool_candidates="kdiff3 $merge_tool_candidates"
188                 fi
189         fi
191         # $EDITOR is emacs so add emerge as a candidate
192         if echo "${VISUAL:-$EDITOR}" | grep 'emacs' > /dev/null 2>&1; then
193                 merge_tool_candidates="$merge_tool_candidates emerge"
194         fi
196         # $EDITOR is vim so add vimdiff as a candidate
197         if echo "${VISUAL:-$EDITOR}" | grep 'vim' > /dev/null 2>&1; then
198                 merge_tool_candidates="$merge_tool_candidates vimdiff"
199         fi
201         merge_tool_candidates="$merge_tool_candidates opendiff emerge vimdiff"
202         echo "merge tool candidates: $merge_tool_candidates"
204         # Loop over each candidate and stop when a valid merge tool is found.
205         for i in $merge_tool_candidates
206         do
207                 init_merge_tool_path $i
208                 if type "$merge_tool_path" > /dev/null 2>&1; then
209                         merge_tool=$i
210                         break
211                 fi
212         done
214         if test -z "$merge_tool" ; then
215                 echo "No known merge resolution program available."
216                 exit 1
217         fi
219 else
220         # A merge tool has been set, so verify that it's valid.
221         if ! valid_tool "$merge_tool"; then
222                 echo >&2 "Unknown merge tool $merge_tool"
223                 exit 1
224         fi
226         init_merge_tool_path "$merge_tool"
228         if test -z "$merge_tool_cmd" && ! type "$merge_tool_path" > /dev/null 2>&1; then
229                 echo "The merge tool $merge_tool is not available as '$merge_tool_path'"
230                 exit 1
231         fi
232 fi
235 # Launch the merge tool on each path provided by 'git diff'
236 while test $# -gt 6
237 do
238         launch_merge_tool "$1" "$2" "$5"
239         shift 7
240 done