After git-apply fails, attempt to find a base tree that the patch cleanly applies to, and do a three-way merge using that base tree into the current index, if .dotest/.3way file exists. This flag can be controlled by giving -m flag to git-applymbox command. When the fall-back merge fails, the working tree can be resolved the same way as you would normally hand resolve a conflicting merge. When making commit, use .dotest/final-commit as the log message template. Or you could just choose to 'git-checkout-index -f -a' to revert the failed merge. Signed-off-by: Junio C Hamano <junkio@cox.net>
		
			
				
	
	
		
			198 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			198 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
#!/bin/sh
 | 
						|
##
 | 
						|
## applypatch takes four file arguments, and uses those to
 | 
						|
## apply the unpacked patch (surprise surprise) that they
 | 
						|
## represent to the current tree.
 | 
						|
##
 | 
						|
## The arguments are:
 | 
						|
##	$1 - file with commit message
 | 
						|
##	$2 - file with the actual patch
 | 
						|
##	$3 - "info" file with Author, email and subject
 | 
						|
##	$4 - optional file containing signoff to add
 | 
						|
##
 | 
						|
. git-sh-setup || die "Not a git archive."
 | 
						|
 | 
						|
final=.dotest/final-commit
 | 
						|
##
 | 
						|
## If this file exists, we ask before applying
 | 
						|
##
 | 
						|
query_apply=.dotest/.query_apply
 | 
						|
 | 
						|
## We do not munge the first line of the commit message too much
 | 
						|
## if this file exists.
 | 
						|
keep_subject=.dotest/.keep_subject
 | 
						|
 | 
						|
## We do not attempt the 3-way merge fallback unless this file exists.
 | 
						|
fall_back_3way=.dotest/.3way
 | 
						|
 | 
						|
MSGFILE=$1
 | 
						|
PATCHFILE=$2
 | 
						|
INFO=$3
 | 
						|
SIGNOFF=$4
 | 
						|
EDIT=${VISUAL:-${EDITOR:-vi}}
 | 
						|
 | 
						|
export GIT_AUTHOR_NAME="$(sed -n '/^Author/ s/Author: //p' "$INFO")"
 | 
						|
export GIT_AUTHOR_EMAIL="$(sed -n '/^Email/ s/Email: //p' "$INFO")"
 | 
						|
export GIT_AUTHOR_DATE="$(sed -n '/^Date/ s/Date: //p' "$INFO")"
 | 
						|
export SUBJECT="$(sed -n '/^Subject/ s/Subject: //p' "$INFO")"
 | 
						|
 | 
						|
if test '' != "$SIGNOFF"
 | 
						|
then
 | 
						|
	if test -f "$SIGNOFF"
 | 
						|
	then
 | 
						|
		SIGNOFF=`cat "$SIGNOFF"` || exit
 | 
						|
	elif case "$SIGNOFF" in yes | true | me | please) : ;; *) false ;; esac
 | 
						|
	then
 | 
						|
		SIGNOFF=`git-var GIT_COMMITTER_IDENT | sed -e '
 | 
						|
				s/>.*/>/
 | 
						|
				s/^/Signed-off-by: /'
 | 
						|
		`
 | 
						|
	else
 | 
						|
		SIGNOFF=
 | 
						|
	fi
 | 
						|
	if test '' != "$SIGNOFF"
 | 
						|
	then
 | 
						|
		LAST_SIGNED_OFF_BY=`
 | 
						|
			sed -ne '/^Signed-off-by: /p' "$MSGFILE" |
 | 
						|
			tail -n 1
 | 
						|
		`
 | 
						|
		test "$LAST_SIGNED_OFF_BY" = "$SIGNOFF" || {
 | 
						|
		    test '' = "$LAST_SIGNED_OFF_BY" && echo
 | 
						|
		    echo "$SIGNOFF"
 | 
						|
		} >>"$MSGFILE"
 | 
						|
	fi
 | 
						|
fi
 | 
						|
 | 
						|
patch_header=
 | 
						|
test -f "$keep_subject" || patch_header='[PATCH] '
 | 
						|
 | 
						|
{
 | 
						|
	echo "$patch_header$SUBJECT"
 | 
						|
	if test -s "$MSGFILE"
 | 
						|
	then
 | 
						|
		echo
 | 
						|
		cat "$MSGFILE"
 | 
						|
	fi
 | 
						|
} >"$final"
 | 
						|
 | 
						|
interactive=yes
 | 
						|
test -f "$query_apply" || interactive=no
 | 
						|
 | 
						|
while [ "$interactive" = yes ]; do
 | 
						|
	echo "Commit Body is:"
 | 
						|
	echo "--------------------------"
 | 
						|
	cat "$final"
 | 
						|
	echo "--------------------------"
 | 
						|
	echo -n "Apply? [y]es/[n]o/[e]dit/[a]ccept all "
 | 
						|
	read reply
 | 
						|
	case "$reply" in
 | 
						|
		y|Y) interactive=no;;
 | 
						|
		n|N) exit 2;;	# special value to tell dotest to keep going
 | 
						|
		e|E) "$EDIT" "$final";;
 | 
						|
		a|A) rm -f "$query_apply"
 | 
						|
		     interactive=no ;;
 | 
						|
	esac
 | 
						|
