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"
216 tmpsuffix
=$
(git config mergetool.tmpsuffix || true
)
220 f
=$
(git ls-files
-u -- "$MERGED")
223 if test ! -f "$MERGED"
225 echo "$MERGED: file not found"
227 echo "$MERGED: file does not need merging"
232 ext
="$$$(expr "$MERGED" : '.*\(\.[^/]*\)$')"
233 BACKUP
="./$MERGED.BACKUP.$ext$tmpsuffix"
234 LOCAL
="./$MERGED.LOCAL.$ext$tmpsuffix"
235 REMOTE
="./$MERGED.REMOTE.$ext$tmpsuffix"
236 BASE
="./$MERGED.BASE.$ext$tmpsuffix"
238 base_mode
=$
(git ls-files
-u -- "$MERGED" |
awk '{if ($3==1) print $1;}')
239 local_mode
=$
(git ls-files
-u -- "$MERGED" |
awk '{if ($3==2) print $1;}')
240 remote_mode
=$
(git ls-files
-u -- "$MERGED" |
awk '{if ($3==3) print $1;}')
242 if is_submodule
"$local_mode" || is_submodule
"$remote_mode"
244 echo "Submodule merge conflict for '$MERGED':"
245 local_sha1
=$
(git ls-files
-u -- "$MERGED" |
awk '{if ($3==2) print $2;}')
246 remote_sha1
=$
(git ls-files
-u -- "$MERGED" |
awk '{if ($3==3) print $2;}')
247 describe_file
"$local_mode" "local" "$local_sha1"
248 describe_file
"$remote_mode" "remote" "$remote_sha1"
249 resolve_submodule_merge
253 mv -- "$MERGED" "$BACKUP"
254 cp -- "$BACKUP" "$MERGED"
256 checkout_staged_file
1 "$MERGED" "$BASE"
257 checkout_staged_file
2 "$MERGED" "$LOCAL"
258 checkout_staged_file
3 "$MERGED" "$REMOTE"
260 if test -z "$local_mode" ||
test -z "$remote_mode"
262 echo "Deleted merge conflict for '$MERGED':"
263 describe_file
"$local_mode" "local" "$LOCAL"
264 describe_file
"$remote_mode" "remote" "$REMOTE"
265 resolve_deleted_merge
269 if is_symlink
"$local_mode" || is_symlink
"$remote_mode"
271 echo "Symbolic link merge conflict for '$MERGED':"
272 describe_file
"$local_mode" "local" "$LOCAL"
273 describe_file
"$remote_mode" "remote" "$REMOTE"
274 resolve_symlink_merge
278 echo "Normal merge conflict for '$MERGED':"
279 describe_file
"$local_mode" "local" "$LOCAL"
280 describe_file
"$remote_mode" "remote" "$REMOTE"
281 if test "$guessed_merge_tool" = true ||
test "$prompt" = true
283 printf "Hit return to start merge resolution tool (%s): " "$merge_tool"
294 if ! run_merge_tool
"$merge_tool" "$present"
296 echo "merge of $MERGED failed" 1>&2
297 mv -- "$BACKUP" "$MERGED"
299 if test "$merge_keep_temporaries" = "false"
307 if test "$merge_keep_backup" = "true"
309 mv -- "$BACKUP" "$MERGED.orig"
319 prompt
=$
(git config
--bool mergetool.prompt
)
320 guessed_merge_tool
=false
328 TOOL_MODE
=$
(expr "z$1" : 'z-[^=]*=\(.*\)')
341 merge_tool
=$
(expr "z$1" : 'z-[^=]*=\(.*\)')
370 prompt_after_failed_merge
() {
373 printf "Continue merging other unresolved paths (y/n) ? "
388 if test -z "$merge_tool"
390 # Check if a merge tool has been configured
391 merge_tool
=$
(get_configured_merge_tool
)
392 # Try to guess an appropriate merge tool if no tool has been set.
393 if test -z "$merge_tool"
395 merge_tool
=$
(guess_merge_tool
) ||
exit
396 guessed_merge_tool
=true
399 merge_keep_backup
="$(git config --bool mergetool.keepBackup || echo true)"
400 merge_keep_temporaries
="$(git config --bool mergetool.keepTemporaries || echo false)"
410 if test -e "$GIT_DIR/MERGE_RR"
412 files
=$
(git rerere remaining
)
414 files
=$
(git ls-files
-u |
sed -e 's/^[^ ]* //' |
sort -u)
417 files
=$
(git ls-files
-u -- "$@" |
sed -e 's/^[^ ]* //' |
sort -u)
422 echo "No files need merging"
427 printf "%s\n" "$files"
433 if test $last_status -ne 0
435 prompt_after_failed_merge ||
exit 1
440 if test $last_status -ne 0