[PATCH] Fall back to three-way merge when applying a patch.
From: Junio C Hamano <hidden>
Date: 2016-06-15 22:42:08
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. 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 <redacted> --- * I will be placing this in the proposed updates branch. Hopefully this would alleviate the complaints from people who find the "no fuzz" policy of git-apply is too strict. The change is helped if the patch sender uses the updated git-format-patch that records which tree the patch is supposed to cleanly apply to, but that is not strictly necessary; it tries to find a tree that patch applies to from the recent commits itself. git-applypatch.sh | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 61 insertions(+), 1 deletions(-) applies-to: 9eec9ff9326032bca405a12a265918725edf4ac7 fb533c6562481dfbc4719f740aa0d85e2a45700c
diff --git a/git-applypatch.sh b/git-applypatch.sh
--- a/git-applypatch.sh
+++ b/git-applypatch.sh@@ -105,7 +105,67 @@ git-apply --index "$PATCHFILE" || { # 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-* + + ( + 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
--- 0.99.8.GIT