ae2b0f1518
Now all the users of this script detect its exit status and die, complaining that it is outside git repository. So move the code that dies from all callers to git-sh-setup script. 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
|
|
|
|
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
|