From: Linus Torvalds Date: Mon, 6 Jun 2005 03:02:31 +0000 (-0700) Subject: git-read-tree: be a lot more careful about merging dirty trees X-Git-Tag: v0.99~350 X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=02ede67ad45973f9f8d07db7896a537de97d21b6;p=git.git 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. --- diff --git a/git-resolve-script b/git-resolve-script index b7ccc2011..4fc7a6dce 100644 --- a/git-resolve-script +++ b/git-resolve-script @@ -39,13 +39,14 @@ if [ "$common" == "$head" ]; then echo "Destroying all noncommitted data!" echo "Kill me within 3 seconds.." 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 git-diff-tree -p ORIG_HEAD HEAD | git-apply --stat exit 0 fi 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" result_tree=$(git-write-tree 2> /dev/null) if [ $? -ne 0 ]; then diff --git a/read-tree.c b/read-tree.c index 283f4d857..647f501f6 100644 --- a/read-tree.c +++ b/read-tree.c @@ -93,11 +93,46 @@ static struct cache_entry *merge_entries(struct cache_entry *a, 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 struct cache_entry null_entry; struct cache_entry **dst = src; - struct cache_entry *old = &null_entry; + struct cache_entry *old = NULL; while (nr) { 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 */ if (!ce_stage(ce)) { + verify_cleared(old); old = ce; src++; 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? * That way we get the uptodate stat info. */ - if (path_matches(result, old) && same(result, old)) + if (old_match(old, result)) { *result = *old; + old = NULL; + } ce = result; ce->ce_flags &= ~htons(CE_STAGEMASK); src += 2; 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; src++; nr--; } + verify_cleared(old); } static void merge_stat_info(struct cache_entry **src, int nr)