Revision b018ff60857ee7d79e3c4a8772bac08656a2a3c6 authored by Jeff King on 29 December 2012, 20:51:54 UTC, committed by Junio C Hamano on 04 November 2013, 18:13:27 UTC
When we call "read-tree --reset -u HEAD ORIG_HEAD", the first thing we
do with the index is to call read_cache_unmerged.  Originally that
would read the index, leaving aside any unmerged entries.  However, as
of d1a43f2 (reset --hard/read-tree --reset -u: remove unmerged new
paths, 2008-10-15), it actually creates a new cache entry to serve as
a placeholder, so that we later know to update the working tree.

However, we later noticed that the sha1 of that unmerged entry was
just copied from some higher stage, leaving you with random content in
the index.  That was fixed by e11d7b5 ("reset --merge": fix unmerged
case, 2009-12-31), which instead puts the null sha1 into the newly
created entry, and sets a CE_CONFLICTED flag. At the same time, it
teaches the unpack-trees machinery to pay attention to this flag, so
that oneway_merge throws away the current value.

However, it did not update the code paths for twoway_merge, which is
where we end up in the two-way read-tree with --reset. We notice that
the HEAD and ORIG_HEAD versions are the same, and say "oh, we can just
reuse the current version". But that's not true. The current version
is bogus.

Notice this case and make sure we do not keep the bogus entry; either
we do not have that path in the tree we are moving to (i.e. remove
it), or we want to have the cache entry we created for the tree we are
moving to (i.e. resolve by explicitly saying the "newtree" version is
what we want).

[jc: this is from the almost year-old $gmane/212316]

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
1 parent c479d14
Raw File
t2016-checkout-patch.sh
#!/bin/sh

test_description='git checkout --patch'

. ./lib-patch-mode.sh

test_expect_success PERL 'setup' '
	mkdir dir &&
	echo parent > dir/foo &&
	echo dummy > bar &&
	git add bar dir/foo &&
	git commit -m initial &&
	test_tick &&
	test_commit second dir/foo head &&
	set_and_save_state bar bar_work bar_index &&
	save_head
'

# note: bar sorts before dir/foo, so the first 'n' is always to skip 'bar'

test_expect_success PERL 'saying "n" does nothing' '
	set_and_save_state dir/foo work head &&
	(echo n; echo n) | git checkout -p &&
	verify_saved_state bar &&
	verify_saved_state dir/foo
'

test_expect_success PERL 'git checkout -p' '
	(echo n; echo y) | git checkout -p &&
	verify_saved_state bar &&
	verify_state dir/foo head head
'

test_expect_success PERL 'git checkout -p with staged changes' '
	set_state dir/foo work index &&
	(echo n; echo y) | git checkout -p &&
	verify_saved_state bar &&
	verify_state dir/foo index index
'

test_expect_success PERL 'git checkout -p HEAD with NO staged changes: abort' '
	set_and_save_state dir/foo work head &&
	(echo n; echo y; echo n) | git checkout -p HEAD &&
	verify_saved_state bar &&
	verify_saved_state dir/foo
'

test_expect_success PERL 'git checkout -p HEAD with NO staged changes: apply' '
	(echo n; echo y; echo y) | git checkout -p HEAD &&
	verify_saved_state bar &&
	verify_state dir/foo head head
'

test_expect_success PERL 'git checkout -p HEAD with change already staged' '
	set_state dir/foo index index &&
	# the third n is to get out in case it mistakenly does not apply
	(echo n; echo y; echo n) | git checkout -p HEAD &&
	verify_saved_state bar &&
	verify_state dir/foo head head
'

test_expect_success PERL 'git checkout -p HEAD^' '
	# the third n is to get out in case it mistakenly does not apply
	(echo n; echo y; echo n) | git checkout -p HEAD^ &&
	verify_saved_state bar &&
	verify_state dir/foo parent parent
'

test_expect_success PERL 'git checkout -p handles deletion' '
	set_state dir/foo work index &&
	rm dir/foo &&
	(echo n; echo y) | git checkout -p &&
	verify_saved_state bar &&
	verify_state dir/foo index index
'

# The idea in the rest is that bar sorts first, so we always say 'y'
# first and if the path limiter fails it'll apply to bar instead of
# dir/foo.  There's always an extra 'n' to reject edits to dir/foo in
# the failure case (and thus get out of the loop).

test_expect_success PERL 'path limiting works: dir' '
	set_state dir/foo work head &&
	(echo y; echo n) | git checkout -p dir &&
	verify_saved_state bar &&
	verify_state dir/foo head head
'

test_expect_success PERL 'path limiting works: -- dir' '
	set_state dir/foo work head &&
	(echo y; echo n) | git checkout -p -- dir &&
	verify_saved_state bar &&
	verify_state dir/foo head head
'

test_expect_success PERL 'path limiting works: HEAD^ -- dir' '
	# the third n is to get out in case it mistakenly does not apply
	(echo y; echo n; echo n) | git checkout -p HEAD^ -- dir &&
	verify_saved_state bar &&
	verify_state dir/foo parent parent
'

test_expect_success PERL 'path limiting works: foo inside dir' '
	set_state dir/foo work head &&
	# the third n is to get out in case it mistakenly does not apply
	(echo y; echo n; echo n) | (cd dir && git checkout -p foo) &&
	verify_saved_state bar &&
	verify_state dir/foo head head
'

test_expect_success PERL 'none of this moved HEAD' '
	verify_saved_head
'

test_done
back to top