Subject: Re: [PATCH 3/3] checkout -m: attempt merge when deletion of path was staged

From: Junio C Hamano <[email-masked]>
Date: Wed, 13 Aug 2014 11:59:18 -0700

Junio C Hamano <[email-masked]> writes:

> Jonathan Nieder <[email-masked]> writes:
>
>> twoway_merge() is missing an o->gently check in the case where a file
>> that needs to be modified is missing from the index but present in the
>> old and new trees.  As a result, in this case 'git checkout -m' errors
>> out instead of trying to perform a merge.
>
> I see two hunks in threeway_merge(), so two existing callers there
> will not change their behaviour.  Two hunks in twoway_merge() means
> that among three existing callers in that function, this one at the
> end (not shown in your patch) changes its behaviour:
>
> 	else if (newtree) {
> 		if (oldtree && !o->initial_checkout) {
> 			/*
> 			 * deletion of the path was staged;
> 			 */
> 			if (same(oldtree, newtree))
> 				return 1;
> 			return reject_merge(oldtree, o);
> 		}
> 		return merged_entry(newtree, current, o);
> 	}
> 	return deleted_entry(oldtree, current, o);
>
>> This is the most iffy of the three patches, mostly because I was too
>> lazy to write a test.
>
> You would trigger this codepath by jumping from an old revision to a
> new revision after "git rm $path" any path that has been modified
> between the two.  The only behaviour difference is that it will stop
> issuing an error message---the "checkout -m" will successfully switch
> between the revs and leave the index in a "we modified, they removed"
> conflicting state with or without your patch.

IOW, something like this perhaps?

diff --git a/t/t7201-co.sh b/t/t7201-co.sh
index 0c9ec0a..cedbb6a 100755
--- a/t/t7201-co.sh
+++ b/t/t7201-co.sh
@@ -223,6 +223,23 @@ test_expect_success 'checkout --merge --conflict=diff3 <branch>' '
 	test_cmp two expect
 '
 
+test_expect_success 'switch to another branch while carrying a deletion' '
+
+	git checkout -f master && git reset --hard && git clean -f &&
+	git rm two &&
+
+	test_must_fail git checkout simple 2>errs &&
+	test_i18ngrep overwritten errs &&
+
+	git checkout --merge simple 2>errs &&
+	! test_i18ngrep overwritten errs &&
+	git ls-files -u &&
+	test_must_fail git cat-file -t :0:two &&
+	test "$(git cat-file -t :1:two)" = blob &&
+	test "$(git cat-file -t :2:two)" = blob &&
+	test_must_fail git cat-file -t :3:two
+'
+
 test_expect_success 'checkout to detach HEAD (with advice declined)' '
 
 	git config advice.detachedHead false &&