3 # This program resolves merge conflicts in git
5 # Copyright (c) 2006 Theodore Y. Ts'o
7 # This file is licensed under the GPL v2, or a later version
8 # at the discretion of Junio C Hamano.
11 USAGE
='[--tool=tool] [--tool-help] [-y|--no-prompt|--prompt] [file to merge] ...'
18 # Returns true if the mode reflects a symlink
32 test -n "$remote_mode"
39 cleanup_temp_files
() {
40 if test "$1" = --save-backup
42 rm -rf -- "$MERGED.orig"
43 test -e "$BACKUP" && mv -- "$BACKUP" "$MERGED.orig"
44 rm -f -- "$LOCAL" "$REMOTE" "$BASE"
46 rm -f -- "$LOCAL" "$REMOTE" "$BASE" "$BACKUP"
55 printf " {%s}: " "$branch"
59 elif is_symlink
"$mode"
61 echo "a symbolic link -> '$(cat "$file")'"
62 elif is_submodule
"$mode"
64 echo "submodule commit $file"
73 resolve_symlink_merge
() {
76 printf "Use (l)ocal or (r)emote, or (a)bort? "
80 git checkout-index
-f --stage=2 -- "$MERGED"
82 cleanup_temp_files
--save-backup
86 git checkout-index
-f --stage=3 -- "$MERGED"
88 cleanup_temp_files
--save-backup
98 resolve_deleted_merge
() {
103 printf "Use (m)odified or (d)eleted file, or (a)bort? "
105 printf "Use (c)reated or (d)eleted file, or (a)bort? "
111 cleanup_temp_files
--save-backup
115 git
rm -- "$MERGED" > /dev
/null
126 resolve_submodule_merge
() {
129 printf "Use (l)ocal or (r)emote, or (a)bort? "
135 if test -n "$(git ls-tree HEAD -- "$MERGED")"
137 # Local isn't present, but it's a subdirectory
138 git ls-tree
--full-name -r HEAD
-- "$MERGED" |
139 git update-index
--index-info ||
exit $?
141 test -e "$MERGED" && mv -- "$MERGED" "$BACKUP"
142 git update-index
--force-remove "$MERGED"
143 cleanup_temp_files
--save-backup
145 elif is_submodule
"$local_mode"
147 stage_submodule
"$MERGED" "$local_sha1"
149 git checkout-index
-f --stage=2 -- "$MERGED"
157 if test -n "$(git ls-tree MERGE_HEAD -- "$MERGED")"
159 # Remote isn't present, but it's a subdirectory
160 git ls-tree
--full-name -r MERGE_HEAD
-- "$MERGED" |
161 git update-index
--index-info ||
exit $?
163 test -e "$MERGED" && mv -- "$MERGED" "$BACKUP"
164 git update-index
--force-remove "$MERGED"
166 elif is_submodule
"$remote_mode"
168 ! is_submodule
"$local_mode" &&
170 mv -- "$MERGED" "$BACKUP"
171 stage_submodule
"$MERGED" "$remote_sha1"
173 test -e "$MERGED" && mv -- "$MERGED" "$BACKUP"
174 git checkout-index
-f --stage=3 -- "$MERGED"
177 cleanup_temp_files
--save-backup
191 die
"fatal: unable to create directory for module at $path"
192 # Find $path relative to work tree
193 work_tree_root
=$
(cd_to_toplevel
&& pwd)
194 work_rel_path
=$
(cd "$path" &&
195 GIT_WORK_TREE
="${work_tree_root}" git rev-parse
--show-prefix
197 test -n "$work_rel_path" ||
198 die
"fatal: unable to get path of module $path relative to work tree"
199 git update-index
--add --replace --cacheinfo 160000 "$submodule_sha1" "${work_rel_path%/}" || die
202 checkout_staged_file
() {
204 "$(git checkout-index --temp --stage="$1" "$2" 2>/dev/null)" \
207 if test $?
-eq 0 && test -n "$tmpfile"
209 mv -- "$(git rev-parse --show-cdup)$tmpfile" "$3"
218 f
=$
(git ls-files
-u -- "$MERGED")
221 if test ! -f "$MERGED"
223 echo "$MERGED: file not found"
225 echo "$MERGED: file does not need merging"
230 ext
="$$$(expr "$MERGED" : '.*\(\.[^/]*\)$')"
231 BACKUP
="./$MERGED.BACKUP.$ext"
232 LOCAL
="./$MERGED.LOCAL.$ext"
233 REMOTE
="./$MERGED.REMOTE.$ext"
234 BASE
="./$MERGED.BASE.$ext"
236 base_mode
=$
(git ls-files
-u -- "$MERGED" |
awk '{if ($3==1) print $1;}')
237 local_mode
=$
(git ls-files
-u -- "$MERGED" |
awk '{if ($3==2) print $1;}')
238 remote_mode
=$
(git ls-files
-u -- "$MERGED" |
awk '{if ($3==3) print $1;}')
240 if is_submodule
"$local_mode" || is_submodule
"$remote_mode"
242 echo "Submodule merge conflict for '$MERGED':"
243 local_sha1
=$
(git ls-files
-u -- "$MERGED" |
awk '{if ($3==2) print $2;}')
244 remote_sha1
=$
(git ls-files
-u -- "$MERGED" |
awk '{if ($3==3) print $2;}')
245 describe_file
"$local_mode" "local" "$local_sha1"
246 describe_file
"$remote_mode" "remote" "$remote_sha1"
247 resolve_submodule_merge
251 mv -- "$MERGED" "$BACKUP"
252 cp -- "$BACKUP" "$MERGED"
254 checkout_staged_file
1 "$MERGED" "$BASE"
255 checkout_staged_file
2 "$MERGED" "$LOCAL"
256 checkout_staged_file
3 "$MERGED" "$REMOTE"
258 if test -z "$local_mode" ||
test -z "$remote_mode"
260 echo "Deleted merge conflict for '$MERGED':"
261 describe_file
"$local_mode" "local" "$LOCAL"
262 describe_file
"$remote_mode" "remote" "$REMOTE"
263 resolve_deleted_merge
267 if is_symlink
"$local_mode" || is_symlink
"$remote_mode"
269 echo "Symbolic link merge conflict for '$MERGED':"
270 describe_file
"$local_mode" "local" "$LOCAL"
271 describe_file
"$remote_mode" "remote" "$REMOTE"
272 resolve_symlink_merge
276 echo "Normal merge conflict for '$MERGED':"
277 describe_file
"$local_mode" "local" "$LOCAL"
278 describe_file
"$remote_mode" "remote" "$REMOTE"
279 if test "$guessed_merge_tool" = true ||
test "$prompt" = true
281 printf "Hit return to start merge resolution tool (%s): " "$merge_tool"
292 if ! run_merge_tool
"$merge_tool" "$present"
294 echo "merge of $MERGED failed" 1>&2
295 mv -- "$BACKUP" "$MERGED"
297 if test "$merge_keep_temporaries" = "false"
305 if test "$merge_keep_backup" = "true"
307 mv -- "$BACKUP" "$MERGED.orig"
317 prompt
=$
(git config
--bool mergetool.prompt
)
318 guessed_merge_tool
=false
326 TOOL_MODE
=$
(expr "z$1" : 'z-[^=]*=\(.*\)')
339 merge_tool
=$
(expr "z$1" : 'z-[^=]*=\(.*\)')
368 prompt_after_failed_merge
() {
371 printf "Continue merging other unresolved paths (y/n) ? "
386 if test -z "$merge_tool"
388 # Check if a merge tool has been configured
389 merge_tool
=$
(get_configured_merge_tool
)
390 # Try to guess an appropriate merge tool if no tool has been set.
391 if test -z "$merge_tool"
393 merge_tool
=$
(guess_merge_tool
) ||
exit
394 guessed_merge_tool
=true
397 merge_keep_backup
="$(git config --bool mergetool.keepBackup || echo true)"
398 merge_keep_temporaries
="$(git config --bool mergetool.keepTemporaries || echo false)"
408 if test -e "$GIT_DIR/MERGE_RR"
410 files
=$
(git rerere remaining
)
412 files
=$
(git ls-files
-u |
sed -e 's/^[^ ]* //' |
sort -u)
415 files
=$
(git ls-files
-u -- "$@" |
sed -e 's/^[^ ]* //' |
sort -u)
420 echo "No files need merging"
425 printf "%s\n" "$files"
431 if test $last_status -ne 0
433 prompt_after_failed_merge ||
exit 1
438 if test $last_status -ne 0