mirror of
https://github.com/git/git.git
synced 2024-10-31 22:37:54 +01:00
1a92e53ba3
When we see an add/add conflict on a file, we generate the
conflicted content by doing a 3-way merge with a "virtual"
base consisting of the common lines of the two sides. This
strategy dates back to cb93c19
(merge-one-file: use common
as base, instead of emptiness., 2005-11-09).
Back then, the next step was to call rcs merge to generate
the 3-way conflicts. Using the virtual base produced much
better results, as rcs merge does not attempt to minimize
the hunks. As a result, you'd get a conflict with the
entirety of the files on either side.
Since then, though, we've switched to using git-merge-file,
which uses xdiff's "zealous" merge. This will find the
minimal hunks even with just the simple, empty base.
Let's switch to using that empty base. It's simpler, more
efficient, and reduces our dependencies (we no longer need a
working diff binary). It's also how the merge-recursive
strategy handles this same case.
We can almost get rid of git-sh-setup's create_virtual_base,
but we don't here, for two reasons:
1. The functions in git-sh-setup are part of our public
interface, so it's possible somebody is depending on
it. We'd at least need to deprecate it first.
2. It's also used by mergetool's p4merge driver. It's
unknown whether its 3-way merge is as capable as git's;
if not, then it is benefiting from the function.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
159 lines
3.4 KiB
Bash
Executable file
159 lines
3.4 KiB
Bash
Executable file
#!/bin/sh
|
|
#
|
|
# Copyright (c) Linus Torvalds, 2005
|
|
#
|
|
# This is the git per-file merge script, called with
|
|
#
|
|
# $1 - original file SHA1 (or empty)
|
|
# $2 - file in branch1 SHA1 (or empty)
|
|
# $3 - file in branch2 SHA1 (or empty)
|
|
# $4 - pathname in repository
|
|
# $5 - original file mode (or empty)
|
|
# $6 - file in branch1 mode (or empty)
|
|
# $7 - file in branch2 mode (or empty)
|
|
#
|
|
# Handle some trivial cases.. The _really_ trivial cases have
|
|
# been handled already by git read-tree, but that one doesn't
|
|
# do any merges that might change the tree layout.
|
|
|
|
USAGE='<orig blob> <our blob> <their blob> <path>'
|
|
USAGE="$USAGE <orig mode> <our mode> <their mode>"
|
|
LONG_USAGE="usage: git merge-one-file $USAGE
|
|
|
|
Blob ids and modes should be empty for missing files."
|
|
|
|
SUBDIRECTORY_OK=Yes
|
|
. git-sh-setup
|
|
cd_to_toplevel
|
|
require_work_tree
|
|
|
|
if test $# != 7
|
|
then
|
|
echo "$LONG_USAGE"
|
|
exit 1
|
|
fi
|
|
|
|
case "${1:-.}${2:-.}${3:-.}" in
|
|
#
|
|
# Deleted in both or deleted in one and unchanged in the other
|
|
#
|
|
"$1.." | "$1.$1" | "$1$1.")
|
|
if test -n "$2"
|
|
then
|
|
echo "Removing $4"
|
|
else
|
|
# read-tree checked that index matches HEAD already,
|
|
# so we know we do not have this path tracked.
|
|
# there may be an unrelated working tree file here,
|
|
# which we should just leave unmolested. Make sure
|
|
# we do not have it in the index, though.
|
|
exec git update-index --remove -- "$4"
|
|
fi
|
|
if test -f "$4"
|
|
then
|
|
rm -f -- "$4" &&
|
|
rmdir -p "$(expr "z$4" : 'z\(.*\)/')" 2>/dev/null || :
|
|
fi &&
|
|
exec git update-index --remove -- "$4"
|
|
;;
|
|
|
|
#
|
|
# Added in one.
|
|
#
|
|
".$2.")
|
|
# the other side did not add and we added so there is nothing
|
|
# to be done, except making the path merged.
|
|
exec git update-index --add --cacheinfo "$6" "$2" "$4"
|
|
;;
|
|
"..$3")
|
|
echo "Adding $4"
|
|
if test -f "$4"
|
|
then
|
|
echo "ERROR: untracked $4 is overwritten by the merge." >&2
|
|
exit 1
|
|
fi
|
|
git update-index --add --cacheinfo "$7" "$3" "$4" &&
|
|
exec git checkout-index -u -f -- "$4"
|
|
;;
|
|
|
|
#
|
|
# Added in both, identically (check for same permissions).
|
|
#
|
|
".$3$2")
|
|
if test "$6" != "$7"
|
|
then
|
|
echo "ERROR: File $4 added identically in both branches," >&2
|
|
echo "ERROR: but permissions conflict $6->$7." >&2
|
|
exit 1
|
|
fi
|
|
echo "Adding $4"
|
|
git update-index --add --cacheinfo "$6" "$2" "$4" &&
|
|
exec git checkout-index -u -f -- "$4"
|
|
;;
|
|
|
|
#
|
|
# Modified in both, but differently.
|
|
#
|
|
"$1$2$3" | ".$2$3")
|
|
|
|
case ",$6,$7," in
|
|
*,120000,*)
|
|
echo "ERROR: $4: Not merging symbolic link changes." >&2
|
|
exit 1
|
|
;;
|
|
*,160000,*)
|
|
echo "ERROR: $4: Not merging conflicting submodule changes." >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
src1=$(git-unpack-file $2)
|
|
src2=$(git-unpack-file $3)
|
|
case "$1" in
|
|
'')
|
|
echo "Added $4 in both, but differently."
|
|
orig=$(git-unpack-file e69de29bb2d1d6434b8b29ae775ad8c2e48c5391)
|
|
;;
|
|
*)
|
|
echo "Auto-merging $4"
|
|
orig=$(git-unpack-file $1)
|
|
;;
|
|
esac
|
|
|
|
git merge-file "$src1" "$orig" "$src2"
|
|
ret=$?
|
|
msg=
|
|
if test $ret != 0 || test -z "$1"
|
|
then
|
|
msg='content conflict'
|
|
ret=1
|
|
fi
|
|
|
|
# Create the working tree file, using "our tree" version from the
|
|
# index, and then store the result of the merge.
|
|
git checkout-index -f --stage=2 -- "$4" && cat "$src1" >"$4" || exit 1
|
|
rm -f -- "$orig" "$src1" "$src2"
|
|
|
|
if test "$6" != "$7"
|
|
then
|
|
if test -n "$msg"
|
|
then
|
|
msg="$msg, "
|
|
fi
|
|
msg="${msg}permissions conflict: $5->$6,$7"
|
|
ret=1
|
|
fi
|
|
|
|
if test $ret != 0
|
|
then
|
|
echo "ERROR: $msg in $4" >&2
|
|
exit 1
|
|
fi
|
|
exec git update-index -- "$4"
|
|
;;
|
|
|
|
*)
|
|
echo "ERROR: $4: Not handling case $1 -> $2 -> $3" >&2
|
|
;;
|
|
esac
|
|
exit 1
|