When aborting a failed merge that has brought in a new path using "git reset --hard" or "git read-tree --reset -u", we used to first forget about the new path (via read_cache_unmerged) and then matched the working tree to what is recorded in the index, thus ending up leaving the new path in the work tree. Signed-off-by: Junio C Hamano <gitster@xxxxxxxxx> --- Junio C Hamano <gitster@xxxxxxxxx> writes: > Linus Torvalds <torvalds@xxxxxxxxxxxxxxxxxxxx> writes: > >> On Wed, 15 Oct 2008, Linus Torvalds wrote: >>> >> It's quite possible that we should remove unmerged entries. Except that's >> not how our internal 'read_cache_unmerged()' function works. It really >> just ignores them, and throws them on the floor. We _could_ try to just >> turn them into a (since) stage-0 entry. >> >> Junio? > > I am not sure what should happen when we can't drop the unmerged entry > down to stage-0 due to D/F conflicts, though. IIRC, read-tree proper > would not touch the work tree in such a case, but merge-recursive creates > our and their versions with funny suffixes, which will not be known to the > index and will be left in the working tree. I am still unsure what we should do when we hit D/F conflicts; this one simply replaces but it may be safer to drop ADD_CACHE_OK_TO_REPLACE from the options to trigger an error in such a case. I dunno. read-cache.c | 32 +++++++++++++++++++------------- t/t1005-read-tree-reset.sh | 30 ++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 13 deletions(-) diff --git a/read-cache.c b/read-cache.c index c229fd4..efbab6a 100644 --- a/read-cache.c +++ b/read-cache.c @@ -1489,25 +1489,31 @@ int write_index(const struct index_state *istate, int newfd) int read_index_unmerged(struct index_state *istate) { int i; - struct cache_entry **dst; - struct cache_entry *last = NULL; + int unmerged = 0; read_index(istate); - dst = istate->cache; for (i = 0; i < istate->cache_nr; i++) { struct cache_entry *ce = istate->cache[i]; - if (ce_stage(ce)) { - remove_name_hash(ce); - if (last && !strcmp(ce->name, last->name)) - continue; - cache_tree_invalidate_path(istate->cache_tree, ce->name); - last = ce; + struct cache_entry *new_ce; + int size, len, option; + + if (!ce_stage(ce)) continue; - } - *dst++ = ce; + unmerged = 1; + len = strlen(ce->name); + size = cache_entry_size(len); + new_ce = xcalloc(1, size); + hashcpy(new_ce->sha1, ce->sha1); + memcpy(new_ce->name, ce->name, len); + new_ce->ce_flags = create_ce_flags(len, 0); + new_ce->ce_mode = ce->ce_mode; + option = ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE; + if (add_index_entry(istate, new_ce, option)) + return error("%s: cannot drop to stage #0", + ce->name); + i = index_name_pos(istate, new_ce->name, len); } - istate->cache_nr = dst - istate->cache; - return !!last; + return unmerged; } struct update_callback_data diff --git a/t/t1005-read-tree-reset.sh b/t/t1005-read-tree-reset.sh index b0d31f5..0cd519c 100755 --- a/t/t1005-read-tree-reset.sh +++ b/t/t1005-read-tree-reset.sh @@ -27,4 +27,34 @@ test_expect_success 'reset should work' ' test_cmp expect actual ' +test_expect_success 'reset should remove remnants from a failed merge' ' + git read-tree --reset -u HEAD && + git ls-files -s >expect && + sha1=$(git rev-parse :new) && + ( + echo "100644 $sha1 1 old" + echo "100644 $sha1 3 old" + ) | git update-index --index-info && + >old && + git ls-files -s && + git read-tree --reset -u HEAD && + git ls-files -s >actual && + ! test -f old +' + +test_expect_success 'Porcelain reset should remove remnants too' ' + git read-tree --reset -u HEAD && + git ls-files -s >expect && + sha1=$(git rev-parse :new) && + ( + echo "100644 $sha1 1 old" + echo "100644 $sha1 3 old" + ) | git update-index --index-info && + >old && + git ls-files -s && + git reset --hard && + git ls-files -s >actual && + ! test -f old +' + test_done -- 1.6.0.2.717.gc6f0a -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html