X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=git-applypatch.sh;h=e4b09472e1fa27a6431b431572af7353c4f0a9b2;hb=ae448e3854d8b6e7e37aa88fa3917f5dd97f3210;hp=14635d9bce2b496984f56a656d52cf75523a208e;hpb=d67c4af41f7975940e8fec6405411581570eb672;p=git.git diff --git a/git-applypatch.sh b/git-applypatch.sh index 14635d9b..e4b09472 100755 --- a/git-applypatch.sh +++ b/git-applypatch.sh @@ -10,7 +10,11 @@ ## $3 - "info" file with Author, email and subject ## $4 - optional file containing signoff to add ## -. git-sh-setup || die "Not a git archive." + +USAGE=' []' +. git-sh-setup + +case "$#" in 3|4) ;; *) usage ;; esac final=.dotest/final-commit ## @@ -22,6 +26,8 @@ query_apply=.dotest/.query_apply ## 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 @@ -81,7 +87,7 @@ while [ "$interactive" = yes ]; do echo "--------------------------" cat "$final" echo "--------------------------" - echo -n "Apply? [y]es/[n]o/[e]dit/[a]ccept all " + printf "Apply? [y]es/[n]o/[e]dit/[a]ccept all " read reply case "$reply" in y|Y) interactive=no;; @@ -102,10 +108,90 @@ 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. - exit 1 + + O_OBJECT=`cd "$GIT_OBJECT_DIRECTORY" && pwd` + rm -fr .patch-merge-* + + if git-apply -z --index-info "$PATCHFILE" \ + >.patch-merge-index-info 2>/dev/null && + GIT_INDEX_FILE=.patch-merge-tmp-index \ + git-update-index -z --index-info <.patch-merge-index-info && + GIT_INDEX_FILE=.patch-merge-tmp-index \ + git-write-tree >.patch-merge-tmp-base && + ( + mkdir .patch-merge-tmp-dir && + cd .patch-merge-tmp-dir && + GIT_INDEX_FILE="../.patch-merge-tmp-index" \ + GIT_OBJECT_DIRECTORY="$O_OBJECT" \ + git-apply $binary --index + ) <"$PATCHFILE" + then + echo Using index info to reconstruct a base tree... + mv .patch-merge-tmp-base .patch-merge-base + mv .patch-merge-tmp-index .patch-merge-index + else + ( + N=10 + + # Otherwise, try nearby trees that can be used to apply the + # patch. + 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 + fi + + 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 @@ -118,7 +204,7 @@ 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 +git-update-ref -m "applypatch: $SUBJECT" HEAD $commit $parent || exit if test -x "$GIT_DIR"/hooks/post-applypatch then