Code

grep: NUL terminate input from a file
[git.git] / git-mergetool--lib.sh
1 # git-mergetool--lib is a library for common merge tool functions
2 diff_mode() {
3         test "$TOOL_MODE" = diff
4 }
6 merge_mode() {
7         test "$TOOL_MODE" = merge
8 }
10 translate_merge_tool_path () {
11         case "$1" in
12         vimdiff)
13                 echo vim
14                 ;;
15         gvimdiff)
16                 echo gvim
17                 ;;
18         emerge)
19                 echo emacs
20                 ;;
21         araxis)
22                 echo compare
23                 ;;
24         *)
25                 echo "$1"
26                 ;;
27         esac
28 }
30 check_unchanged () {
31         if test "$MERGED" -nt "$BACKUP"; then
32                 status=0
33         else
34                 while true; do
35                         echo "$MERGED seems unchanged."
36                         printf "Was the merge successful? [y/n] "
37                         read answer < /dev/tty
38                         case "$answer" in
39                         y*|Y*) status=0; break ;;
40                         n*|N*) status=1; break ;;
41                         esac
42                 done
43         fi
44 }
46 valid_tool () {
47         case "$1" in
48         kdiff3 | tkdiff | xxdiff | meld | opendiff | \
49         emerge | vimdiff | gvimdiff | ecmerge | diffuse | araxis)
50                 ;; # happy
51         tortoisemerge)
52                 if ! merge_mode; then
53                         return 1
54                 fi
55                 ;;
56         kompare)
57                 if ! diff_mode; then
58                         return 1
59                 fi
60                 ;;
61         *)
62                 if test -z "$(get_merge_tool_cmd "$1")"; then
63                         return 1
64                 fi
65                 ;;
66         esac
67 }
69 get_merge_tool_cmd () {
70         # Prints the custom command for a merge tool
71         if test -n "$1"; then
72                 merge_tool="$1"
73         else
74                 merge_tool="$(get_merge_tool)"
75         fi
76         if diff_mode; then
77                 echo "$(git config difftool.$merge_tool.cmd ||
78                         git config mergetool.$merge_tool.cmd)"
79         else
80                 echo "$(git config mergetool.$merge_tool.cmd)"
81         fi
82 }
84 run_merge_tool () {
85         merge_tool_path="$(get_merge_tool_path "$1")" || exit
86         base_present="$2"
87         status=0
89         case "$1" in
90         kdiff3)
91                 if merge_mode; then
92                         if $base_present; then
93                                 ("$merge_tool_path" --auto \
94                                         --L1 "$MERGED (Base)" \
95                                         --L2 "$MERGED (Local)" \
96                                         --L3 "$MERGED (Remote)" \
97                                         -o "$MERGED" \
98                                         "$BASE" "$LOCAL" "$REMOTE" \
99                                 > /dev/null 2>&1)
100                         else
101                                 ("$merge_tool_path" --auto \
102                                         --L1 "$MERGED (Local)" \
103                                         --L2 "$MERGED (Remote)" \
104                                         -o "$MERGED" \
105                                         "$LOCAL" "$REMOTE" \
106                                 > /dev/null 2>&1)
107                         fi
108                         status=$?
109                 else
110                         ("$merge_tool_path" --auto \
111                                 --L1 "$MERGED (A)" \
112                                 --L2 "$MERGED (B)" "$LOCAL" "$REMOTE" \
113                         > /dev/null 2>&1)
114                 fi
115                 ;;
116         kompare)
117                 "$merge_tool_path" "$LOCAL" "$REMOTE"
118                 ;;
119         tkdiff)
120                 if merge_mode; then
121                         if $base_present; then
122                                 "$merge_tool_path" -a "$BASE" \
123                                         -o "$MERGED" "$LOCAL" "$REMOTE"
124                         else
125                                 "$merge_tool_path" \
126                                         -o "$MERGED" "$LOCAL" "$REMOTE"
127                         fi
128                         status=$?
129                 else
130                         "$merge_tool_path" "$LOCAL" "$REMOTE"
131                 fi
132                 ;;
133         meld)
134                 if merge_mode; then
135                         touch "$BACKUP"
136                         "$merge_tool_path" "$LOCAL" "$MERGED" "$REMOTE"
137                         check_unchanged
138                 else
139                         "$merge_tool_path" "$LOCAL" "$REMOTE"
140                 fi
141                 ;;
142         diffuse)
143                 if merge_mode; then
144                         touch "$BACKUP"
145                         if $base_present; then
146                                 "$merge_tool_path" \
147                                         "$LOCAL" "$MERGED" "$REMOTE" \
148                                         "$BASE" | cat
149                         else
150                                 "$merge_tool_path" \
151                                         "$LOCAL" "$MERGED" "$REMOTE" | cat
152                         fi
153                         check_unchanged
154                 else
155                         "$merge_tool_path" "$LOCAL" "$REMOTE" | cat
156                 fi
157                 ;;
158         vimdiff)
159                 if merge_mode; then
160                         touch "$BACKUP"
161                         "$merge_tool_path" -d -c "wincmd l" \
162                                 "$LOCAL" "$MERGED" "$REMOTE"
163                         check_unchanged
164                 else
165                         "$merge_tool_path" -d -c "wincmd l" \
166                                 "$LOCAL" "$REMOTE"
167                 fi
168                 ;;
169         gvimdiff)
170                 if merge_mode; then
171                         touch "$BACKUP"
172                         "$merge_tool_path" -d -c "wincmd l" -f \
173                                 "$LOCAL" "$MERGED" "$REMOTE"
174                         check_unchanged
175                 else
176                         "$merge_tool_path" -d -c "wincmd l" -f \
177                                 "$LOCAL" "$REMOTE"
178                 fi
179                 ;;
180         xxdiff)
181                 if merge_mode; then
182                         touch "$BACKUP"
183                         if $base_present; then
184                                 "$merge_tool_path" -X --show-merged-pane \
185                                         -R 'Accel.SaveAsMerged: "Ctrl-S"' \
186                                         -R 'Accel.Search: "Ctrl+F"' \
187                                         -R 'Accel.SearchForward: "Ctrl-G"' \
188                                         --merged-file "$MERGED" \
189                                         "$LOCAL" "$BASE" "$REMOTE"
190                         else
191                                 "$merge_tool_path" -X $extra \
192                                         -R 'Accel.SaveAsMerged: "Ctrl-S"' \
193                                         -R 'Accel.Search: "Ctrl+F"' \
194                                         -R 'Accel.SearchForward: "Ctrl-G"' \
195                                         --merged-file "$MERGED" \
196                                         "$LOCAL" "$REMOTE"
197                         fi
198                         check_unchanged
199                 else
200                         "$merge_tool_path" \
201                                 -R 'Accel.Search: "Ctrl+F"' \
202                                 -R 'Accel.SearchForward: "Ctrl-G"' \
203                                 "$LOCAL" "$REMOTE"
204                 fi
205                 ;;
206         opendiff)
207                 if merge_mode; then
208                         touch "$BACKUP"
209                         if $base_present; then
210                                 "$merge_tool_path" "$LOCAL" "$REMOTE" \
211                                         -ancestor "$BASE" \
212                                         -merge "$MERGED" | cat
213                         else
214                                 "$merge_tool_path" "$LOCAL" "$REMOTE" \
215                                         -merge "$MERGED" | cat
216                         fi
217                         check_unchanged
218                 else
219                         "$merge_tool_path" "$LOCAL" "$REMOTE" | cat
220                 fi
221                 ;;
222         ecmerge)
223                 if merge_mode; then
224                         touch "$BACKUP"
225                         if $base_present; then
226                                 "$merge_tool_path" "$BASE" "$LOCAL" "$REMOTE" \
227                                         --default --mode=merge3 --to="$MERGED"
228                         else
229                                 "$merge_tool_path" "$LOCAL" "$REMOTE" \
230                                         --default --mode=merge2 --to="$MERGED"
231                         fi
232                         check_unchanged
233                 else
234                         "$merge_tool_path" --default --mode=diff2 \
235                                 "$LOCAL" "$REMOTE"
236                 fi
237                 ;;
238         emerge)
239                 if merge_mode; then
240                         if $base_present; then
241                                 "$merge_tool_path" \
242                                         -f emerge-files-with-ancestor-command \
243                                         "$LOCAL" "$REMOTE" "$BASE" \
244                                         "$(basename "$MERGED")"
245                         else
246                                 "$merge_tool_path" \
247                                         -f emerge-files-command \
248                                         "$LOCAL" "$REMOTE" \
249                                         "$(basename "$MERGED")"
250                         fi
251                         status=$?
252                 else
253                         "$merge_tool_path" -f emerge-files-command \
254                                 "$LOCAL" "$REMOTE"
255                 fi
256                 ;;
257         tortoisemerge)
258                 if $base_present; then
259                         touch "$BACKUP"
260                         "$merge_tool_path" \
261                                 -base:"$BASE" -mine:"$LOCAL" \
262                                 -theirs:"$REMOTE" -merged:"$MERGED"
263                         check_unchanged
264                 else
265                         echo "TortoiseMerge cannot be used without a base" 1>&2
266                         status=1
267                 fi
268                 ;;
269         araxis)
270                 if merge_mode; then
271                         touch "$BACKUP"
272                         if $base_present; then
273                                 "$merge_tool_path" -wait -merge -3 -a1 \
274                                         "$BASE" "$LOCAL" "$REMOTE" "$MERGED" \
275                                         >/dev/null 2>&1
276                         else
277                                 "$merge_tool_path" -wait -2 \
278                                         "$LOCAL" "$REMOTE" "$MERGED" \
279                                         >/dev/null 2>&1
280                         fi
281                         check_unchanged
282                 else
283                         "$merge_tool_path" -wait -2 "$LOCAL" "$REMOTE" \
284                                 >/dev/null 2>&1
285                 fi
286                 ;;
287         *)
288                 merge_tool_cmd="$(get_merge_tool_cmd "$1")"
289                 if test -z "$merge_tool_cmd"; then
290                         if merge_mode; then
291                                 status=1
292                         fi
293                         break
294                 fi
295                 if merge_mode; then
296                         trust_exit_code="$(git config --bool \
297                                 mergetool."$1".trustExitCode || echo false)"
298                         if test "$trust_exit_code" = "false"; then
299                                 touch "$BACKUP"
300                                 ( eval $merge_tool_cmd )
301                                 check_unchanged
302                         else
303                                 ( eval $merge_tool_cmd )
304                                 status=$?
305                         fi
306                 else
307                         ( eval $merge_tool_cmd )
308                 fi
309                 ;;
310         esac
311         return $status
314 guess_merge_tool () {
315         if merge_mode; then
316                 tools="tortoisemerge"
317         else
318                 tools="kompare"
319         fi
320         if test -n "$DISPLAY"; then
321                 if test -n "$GNOME_DESKTOP_SESSION_ID" ; then
322                         tools="meld opendiff kdiff3 tkdiff xxdiff $tools"
323                 else
324                         tools="opendiff kdiff3 tkdiff xxdiff meld $tools"
325                 fi
326                 tools="$tools gvimdiff diffuse ecmerge araxis"
327         fi
328         case "${VISUAL:-$EDITOR}" in
329         *vim*)
330                 tools="$tools vimdiff emerge"
331                 ;;
332         *)
333                 tools="$tools emerge vimdiff"
334                 ;;
335         esac
336         echo >&2 "merge tool candidates: $tools"
338         # Loop over each candidate and stop when a valid merge tool is found.
339         for i in $tools
340         do
341                 merge_tool_path="$(translate_merge_tool_path "$i")"
342                 if type "$merge_tool_path" > /dev/null 2>&1; then
343                         echo "$i"
344                         return 0
345                 fi
346         done
348         echo >&2 "No known merge resolution program available."
349         return 1
352 get_configured_merge_tool () {
353         # Diff mode first tries diff.tool and falls back to merge.tool.
354         # Merge mode only checks merge.tool
355         if diff_mode; then
356                 merge_tool=$(git config diff.tool || git config merge.tool)
357         else
358                 merge_tool=$(git config merge.tool)
359         fi
360         if test -n "$merge_tool" && ! valid_tool "$merge_tool"; then
361                 echo >&2 "git config option $TOOL_MODE.tool set to unknown tool: $merge_tool"
362                 echo >&2 "Resetting to default..."
363                 return 1
364         fi
365         echo "$merge_tool"
368 get_merge_tool_path () {
369         # A merge tool has been set, so verify that it's valid.
370         if test -n "$1"; then
371                 merge_tool="$1"
372         else
373                 merge_tool="$(get_merge_tool)"
374         fi
375         if ! valid_tool "$merge_tool"; then
376                 echo >&2 "Unknown merge tool $merge_tool"
377                 exit 1
378         fi
379         if diff_mode; then
380                 merge_tool_path=$(git config difftool."$merge_tool".path ||
381                                   git config mergetool."$merge_tool".path)
382         else
383                 merge_tool_path=$(git config mergetool."$merge_tool".path)
384         fi
385         if test -z "$merge_tool_path"; then
386                 merge_tool_path="$(translate_merge_tool_path "$merge_tool")"
387         fi
388         if test -z "$(get_merge_tool_cmd "$merge_tool")" &&
389         ! type "$merge_tool_path" > /dev/null 2>&1; then
390                 echo >&2 "The $TOOL_MODE tool $merge_tool is not available as"\
391                          "'$merge_tool_path'"
392                 exit 1
393         fi
394         echo "$merge_tool_path"
397 get_merge_tool () {
398         # Check if a merge tool has been configured
399         merge_tool=$(get_configured_merge_tool)
400         # Try to guess an appropriate merge tool if no tool has been set.
401         if test -z "$merge_tool"; then
402                 merge_tool="$(guess_merge_tool)" || exit
403         fi
404         echo "$merge_tool"