
The merge-one-file tool predates the invention of GIT_WORK_TREE. By the time GIT_WORK_TREE was invented, most people were using the merge-recursive strategy, which handles resolving internally. Therefore these features have had very little testing together. For the most part, merge-one-file just works with GIT_WORK_TREE; most of its heavy lifting is done by plumbing commands which do respect GIT_WORK_TREE properly. The one exception is a shell redirection which touches the worktree directly, writing results to the wrong place in the presence of a GIT_WORK_TREE variable. This means that merges won't even fail; they will silently produce incorrect results, throwing out the entire "theirs" side of files which need content-level merging! This patch makes merge-one-file chdir to the toplevel of the working tree (and exit if we don't have one). This most closely matches the assumption made by the original script (before separate work trees were invented), and matches what happens when the script is called as part of a merge strategy. While we're at it, we'll also error-check the call to cat. Merging a file in a subdirectory could in fact fail, as the redirection relies on the "checkout-index" call just prior to create leading directories. But we never noticed, since we ignored the error return from running cat. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
166 lines
3.8 KiB
Bash
Executable File
166 lines
3.8 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 "$#" -eq 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 [ "$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."
|
|
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 [ "$6" != "$7" ]; then
|
|
echo "ERROR: File $4 added identically in both branches,"
|
|
echo "ERROR: but permissions conflict $6->$7."
|
|
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."
|
|
exit 1
|
|
;;
|
|
*,160000,*)
|
|
echo "ERROR: $4: Not merging conflicting submodule changes."
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
src2=`git-unpack-file $3`
|
|
case "$1" in
|
|
'')
|
|
echo "Added $4 in both, but differently."
|
|
# This extracts OUR file in $orig, and uses git apply to
|
|
# remove lines that are unique to ours.
|
|
orig=`git-unpack-file $2`
|
|
sz0=`wc -c <"$orig"`
|
|
diff -u -La/$orig -Lb/$orig $orig $src2 | git apply --no-add
|
|
sz1=`wc -c <"$orig"`
|
|
|
|
# If we do not have enough common material, it is not
|
|
# worth trying two-file merge using common subsections.
|
|
expr "$sz0" \< "$sz1" \* 2 >/dev/null || : >$orig
|
|
;;
|
|
*)
|
|
echo "Auto-merging $4"
|
|
orig=`git-unpack-file $1`
|
|
;;
|
|
esac
|
|
|
|
# Be careful for funny filename such as "-L" in "$4", which
|
|
# would confuse "merge" greatly.
|
|
src1=`git-unpack-file $2`
|
|
git merge-file "$src1" "$orig" "$src2"
|
|
ret=$?
|
|
msg=
|
|
if [ $ret -ne 0 ]; then
|
|
msg='content conflict'
|
|
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 [ "$6" != "$7" ]; then
|
|
if [ -n "$msg" ]; then
|
|
msg="$msg, "
|
|
fi
|
|
msg="${msg}permissions conflict: $5->$6,$7"
|
|
ret=1
|
|
fi
|
|
if [ "$1" = '' ]; then
|
|
ret=1
|
|
fi
|
|
|
|
if [ $ret -ne 0 ]; then
|
|
echo "ERROR: $msg in $4"
|
|
exit 1
|
|
fi
|
|
exec git update-index -- "$4"
|
|
;;
|
|
|
|
*)
|
|
echo "ERROR: $4: Not handling case $1 -> $2 -> $3"
|
|
;;
|
|
esac
|
|
exit 1
|