Code

Fix misspelled mergetool.keepBackup
[git.git] / git-mergetool.sh
1 #!/bin/sh
2 #
3 # This program resolves merge conflicts in git
4 #
5 # Copyright (c) 2006 Theodore Y. Ts'o
6 #
7 # This file is licensed under the GPL v2, or a later version
8 # at the discretion of Junio C Hamano.
9 #
11 USAGE='[--tool=tool] [-y|--no-prompt|--prompt] [file to merge] ...'
12 SUBDIRECTORY_OK=Yes
13 OPTIONS_SPEC=
14 TOOL_MODE=merge
15 . git-sh-setup
16 . git-mergetool--lib
17 require_work_tree
19 # Returns true if the mode reflects a symlink
20 is_symlink () {
21     test "$1" = 120000
22 }
24 local_present () {
25     test -n "$local_mode"
26 }
28 remote_present () {
29     test -n "$remote_mode"
30 }
32 base_present () {
33     test -n "$base_mode"
34 }
36 cleanup_temp_files () {
37     if test "$1" = --save-backup ; then
38         mv -- "$BACKUP" "$MERGED.orig"
39         rm -f -- "$LOCAL" "$REMOTE" "$BASE"
40     else
41         rm -f -- "$LOCAL" "$REMOTE" "$BASE" "$BACKUP"
42     fi
43 }
45 describe_file () {
46     mode="$1"
47     branch="$2"
48     file="$3"
50     printf "  {%s}: " "$branch"
51     if test -z "$mode"; then
52         echo "deleted"
53     elif is_symlink "$mode" ; then
54         echo "a symbolic link -> '$(cat "$file")'"
55     else
56         if base_present; then
57             echo "modified"
58         else
59             echo "created"
60         fi
61     fi
62 }
65 resolve_symlink_merge () {
66     while true; do
67         printf "Use (l)ocal or (r)emote, or (a)bort? "
68         read ans
69         case "$ans" in
70             [lL]*)
71                 git checkout-index -f --stage=2 -- "$MERGED"
72                 git add -- "$MERGED"
73                 cleanup_temp_files --save-backup
74                 return 0
75                 ;;
76             [rR]*)
77                 git checkout-index -f --stage=3 -- "$MERGED"
78                 git add -- "$MERGED"
79                 cleanup_temp_files --save-backup
80                 return 0
81                 ;;
82             [aA]*)
83                 return 1
84                 ;;
85             esac
86         done
87 }
89 resolve_deleted_merge () {
90     while true; do
91         if base_present; then
92             printf "Use (m)odified or (d)eleted file, or (a)bort? "
93         else
94             printf "Use (c)reated or (d)eleted file, or (a)bort? "
95         fi
96         read ans
97         case "$ans" in
98             [mMcC]*)
99                 git add -- "$MERGED"
100                 cleanup_temp_files --save-backup
101                 return 0
102                 ;;
103             [dD]*)
104                 git rm -- "$MERGED" > /dev/null
105                 cleanup_temp_files
106                 return 0
107                 ;;
108             [aA]*)
109                 return 1
110                 ;;
111             esac
112         done
115 checkout_staged_file () {
116     tmpfile=$(expr "$(git checkout-index --temp --stage="$1" "$2")" : '\([^     ]*\)    ')
118     if test $? -eq 0 -a -n "$tmpfile" ; then
119         mv -- "$(git rev-parse --show-cdup)$tmpfile" "$3"
120     fi
123 merge_file () {
124     MERGED="$1"
126     f=$(git ls-files -u -- "$MERGED")
127     if test -z "$f" ; then
128         if test ! -f "$MERGED" ; then
129             echo "$MERGED: file not found"
130         else
131             echo "$MERGED: file does not need merging"
132         fi
133         return 1
134     fi
136     ext="$$$(expr "$MERGED" : '.*\(\.[^/]*\)$')"
137     BACKUP="./$MERGED.BACKUP.$ext"
138     LOCAL="./$MERGED.LOCAL.$ext"
139     REMOTE="./$MERGED.REMOTE.$ext"
140     BASE="./$MERGED.BASE.$ext"
142     mv -- "$MERGED" "$BACKUP"
143     cp -- "$BACKUP" "$MERGED"
145     base_mode=$(git ls-files -u -- "$MERGED" | awk '{if ($3==1) print $1;}')
146     local_mode=$(git ls-files -u -- "$MERGED" | awk '{if ($3==2) print $1;}')
147     remote_mode=$(git ls-files -u -- "$MERGED" | awk '{if ($3==3) print $1;}')
149     base_present   && checkout_staged_file 1 "$MERGED" "$BASE"
150     local_present  && checkout_staged_file 2 "$MERGED" "$LOCAL"
151     remote_present && checkout_staged_file 3 "$MERGED" "$REMOTE"
153     if test -z "$local_mode" -o -z "$remote_mode"; then
154         echo "Deleted merge conflict for '$MERGED':"
155         describe_file "$local_mode" "local" "$LOCAL"
156         describe_file "$remote_mode" "remote" "$REMOTE"
157         resolve_deleted_merge
158         return
159     fi
161     if is_symlink "$local_mode" || is_symlink "$remote_mode"; then
162         echo "Symbolic link merge conflict for '$MERGED':"
163         describe_file "$local_mode" "local" "$LOCAL"
164         describe_file "$remote_mode" "remote" "$REMOTE"
165         resolve_symlink_merge
166         return
167     fi
169     echo "Normal merge conflict for '$MERGED':"
170     describe_file "$local_mode" "local" "$LOCAL"
171     describe_file "$remote_mode" "remote" "$REMOTE"
172     if "$prompt" = true; then
173         printf "Hit return to start merge resolution tool (%s): " "$merge_tool"
174         read ans
175     fi
177     present=false
178     base_present &&
179     present=true
181     if ! run_merge_tool "$merge_tool" "$present"; then
182         echo "merge of $MERGED failed" 1>&2
183         mv -- "$BACKUP" "$MERGED"
185         if test "$merge_keep_temporaries" = "false"; then
186             cleanup_temp_files
187         fi
189         return 1
190     fi
192     if test "$merge_keep_backup" = "true"; then
193         mv -- "$BACKUP" "$MERGED.orig"
194     else
195         rm -- "$BACKUP"
196     fi
198     git add -- "$MERGED"
199     cleanup_temp_files
200     return 0
203 prompt=$(git config --bool mergetool.prompt || echo true)
205 while test $# != 0
206 do
207     case "$1" in
208         -t|--tool*)
209             case "$#,$1" in
210                 *,*=*)
211                     merge_tool=$(expr "z$1" : 'z-[^=]*=\(.*\)')
212                     ;;
213                 1,*)
214                     usage ;;
215                 *)
216                     merge_tool="$2"
217                     shift ;;
218             esac
219             ;;
220         -y|--no-prompt)
221             prompt=false
222             ;;
223         --prompt)
224             prompt=true
225             ;;
226         --)
227             shift
228             break
229             ;;
230         -*)
231             usage
232             ;;
233         *)
234             break
235             ;;
236     esac
237     shift
238 done
240 prompt_after_failed_merge() {
241     while true; do
242         printf "Continue merging other unresolved paths (y/n) ? "
243         read ans
244         case "$ans" in
246             [yY]*)
247                 return 0
248                 ;;
250             [nN]*)
251                 return 1
252                 ;;
253         esac
254     done
257 merge_tool=$(get_merge_tool "$merge_tool") || exit
258 merge_tool_cmd="$(get_merge_tool_cmd "$merge_tool")"
259 merge_tool_path="$(get_merge_tool_path "$merge_tool")" || exit
260 merge_keep_backup="$(git config --bool mergetool.keepBackup || echo true)"
261 merge_keep_temporaries="$(git config --bool mergetool.keepTemporaries || echo false)"
262 merge_tool_trust_exit_code="$(git config --bool mergetool."$merge_tool".trustExitCode || echo false)"
264 last_status=0
265 rollup_status=0
267 if test $# -eq 0 ; then
268     files=$(git ls-files -u | sed -e 's/^[^     ]*      //' | sort -u)
269     if test -z "$files" ; then
270         echo "No files need merging"
271         exit 0
272     fi
273     echo Merging the files: "$files"
274     git ls-files -u |
275     sed -e 's/^[^       ]*      //' |
276     sort -u |
277     while IFS= read i
278     do
279         if test $last_status -ne 0; then
280             prompt_after_failed_merge < /dev/tty || exit 1
281         fi
282         printf "\n"
283         merge_file "$i" < /dev/tty > /dev/tty
284         last_status=$?
285         if test $last_status -ne 0; then
286             rollup_status=1
287         fi
288     done
289 else
290     while test $# -gt 0; do
291         if test $last_status -ne 0; then
292             prompt_after_failed_merge || exit 1
293         fi
294         printf "\n"
295         merge_file "$1"
296         last_status=$?
297         if test $last_status -ne 0; then
298             rollup_status=1
299         fi
300         shift
301     done
302 fi
304 exit $rollup_status