done
 | 
						|
 | 
						|
if test -x "$GIT_DIR"/hooks/applypatch-msg
 | 
						|
then
 | 
						|
	"$GIT_DIR"/hooks/applypatch-msg "$final" || exit
 | 
						|
fi
 | 
						|
 | 
						|
echo
 | 
						|
echo Applying "'$SUBJECT'"
 | 
						|
echo
 | 
						|
 | 
						|
git-apply --index "$PATCHFILE" || {
 | 
						|
 | 
						|
	# git-apply exits with status 1 when the patch does not apply,
 | 
						|
	# but it die()s with other failures, most notably upon corrupt
 | 
						|
	# patch.  In the latter case, there is no point to try applying
 | 
						|
	# it to another tree and do 3-way merge.
 | 
						|
	test $? = 1 || exit 1
 | 
						|
 | 
						|
	test -f "$fall_back_3way" || exit 1
 | 
						|
 | 
						|
	# Here if we know which revision the patch applies to,
 | 
						|
	# we create a temporary working tree and index, apply the
 | 
						|
	# patch, and attempt 3-way merge with the resulting tree.
 | 
						|
 | 
						|
	O_OBJECT=`cd "$GIT_OBJECT_DIRECTORY" && pwd`
 | 
						|
	rm -fr .patch-merge-*
 | 
						|
 | 
						|
	(
 | 
						|
		N=10
 | 
						|
 | 
						|
		# if the patch records the base tree...
 | 
						|
		sed -ne '
 | 
						|
			/^diff /q
 | 
						|
			/^applies-to: \([0-9a-f]*\)$/{
 | 
						|
				s//\1/p
 | 
						|
				q
 | 
						|
			}
 | 
						|
		' "$PATCHFILE"
 | 
						|
 | 
						|
		# or hoping the patch is against our recent commits...
 | 
						|
		git-rev-list --max-count=$N HEAD
 | 
						|
 | 
						|
		# or hoping the patch is against known tags...
 | 
						|
		git-ls-remote --tags .
 | 
						|
	) |
 | 
						|
	while read base junk
 | 
						|
	do
 | 
						|
		# Try it if we have it as a tree.
 | 
						|
		git-cat-file tree "$base" >/dev/null 2>&1 || continue
 | 
						|
 | 
						|
		rm -fr .patch-merge-tmp-* &&
 | 
						|
		mkdir .patch-merge-tmp-dir || break
 | 
						|
		(
 | 
						|
			cd .patch-merge-tmp-dir &&
 | 
						|
			GIT_INDEX_FILE=../.patch-merge-tmp-index &&
 | 
						|
			GIT_OBJECT_DIRECTORY="$O_OBJECT" &&
 | 
						|
			export GIT_INDEX_FILE GIT_OBJECT_DIRECTORY &&
 | 
						|
			git-read-tree "$base" &&
 | 
						|
			git-apply --index &&
 | 
						|
			mv ../.patch-merge-tmp-index ../.patch-merge-index &&
 | 
						|
			echo "$base" >../.patch-merge-base
 | 
						|
		) <"$PATCHFILE"  2>/dev/null && break
 | 
						|
	done
 | 
						|
 | 
						|
	test -f .patch-merge-index &&
 | 
						|
	his_tree=$(GIT_INDEX_FILE=.patch-merge-index git-write-tree) &&
 | 
						|
	orig_tree=$(cat .patch-merge-base) &&
 | 
						|
	rm -fr .patch-merge-* || exit 1
 | 
						|
 | 
						|
	echo Falling back to patching base and 3-way merge using $orig_tree...
 | 
						|
 | 
						|
	# This is not so wrong.  Depending on which base we picked,
 | 
						|
	# orig_tree may be wildly different from ours, but his_tree
 | 
						|
	# has the same set of wildly different changes in parts the
 | 
						|
	# patch did not touch, so resolve ends up cancelling them,
 | 
						|
	# saying that we reverted all those changes.
 | 
						|
 | 
						|
	if git-merge-resolve $orig_tree -- HEAD $his_tree
 | 
						|
	then
 | 
						|
		echo Done.
 | 
						|
	else
 | 
						|
		echo Failed to merge in the changes.
 | 
						|
		exit 1
 | 
						|
	fi
 | 
						|
}
 | 
						|
 | 
						|
if test -x "$GIT_DIR"/hooks/pre-applypatch
 | 
						|
then
 | 
						|
	"$GIT_DIR"/hooks/pre-applypatch || exit
 | 
						|
fi
 | 
						|
 | 
						|
tree=$(git-write-tree) || exit 1
 | 
						|
echo Wrote tree $tree
 | 
						|
parent=$(git-rev-parse --verify HEAD) &&
 | 
						|
commit=$(git-commit-tree $tree -p $parent <"$final") || exit 1
 | 
						|
echo Committed: $commit
 | 
						|
git-update-ref HEAD $commit $parent || exit
 | 
						|
 | 
						|
if test -x "$GIT_DIR"/hooks/post-applypatch
 | 
						|
then
 | 
						|
	"$GIT_DIR"/hooks/post-applypatch
 | 
						|
fi
 |