git-read-tree: be a lot more careful about merging dirty trees
We don't want to overwrite state that we haven't committed yet when merging, so it's better to make git-read-tree fail than end up with a merge tree that ends up not having the dirty changes. Update git-resolve-script to fail cleanly when git-read-tree fails.
This commit is contained in:
@ -39,13 +39,14 @@ if [ "$common" == "$head" ]; then
|
|||||||
echo "Destroying all noncommitted data!"
|
echo "Destroying all noncommitted data!"
|
||||||
echo "Kill me within 3 seconds.."
|
echo "Kill me within 3 seconds.."
|
||||||
sleep 3
|
sleep 3
|
||||||
git-read-tree -m $merge && git-checkout-cache -f -u -a
|
git-read-tree -m $merge || exit 1
|
||||||
|
git-checkout-cache -f -u -a
|
||||||
echo $merge > "$GIT_DIR"/HEAD
|
echo $merge > "$GIT_DIR"/HEAD
|
||||||
git-diff-tree -p ORIG_HEAD HEAD | git-apply --stat
|
git-diff-tree -p ORIG_HEAD HEAD | git-apply --stat
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
echo "Trying to merge $merge into $head"
|
echo "Trying to merge $merge into $head"
|
||||||
git-read-tree -m $common $head $merge
|
git-read-tree -m $common $head $merge || exit 1
|
||||||
merge_msg="Merge of $merge_repo"
|
merge_msg="Merge of $merge_repo"
|
||||||
result_tree=$(git-write-tree 2> /dev/null)
|
result_tree=$(git-write-tree 2> /dev/null)
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
|
54
read-tree.c
54
read-tree.c
@ -93,11 +93,46 @@ static struct cache_entry *merge_entries(struct cache_entry *a,
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When a CE gets turned into an unmerged entry, we
|
||||||
|
* want it to be up-to-date
|
||||||
|
*/
|
||||||
|
static void verify_uptodate(struct cache_entry *ce)
|
||||||
|
{
|
||||||
|
struct stat st;
|
||||||
|
|
||||||
|
if (!lstat(ce->name, &st)) {
|
||||||
|
unsigned changed = ce_match_stat(ce, &st);
|
||||||
|
if (!changed)
|
||||||
|
return;
|
||||||
|
errno = 0;
|
||||||
|
}
|
||||||
|
if (errno == ENOENT)
|
||||||
|
return;
|
||||||
|
die("Entry '%s' not uptodate. Cannot merge.", ce->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the old tree contained a CE that isn't even in the
|
||||||
|
* result, that's always a problem, regardless of whether
|
||||||
|
* it's up-to-date or not (ie it can be a file that we
|
||||||
|
* have updated but not committed yet).
|
||||||
|
*/
|
||||||
|
static void verify_cleared(struct cache_entry *ce)
|
||||||
|
{
|
||||||
|
if (ce)
|
||||||
|
die("Entry '%s' would be overwritten by merge. Cannot merge.", ce->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int old_match(struct cache_entry *old, struct cache_entry *a)
|
||||||
|
{
|
||||||
|
return old && path_matches(old, a) && same(old, a);
|
||||||
|
}
|
||||||
|
|
||||||
static void trivially_merge_cache(struct cache_entry **src, int nr)
|
static void trivially_merge_cache(struct cache_entry **src, int nr)
|
||||||
{
|
{
|
||||||
static struct cache_entry null_entry;
|
|
||||||
struct cache_entry **dst = src;
|
struct cache_entry **dst = src;
|
||||||
struct cache_entry *old = &null_entry;
|
struct cache_entry *old = NULL;
|
||||||
|
|
||||||
while (nr) {
|
while (nr) {
|
||||||
struct cache_entry *ce, *result;
|
struct cache_entry *ce, *result;
|
||||||
@ -106,6 +141,7 @@ static void trivially_merge_cache(struct cache_entry **src, int nr)
|
|||||||
|
|
||||||
/* We throw away original cache entries except for the stat information */
|
/* We throw away original cache entries except for the stat information */
|
||||||
if (!ce_stage(ce)) {
|
if (!ce_stage(ce)) {
|
||||||
|
verify_cleared(old);
|
||||||
old = ce;
|
old = ce;
|
||||||
src++;
|
src++;
|
||||||
nr--;
|
nr--;
|
||||||
@ -117,18 +153,30 @@ static void trivially_merge_cache(struct cache_entry **src, int nr)
|
|||||||
* See if we can re-use the old CE directly?
|
* See if we can re-use the old CE directly?
|
||||||
* That way we get the uptodate stat info.
|
* That way we get the uptodate stat info.
|
||||||
*/
|
*/
|
||||||
if (path_matches(result, old) && same(result, old))
|
if (old_match(old, result)) {
|
||||||
*result = *old;
|
*result = *old;
|
||||||
|
old = NULL;
|
||||||
|
}
|
||||||
ce = result;
|
ce = result;
|
||||||
ce->ce_flags &= ~htons(CE_STAGEMASK);
|
ce->ce_flags &= ~htons(CE_STAGEMASK);
|
||||||
src += 2;
|
src += 2;
|
||||||
nr -= 2;
|
nr -= 2;
|
||||||
active_nr -= 2;
|
active_nr -= 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we had an old entry that we now effectively
|
||||||
|
* overwrite, make sure it wasn't dirty.
|
||||||
|
*/
|
||||||
|
if (old_match(old, ce)) {
|
||||||
|
verify_uptodate(old);
|
||||||
|
old = NULL;
|
||||||
|
}
|
||||||
*dst++ = ce;
|
*dst++ = ce;
|
||||||
src++;
|
src++;
|
||||||
nr--;
|
nr--;
|
||||||
}
|
}
|
||||||
|
verify_cleared(old);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void merge_stat_info(struct cache_entry **src, int nr)
|
static void merge_stat_info(struct cache_entry **src, int nr)
|
||||||
|
Reference in New Issue
Block a user