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] ...'
19 # Returns true if the mode reflects a symlink
33 test -n "$remote_mode"
40 cleanup_temp_files
() {
41 if test "$1" = --save-backup
43 rm -rf -- "$MERGED.orig"
44 test -e "$BACKUP" && mv -- "$BACKUP" "$MERGED.orig"
45 rm -f -- "$LOCAL" "$REMOTE" "$BASE"
47 rm -f -- "$LOCAL" "$REMOTE" "$BASE" "$BACKUP"
56 printf " {%s}: " "$branch"
60 elif is_symlink
"$mode"
62 echo "a symbolic link -> '$(cat "$file")'"
63 elif is_submodule
"$mode"
65 echo "submodule commit $file"
74 resolve_symlink_merge
() {
77 printf "Use (l)ocal or (r)emote, or (a)bort? "
81 git checkout-index
-f --stage=2 -- "$MERGED"
83 cleanup_temp_files
--save-backup
87 git checkout-index
-f --stage=3 -- "$MERGED"
89 cleanup_temp_files
--save-backup
99 resolve_deleted_merge
() {
104 printf "Use (m)odified or (d)eleted file, or (a)bort? "
106 printf "Use (c)reated or (d)eleted file, or (a)bort? "
112 cleanup_temp_files
--save-backup
116 git
rm -- "$MERGED" > /dev
/null
127 resolve_submodule_merge
() {
130 printf "Use (l)ocal or (r)emote, or (a)bort? "
136 if test -n "$(git ls-tree HEAD -- "$MERGED")"
138 # Local isn't present, but it's a subdirectory
139 git ls-tree
--full-name -r HEAD
-- "$MERGED" |
140 git update-index
--index-info ||
exit $?
142 test -e "$MERGED" && mv -- "$MERGED" "$BACKUP"
143 git update-index
--force-remove "$MERGED"
144 cleanup_temp_files
--save-backup
146 elif is_submodule
"$local_mode"
148 stage_submodule
"$MERGED" "$local_sha1"
150 git checkout-index
-f --stage=2 -- "$MERGED"
158 if test -n "$(git ls-tree MERGE_HEAD -- "$MERGED")"
160 # Remote isn't present, but it's a subdirectory
161 git ls-tree
--full-name -r MERGE_HEAD
-- "$MERGED" |
162 git update-index
--index-info ||
exit $?
164 test -e "$MERGED" && mv -- "$MERGED" "$BACKUP"
165 git update-index
--force-remove "$MERGED"
167 elif is_submodule
"$remote_mode"
169 ! is_submodule
"$local_mode" &&
171 mv -- "$MERGED" "$BACKUP"
172 stage_submodule
"$MERGED" "$remote_sha1"
174 test -e "$MERGED" && mv -- "$MERGED" "$BACKUP"
175 git checkout-index
-f --stage=3 -- "$MERGED"
178 cleanup_temp_files
--save-backup
192 die
"fatal: unable to create directory for module at $path"
193 # Find $path relative to work tree
194 work_tree_root
=$
(cd_to_toplevel
&& pwd)
195 work_rel_path
=$
(cd "$path" &&
196 GIT_WORK_TREE
="${work_tree_root}" git rev-parse
--show-prefix
198 test -n "$work_rel_path" ||
199 die
"fatal: unable to get path of module $path relative to work tree"
200 git update-index
--add --replace --cacheinfo 160000 "$submodule_sha1" "${work_rel_path%/}" || die
203 checkout_staged_file
() {
205 "$(git checkout-index --temp --stage="$1" "$2" 2>/dev/null)" \
208 if test $?
-eq 0 -a -n "$tmpfile"
210 mv -- "$(git rev-parse --show-cdup)$tmpfile" "$3"
219 f
=$
(git ls-files
-u -- "$MERGED")
222 if test ! -f "$MERGED"
224 echo "$MERGED: file not found"
226 echo "$MERGED: file does not need merging"
231 ext
="$$$(expr "$MERGED" : '.*\(\.[^/]*\)$')"
232 BACKUP
="./$MERGED.BACKUP.$ext"
233 LOCAL
="./$MERGED.LOCAL.$ext"
234 REMOTE
="./$MERGED.REMOTE.$ext"
235 BASE
="./$MERGED.BASE.$ext"
237 base_mode
=$
(git ls-files
-u -- "$MERGED" |
awk '{if ($3==1) print $1;}')
238 local_mode
=$
(git ls-files
-u -- "$MERGED" |
awk '{if ($3==2) print $1;}')
239 remote_mode
=$
(git ls-files
-u -- "$MERGED" |
awk '{if ($3==3) print $1;}')
241 if is_submodule
"$local_mode" || is_submodule
"$remote_mode"
243 echo "Submodule merge conflict for '$MERGED':"
244 local_sha1
=$
(git ls-files
-u -- "$MERGED" |
awk '{if ($3==2) print $2;}')
245 remote_sha1
=$
(git ls-files
-u -- "$MERGED" |
awk '{if ($3==3) print $2;}')
246 describe_file
"$local_mode" "local" "$local_sha1"
247 describe_file
"$remote_mode" "remote" "$remote_sha1"
248 resolve_submodule_merge
252 mv -- "$MERGED" "$BACKUP"
253 cp -- "$BACKUP" "$MERGED"
255 checkout_staged_file
1 "$MERGED" "$BASE"
256 checkout_staged_file
2 "$MERGED" "$LOCAL"
257 checkout_staged_file
3 "$MERGED" "$REMOTE"
259 if test -z "$local_mode" -o -z "$remote_mode"
261 echo "Deleted merge conflict for '$MERGED':"
262 describe_file
"$local_mode" "local" "$LOCAL"
263 describe_file
"$remote_mode" "remote" "$REMOTE"
264 resolve_deleted_merge
268 if is_symlink
"$local_mode" || is_symlink
"$remote_mode"
270 echo "Symbolic link merge conflict for '$MERGED':"
271 describe_file
"$local_mode" "local" "$LOCAL"
272 describe_file
"$remote_mode" "remote" "$REMOTE"
273 resolve_symlink_merge
277 echo "Normal merge conflict for '$MERGED':"
278 describe_file
"$local_mode" "local" "$LOCAL"
279 describe_file
"$remote_mode" "remote" "$REMOTE"
282 printf "Hit return to start merge resolution tool (%s): " "$merge_tool"
293 if ! run_merge_tool
"$merge_tool" "$present"
295 echo "merge of $MERGED failed" 1>&2
296 mv -- "$BACKUP" "$MERGED"
298 if test "$merge_keep_temporaries" = "false"
306 if test "$merge_keep_backup" = "true"
308 mv -- "$BACKUP" "$MERGED.orig"
320 list_merge_tool_candidates
321 unavailable
= available
= LF
='
325 merge_tool_path
=$
(translate_merge_tool_path
"$i")
326 if type "$merge_tool_path" >/dev
/null
2>&1
328 available
="$available$i$LF"
330 unavailable
="$unavailable$i$LF"
333 if test -n "$available"
335 echo "'git mergetool --tool=<tool>' may be set to one of the following:"
336 echo "$available" |
sort |
sed -e 's/^/ /'
338 echo "No suitable tool for 'git mergetool --tool=<tool>' found."
340 if test -n "$unavailable"
343 echo 'The following tools are valid, but not currently available:'
344 echo "$unavailable" |
sort |
sed -e 's/^/ /'
346 if test -n "$unavailable$available"
349 echo "Some of the tools listed above only work in a windowed"
350 echo "environment. If run in a terminal-only session, they will fail."
355 prompt
=$
(git config
--bool mergetool.prompt ||
echo true
)
366 merge_tool
=$
(expr "z$1" : 'z-[^=]*=\(.*\)')
395 prompt_after_failed_merge
() {
398 printf "Continue merging other unresolved paths (y/n) ? "
411 if test -z "$merge_tool"
413 merge_tool
=$
(get_merge_tool
"$merge_tool") ||
exit
415 merge_keep_backup
="$(git config --bool mergetool.keepBackup || echo true)"
416 merge_keep_temporaries
="$(git config --bool mergetool.keepTemporaries || echo false)"
426 if test -e "$GIT_DIR/MERGE_RR"
428 files
=$
(git rerere remaining
)
430 files
=$
(git ls-files
-u |
sed -e 's/^[^ ]* //' |
sort -u)
433 files
=$
(git ls-files
-u -- "$@" |
sed -e 's/^[^ ]* //' |
sort -u)
438 echo "No files need merging"
449 if test $last_status -ne 0
451 prompt_after_failed_merge ||
exit 1
456 if test $last_status -ne 0