git-merge: preserve and merge local changes when doing fast forward

The idea and the logic are identical to what "checkout -m" does
when switching the branches.  Instead of refusing the two-way
merge, perform the three-way merge between the old head, the
working tree and the new head, and leave the (potentially
conflicted) merge result in the working tree.  When the resulting
conflict were too much to handle for the user, there is no easy
way to get that back, so they are stashed away in $GIT_DIR/LOCAL_DIFF
file.  We do the same for "git checkout -m".

If this turns out to be a sane thing to do, we probably should
make the common logic between "checkout -m" and this into a
built-in command.

Signed-off-by: Junio C Hamano <junkio@cox.net>
This commit is contained in:
Junio C Hamano
2006-11-29 18:53:13 -08:00
parent 278fcd7deb
commit a2a92ab17f
2 changed files with 53 additions and 1 deletions

View File

@@ -168,6 +168,9 @@ else
exit 1 ;;
esac
# First stash away the local changes
git diff-index --binary -p HEAD >"$GIT_DIR"/LOCAL_DIFF
# Match the index to the working tree, and do a three-way.
git diff-files --name-only | git update-index --remove --stdin &&
work=`git write-tree` &&
@@ -195,6 +198,10 @@ else
sed -e 's/^[0-7]* [0-9a-f]* /'"0 $z40 /"
echo "$unmerged"
) | git update-index --index-info
echo >&2 "Conflicts in locally modified files:"
git diff --name-only --diff-filter=U >&2
echo >&2 "Your local changes are found in $GIT_DIR/LOCAL_DIFF"
;;
esac
exit 0

View File

@@ -91,6 +91,51 @@ finish () {
esac
}
merge_local_changes () {
merge_error=$(git-read-tree -m -u $1 $2 2>&1) || (
# First stash away the local changes
git diff-index --binary -p HEAD >"$GIT_DIR"/LOCAL_DIFF
# Match the index to the working tree, and do a three-way.
git diff-files --name-only |
git update-index --remove --stdin &&
work=`git write-tree` &&
git read-tree --reset -u $2 &&
git read-tree -m -u --aggressive $1 $2 $work || exit
echo >&2 "Carrying local changes forward."
if result=`git write-tree 2>/dev/null`
then
echo >&2 "Trivially automerged."
else
git merge-index -o git-merge-one-file -a
fi
# Do not register the cleanly merged paths in the index
# yet; this is not a real merge before committing, but
# just carrying the working tree changes along.
unmerged=`git ls-files -u`
git read-tree --reset $2
case "$unmerged" in
'') ;;
*)
(
z40=0000000000000000000000000000000000000000
echo "$unmerged" |
sed -e 's/^[0-7]* [0-9a-f]* /'"0 $z40 /"
echo "$unmerged"
) | git update-index --index-info
echo >&2 "Conflicts in locally modified files:"
git diff --name-only --diff-filter=U >&2
echo >&2 "Your local changes are found in $GIT_DIR/LOCAL_DIFF"
;;
esac
exit 0
)
}
case "$#" in 0) usage ;; esac
rloga= have_message=
@@ -264,7 +309,7 @@ f,*)
echo "Updating $(git-rev-parse --short $head)..$(git-rev-parse --short $1)"
git-update-index --refresh 2>/dev/null
new_head=$(git-rev-parse --verify "$1^0") &&
git-read-tree -u -v -m $head "$new_head" &&
merge_local_changes $head $new_head &&
finish "$new_head" "Fast forward"
dropsave
exit 0