merge: forcefully mark files that we get from the second parent as dirty
After a hg merge, we want to include in the commit all the files that we
got from the second parent, so that we have the correct file-level
history. To make them visible to hg commit, we try to mark them as dirty.
Unfortunately, right now we can't really mark them as dirty[1] - the
best we can do is to mark them as needing a full comparison of their
contents, but they will still be considered clean if they happen to be
identical to the version in the first parent.
This changeset extends the dirstate format in a compatible way, so that
we can mark a file as dirty:
Right now we use a negative file size to indicate we don't have valid
stat data for this entry. In practice, this size is always -1.
This patch uses -2 to indicate that the entry is dirty. Older versions
of hg won't choke on this dirstate, but they may happily mark the file
as clean after a full comparison, destroying all of our hard work.
The patch adds a dirstate.normallookup method with the semantics of the
current normaldirty, and changes normaldirty to forcefully mark the
entry as dirty.
This should fix issue522.
[1] - well, we could put them in state 'm', but that state has a
different meaning.
#!/bin/sh
#
# hgmerge - default merge helper for Mercurial
#
# This tries to find a way to do three-way merge on the current system.
# The result ought to end up in $1. Script is run in root directory of
# repository.
#
# Environment variables set by Mercurial:
# HG_FILE name of file within repo
# HG_MY_NODE revision being merged
# HG_OTHER_NODE revision being merged
set -e # bail out quickly on failure
LOCAL="$1"
BASE="$2"
OTHER="$3"
if [ -z "$EDITOR" ]; then
EDITOR="vi"
fi
# find decent versions of our utilities, insisting on the GNU versions where we
# need to
MERGE="merge"
DIFF3="gdiff3"
DIFF="gdiff"
PATCH="gpatch"
type "$MERGE" >/dev/null 2>&1 || MERGE=
type "$DIFF3" >/dev/null 2>&1 || DIFF3="diff3"
$DIFF3 --version >/dev/null 2>&1 || DIFF3=
type "$DIFF" >/dev/null 2>&1 || DIFF="diff"
type "$DIFF" >/dev/null 2>&1 || DIFF=
type "$PATCH" >/dev/null 2>&1 || PATCH="patch"
type "$PATCH" >/dev/null 2>&1 || PATCH=
# find optional visual utilities
FILEMERGE="/Developer/Applications/Utilities/FileMerge.app/Contents/MacOS/FileMerge"
KDIFF3="kdiff3"
TKDIFF="tkdiff"
MELD="meld"
type "$FILEMERGE" >/dev/null 2>&1 || FILEMERGE=
type "$KDIFF3" >/dev/null 2>&1 || KDIFF3=
type "$TKDIFF" >/dev/null 2>&1 || TKDIFF=
type "$MELD" >/dev/null 2>&1 || MELD=
# Hack for Solaris
TEST="/usr/bin/test"
type "$TEST" >/dev/null 2>&1 || TEST="/bin/test"
type "$TEST" >/dev/null 2>&1 || TEST="test"
# random part of names
RAND="$RANDOM$RANDOM"
# temporary directory for diff+patch merge
HGTMP="${TMPDIR-/tmp}/hgmerge.$RAND"
# backup file
BACKUP="$LOCAL.orig.$RAND"
# file used to test for file change
CHGTEST="$LOCAL.chg.$RAND"
# put all your required cleanup here
cleanup() {
rm -f "$BACKUP" "$CHGTEST"
rm -rf "$HGTMP"
}
# functions concerning program exit
success() {
cleanup
exit 0
}
failure() {
echo "merge failed" 1>&2
mv "$BACKUP" "$LOCAL"
cleanup
exit 1
}
# Ask if the merge was successful
ask_if_merged() {
while true; do
echo "$LOCAL seems unchanged."
echo "Was the merge successful? [y/n]"
read answer
case "$answer" in
y*|Y*) success;;
n*|N*) failure;;
esac
done
}
# Check if conflict markers are present and ask if the merge was successful
conflicts_or_success() {
while egrep '^(<<<<<<< .*|=======|>>>>>>> .*)$' "$LOCAL" >/dev/null; do
echo "$LOCAL contains conflict markers."
echo "Keep this version? [y/n]"
read answer
case "$answer" in
y*|Y*) success;;
n*|N*) failure;;
esac
done
success
}
# Clean up when interrupted
trap "failure" 1 2 3 6 15 # HUP INT QUIT ABRT TERM
# Back up our file (and try hard to keep the mtime unchanged)
mv "$LOCAL" "$BACKUP"
cp "$BACKUP" "$LOCAL"
# Attempt to do a non-interactive merge
if [ -n "$MERGE" -o -n "$DIFF3" ]; then
if [ -n "$MERGE" ]; then
$MERGE "$LOCAL" "$BASE" "$OTHER" 2> /dev/null && success
elif [ -n "$DIFF3" ]; then
$DIFF3 -m "$BACKUP" "$BASE" "$OTHER" > "$LOCAL" && success
fi
if [ $? -gt 1 ]; then
echo "automatic merge failed! Exiting." 1>&2
failure
fi
fi
# on MacOS X try FileMerge.app, shipped with Apple's developer tools
if [ -n "$FILEMERGE" ]; then
cp "$BACKUP" "$LOCAL"
cp "$BACKUP" "$CHGTEST"
# filemerge prefers the right by default
$FILEMERGE -left "$OTHER" -right "$LOCAL" -ancestor "$BASE" -merge "$LOCAL"
[ $? -ne 0 ] && echo "FileMerge failed to launch" && failure
$TEST "$LOCAL" -nt "$CHGTEST" && conflicts_or_success || ask_if_merged
fi
if [ -n "$DISPLAY" ]; then
# try using kdiff3, which is fairly nice
if [ -n "$KDIFF3" ]; then
$KDIFF3 --auto "$BASE" "$BACKUP" "$OTHER" -o "$LOCAL" || failure
conflicts_or_success
fi
# try using tkdiff, which is a bit less sophisticated
if [ -n "$TKDIFF" ]; then
$TKDIFF "$BACKUP" "$OTHER" -a "$BASE" -o "$LOCAL" || failure
conflicts_or_success
fi
if [ -n "$MELD" ]; then
cp "$BACKUP" "$CHGTEST"
# protect our feet - meld allows us to save to the left file
cp "$BACKUP" "$LOCAL.tmp.$RAND"
# Meld doesn't have automatic merging, so to reduce intervention
# use the file with conflicts
$MELD "$LOCAL.tmp.$RAND" "$LOCAL" "$OTHER" || failure
# Also it doesn't return good error code
$TEST "$LOCAL" -nt "$CHGTEST" && conflicts_or_success || ask_if_merged
fi
fi
# Attempt to do a merge with $EDITOR
if [ -n "$MERGE" -o -n "$DIFF3" ]; then
echo "conflicts detected in $LOCAL"
cp "$BACKUP" "$CHGTEST"
case "$EDITOR" in
"emacs")
$EDITOR "$LOCAL" --eval '(condition-case nil (smerge-mode 1) (error nil))' || failure
;;
*)
$EDITOR "$LOCAL" || failure
;;
esac
# Some editors do not return meaningful error codes
# Do not take any chances
$TEST "$LOCAL" -nt "$CHGTEST" && conflicts_or_success || ask_if_merged
fi
# attempt to manually merge with diff and patch
if [ -n "$DIFF" -a -n "$PATCH" ]; then
(umask 077 && mkdir "$HGTMP") || {
echo "Could not create temporary directory $HGTMP" 1>&2
failure
}
$DIFF -u "$BASE" "$OTHER" > "$HGTMP/diff" || :
if $PATCH "$LOCAL" < "$HGTMP/diff"; then
success
else
# If rejects are empty after using the editor, merge was ok
$EDITOR "$LOCAL" "$LOCAL.rej" || failure
$TEST -s "$LOCAL.rej" || success
fi
failure
fi
echo
echo "hgmerge: unable to find any merge utility!"
echo "supported programs:"
echo "merge, FileMerge, tkdiff, kdiff3, meld, diff+patch"
echo
failure