git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 1/3] Fix sparse checkout not removing files from index
@ 2010-07-26  9:08 Nguyễn Thái Ngọc Duy
  2010-07-26  9:08 ` [PATCH 2/3] unpack-trees.c: Do not check ce_stage in will_have_skip_worktree() Nguyễn Thái Ngọc Duy
                   ` (2 more replies)
  0 siblings, 3 replies; 17+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2010-07-26  9:08 UTC (permalink / raw)
  To: git, Junio C Hamano; +Cc: Nguyễn Thái Ngọc Duy

When unpack_trees() nearly completes, sparse pattern is applied and
will prevent any updates (CE_UPDATE, CE_WT_REMOVE, CE_REMOVE) outside
sparse checkout area by clearing these flags.

Unfortunately CE_REMOVE also instructs write_index() to remove files from
the index. By clearing this flag, index may keep unwanted files.

This patch turns CE_REMOVE in unpack-trees.c to CE_REMOVE|CE_WT_REMOVE
and only remove files from worktree if CE_WT_REMOVE is set. CE_REMOVE
is now only meaningful to index operations and CE_WT_REMOVE to
worktree ones.

This fault made the test suite itself faulty. With this fault fixed,
two other faults are revealed :(

Reported in http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=583699

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 cache.h                              |    3 +--
 t/t1011-read-tree-sparse-checkout.sh |    9 ++++++++-
 unpack-trees.c                       |   22 ++++++++++++----------
 3 files changed, 21 insertions(+), 13 deletions(-)

diff --git a/cache.h b/cache.h
index 0d101e4..4e7bb62 100644
--- a/cache.h
+++ b/cache.h
@@ -179,8 +179,7 @@ struct cache_entry {
 #define CE_UNHASHED  (0x200000)
 #define CE_CONFLICTED (0x800000)
 
-/* Only remove in work directory, not index */
-#define CE_WT_REMOVE (0x400000)
+#define CE_WT_REMOVE (0x400000) /* remove in work directory */
 
 #define CE_UNPACKED  (0x1000000)
 
diff --git a/t/t1011-read-tree-sparse-checkout.sh b/t/t1011-read-tree-sparse-checkout.sh
index 62246db..885578a 100755
--- a/t/t1011-read-tree-sparse-checkout.sh
+++ b/t/t1011-read-tree-sparse-checkout.sh
@@ -138,7 +138,7 @@ test_expect_success 'read-tree adds to worktree, absent case' '
 	test ! -f sub/added
 '
 
-test_expect_success 'read-tree adds to worktree, dirty case' '
+test_expect_failure 'read-tree adds to worktree, dirty case' '
 	echo init.t > .git/info/sparse-checkout &&
 	git checkout -f removed &&
 	mkdir sub &&
@@ -147,4 +147,11 @@ test_expect_success 'read-tree adds to worktree, dirty case' '
 	grep -q dirty sub/added
 '
 
+test_expect_success 'read-tree --reset removes outside worktree' '
+	echo init.t > .git/info/sparse-checkout &&
+	git checkout -f top &&
+	git reset --hard removed &&
+	test -z "$(git ls-files sub/added)"
+'
+
 test_done
diff --git a/unpack-trees.c b/unpack-trees.c
index e8f03f5..f2d148c 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -53,6 +53,9 @@ static void add_entry(struct unpack_trees_options *o, struct cache_entry *ce,
 
 	clear |= CE_HASHED | CE_UNHASHED;
 
+	if (set & CE_REMOVE)
+		set |= CE_WT_REMOVE;
+
 	memcpy(new, ce, size);
 	new->next = NULL;
 	new->ce_flags = (new->ce_flags & ~clear) | set;
@@ -84,7 +87,7 @@ static int check_updates(struct unpack_trees_options *o)
 	if (o->update && o->verbose_update) {
 		for (total = cnt = 0; cnt < index->cache_nr; cnt++) {
 			struct cache_entry *ce = index->cache[cnt];
-			if (ce->ce_flags & (CE_UPDATE | CE_REMOVE | CE_WT_REMOVE))
+			if (ce->ce_flags & (CE_UPDATE | CE_WT_REMOVE))
 				total++;
 		}
 
@@ -104,12 +107,6 @@ static int check_updates(struct unpack_trees_options *o)
 				unlink_entry(ce);
 			continue;
 		}
-
-		if (ce->ce_flags & CE_REMOVE) {
-			display_progress(progress, ++cnt);
-			if (o->update)
-				unlink_entry(ce);
-		}
 	}
 	remove_marked_cache_entries(&o->result);
 	remove_scheduled_dirs();
@@ -799,10 +796,15 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
 			/*
 			 * Merge strategies may set CE_UPDATE|CE_REMOVE outside checkout
 			 * area as a result of ce_skip_worktree() shortcuts in
-			 * verify_absent() and verify_uptodate(). Clear them.
+			 * verify_absent() and verify_uptodate().
+			 * Make sure they don't modify worktree.
 			 */
-			if (ce_skip_worktree(ce))
-				ce->ce_flags &= ~(CE_UPDATE | CE_REMOVE);
+			if (ce_skip_worktree(ce)) {
+				ce->ce_flags &= ~CE_UPDATE;
+
+				if (ce->ce_flags & CE_REMOVE)
+					ce->ce_flags &= ~CE_WT_REMOVE;
+			}
 			else
 				empty_worktree = 0;
 
-- 
1.7.1.rc1.69.g24c2f7

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH 2/3] unpack-trees.c: Do not check ce_stage in will_have_skip_worktree()
  2010-07-26  9:08 [PATCH 1/3] Fix sparse checkout not removing files from index Nguyễn Thái Ngọc Duy
@ 2010-07-26  9:08 ` Nguyễn Thái Ngọc Duy
  2010-07-31  2:11   ` Jonathan Nieder
  2010-07-26  9:08 ` [PATCH 3/3] Mark new entries skip-worktree appropriately Nguyễn Thái Ngọc Duy
  2010-07-30  1:35 ` [PATCH 1/3] Fix sparse checkout not removing files from index Jonathan Nieder
  2 siblings, 1 reply; 17+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2010-07-26  9:08 UTC (permalink / raw)
  To: git, Junio C Hamano; +Cc: Nguyễn Thái Ngọc Duy

The idea of sparse checkout is conflicted entries should always stay
in worktree, regardless $GIT_DIR/info/sparse-checkout. Therefore,
ce_stage(ce) usually means no CE_SKIP_WORKTREE. This is true when all
entries have been merged into the index, and identical staged entries
collapsed.

However, will_have_skip_worktree() since f1f523e (unpack-trees():
ignore worktree check outside checkout area) is also used earlier in
verify_* functions, when entries have not been merged to index yet
and ce_stage() may not be zero. Checking ce_stage() then can make
unnecessary verification on entries outside checkout area and error out.

This fixes part of test case "read-tree adds to worktree, dirty case".
The error is changed from

error: Untracked working tree file 'sub/added' would be overwritten by merge.

before this patch to

grep: sub/added: No such file or directory

which will be addressed in the next patch.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 unpack-trees.c |    5 +----
 1 files changed, 1 insertions(+), 4 deletions(-)

diff --git a/unpack-trees.c b/unpack-trees.c
index f2d148c..7ea8c5f 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -135,9 +135,6 @@ static int will_have_skip_worktree(const struct cache_entry *ce, struct unpack_t
 {
 	const char *basename;
 
-	if (ce_stage(ce))
-		return 0;
-
 	basename = strrchr(ce->name, '/');
 	basename = basename ? basename+1 : ce->name;
 	return excluded_from_list(ce->name, ce_namelen(ce), basename, NULL, o->el) <= 0;
@@ -147,7 +144,7 @@ static int apply_sparse_checkout(struct cache_entry *ce, struct unpack_trees_opt
 {
 	int was_skip_worktree = ce_skip_worktree(ce);
 
-	if (will_have_skip_worktree(ce, o))
+	if (!ce_stage(ce) && will_have_skip_worktree(ce, o))
 		ce->ce_flags |= CE_SKIP_WORKTREE;
 	else
 		ce->ce_flags &= ~CE_SKIP_WORKTREE;
-- 
1.7.1.rc1.69.g24c2f7

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH 3/3] Mark new entries skip-worktree appropriately
  2010-07-26  9:08 [PATCH 1/3] Fix sparse checkout not removing files from index Nguyễn Thái Ngọc Duy
  2010-07-26  9:08 ` [PATCH 2/3] unpack-trees.c: Do not check ce_stage in will_have_skip_worktree() Nguyễn Thái Ngọc Duy
@ 2010-07-26  9:08 ` Nguyễn Thái Ngọc Duy
  2010-07-31  2:32   ` Jonathan Nieder
  2010-07-30  1:35 ` [PATCH 1/3] Fix sparse checkout not removing files from index Jonathan Nieder
  2 siblings, 1 reply; 17+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2010-07-26  9:08 UTC (permalink / raw)
  To: git, Junio C Hamano; +Cc: Nguyễn Thái Ngọc Duy

Sparse checkout updates worktree based on the old and new
skip-worktree status when $GIT_DIR/info/sparse-checkout changes:

old = ce_skip_worktree(ce);           // current skip-worktree
new = will_have_skip_work_tree(ce);   // from $GIT..sparse-checkout
if (old && !new) add_file_back(ce);   // shrink checkout area
if (!old && new) remove_file_out(ce); [1] // enlarge checkout area

New entries after merging will always have skip-worktree unset
(i.e. old = 0). If those files are filtered out by
$GIT_DIR/info/sparse-checkout (i.e. new != 0), then case [1] will
happen. But there is nothing to remove because they're new.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 t/t1011-read-tree-sparse-checkout.sh |    2 +-
 unpack-trees.c                       |    2 ++
 2 files changed, 3 insertions(+), 1 deletions(-)

diff --git a/t/t1011-read-tree-sparse-checkout.sh b/t/t1011-read-tree-sparse-checkout.sh
index 885578a..86a6246 100755
--- a/t/t1011-read-tree-sparse-checkout.sh
+++ b/t/t1011-read-tree-sparse-checkout.sh
@@ -138,7 +138,7 @@ test_expect_success 'read-tree adds to worktree, absent case' '
 	test ! -f sub/added
 '
 
-test_expect_failure 'read-tree adds to worktree, dirty case' '
+test_expect_success 'read-tree adds to worktree, dirty case' '
 	echo init.t > .git/info/sparse-checkout &&
 	git checkout -f removed &&
 	mkdir sub &&
diff --git a/unpack-trees.c b/unpack-trees.c
index 7ea8c5f..f98b1cb 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -1091,6 +1091,8 @@ static int merged_entry(struct cache_entry *merge, struct cache_entry *old,
 	if (!old) {
 		if (verify_absent(merge, "overwritten", o))
 			return -1;
+		if (!o->skip_sparse_checkout && will_have_skip_worktree(merge, o))
+			update |= CE_SKIP_WORKTREE;
 		invalidate_ce_path(merge, o);
 	} else if (!(old->ce_flags & CE_CONFLICTED)) {
 		/*
-- 
1.7.1.rc1.69.g24c2f7

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* Re: [PATCH 1/3] Fix sparse checkout not removing files from index
  2010-07-26  9:08 [PATCH 1/3] Fix sparse checkout not removing files from index Nguyễn Thái Ngọc Duy
  2010-07-26  9:08 ` [PATCH 2/3] unpack-trees.c: Do not check ce_stage in will_have_skip_worktree() Nguyễn Thái Ngọc Duy
  2010-07-26  9:08 ` [PATCH 3/3] Mark new entries skip-worktree appropriately Nguyễn Thái Ngọc Duy
@ 2010-07-30  1:35 ` Jonathan Nieder
  2010-07-30  8:24   ` Nguyen Thai Ngoc Duy
  2 siblings, 1 reply; 17+ messages in thread
From: Jonathan Nieder @ 2010-07-30  1:35 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: git, Junio C Hamano

Nguyễn Thái Ngọc Duy wrote:

> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>

The log message left me confused.  Let’s see if the code is easier.

> +++ b/cache.h
> @@ -179,8 +179,7 @@ struct cache_entry {
>  #define CE_UNHASHED  (0x200000)
>  #define CE_CONFLICTED (0x800000)
>  
> -/* Only remove in work directory, not index */
> -#define CE_WT_REMOVE (0x400000)
> +#define CE_WT_REMOVE (0x400000) /* remove in work directory */

Before[1], CE_REMOVE was used by read-tree et al in core to represent
something to be removed from the index file and work tree (e.g., when
switching branches).

In the new order, one uses CE_REMOVE|CE_WT_REMOVE for that, right?

> +++ b/t/t1011-read-tree-sparse-checkout.sh
> @@ -147,4 +147,11 @@ test_expect_success 'read-tree adds to worktree, dirty case' '
>  	grep -q dirty sub/added
>  '
>  
> +test_expect_success 'read-tree --reset removes outside worktree' '
> +	echo init.t > .git/info/sparse-checkout &&
> +	git checkout -f top &&
> +	git reset --hard removed &&
> +	test -z "$(git ls-files sub/added)"
> +'
> +

Using reset --hard to remove a file outside the sparse checkout.
A sane, simple test; thanks.  (Nitpick: I would have used

	>empty &&
	git ls-files sub/added >output &&
	test_cmp empty output

even though that does not produce any more helpful output in this
case.)

> +++ b/unpack-trees.c
> @@ -53,6 +53,9 @@ static void add_entry(struct unpack_trees_options *o, struct cache_entry *ce,
>  
>  	clear |= CE_HASHED | CE_UNHASHED;
>  
> +	if (set & CE_REMOVE)
> +		set |= CE_WT_REMOVE;
> +

A bridge between the old and new worlds.

I would have added CE_WT_REMOVE to callers instead (they all pass
constants more or less), but I suppose this way makes the patch
shorter.

[...]
> @@ -84,7 +87,7 @@ static int check_updates(struct unpack_trees_options *o)
>  	if (o->update && o->verbose_update) {
>  		for (total = cnt = 0; cnt < index->cache_nr; cnt++) {
>  			struct cache_entry *ce = index->cache[cnt];
> -			if (ce->ce_flags & (CE_UPDATE | CE_REMOVE | CE_WT_REMOVE))
> +			if (ce->ce_flags & (CE_UPDATE | CE_WT_REMOVE))

Only entries marked CE_WT_REMOVE will be touched by read-tree -u.

> @@ -104,12 +107,6 @@ static int check_updates(struct unpack_trees_options *o)
>  				unlink_entry(ce);
>  			continue;
>  		}
> -
> -		if (ce->ce_flags & CE_REMOVE) {
[...]

and the CE_WT_REMOVE case takes care of that.  Makes sense.

> @@ -799,10 +796,15 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
>  			/*
>  			 * Merge strategies may set CE_UPDATE|CE_REMOVE outside checkout
>  			 * area as a result of ce_skip_worktree() shortcuts in
> -			 * verify_absent() and verify_uptodate(). Clear them.
> +			 * verify_absent() and verify_uptodate().
> +			 * Make sure they don't modify worktree.
>  			 */
> -			if (ce_skip_worktree(ce))
> -				ce->ce_flags &= ~(CE_UPDATE | CE_REMOVE);
> +			if (ce_skip_worktree(ce)) {
> +				ce->ce_flags &= ~CE_UPDATE;
> +
> +				if (ce->ce_flags & CE_REMOVE)
> +					ce->ce_flags &= ~CE_WT_REMOVE;
> +			}
>  			else
>  				empty_worktree = 0;

Ah, and at last we come to the fix. :)

This is a little tricky: the CE_WT_REMOVE case (without CE_REMOVE)
represents a narrowing of the checkout and should be preserved,
while CE_WT_REMOVE|CE_REMOVE represents a removed index entry and
should be changed to just CE_REMOVE.

But it is a clear improvement over the code from before and should
behave as advertised now.  So aside from the log message,

Reviewed-by: Jonathan Nieder <jrnieder@gmail.com>

Thanks.

[1] v1.6.6.1~23^2~1 (Make on-disk index representation separate from
in-core one, 2008-01-14) and v0.99~295 (git-read-tree: remove deleted
files in the working directory, 2005-06-09).

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 1/3] Fix sparse checkout not removing files from index
  2010-07-30  1:35 ` [PATCH 1/3] Fix sparse checkout not removing files from index Jonathan Nieder
@ 2010-07-30  8:24   ` Nguyen Thai Ngoc Duy
  2010-07-30 19:50     ` Jonathan Nieder
  0 siblings, 1 reply; 17+ messages in thread
From: Nguyen Thai Ngoc Duy @ 2010-07-30  8:24 UTC (permalink / raw)
  To: Jonathan Nieder; +Cc: git, Junio C Hamano

2010/7/30 Jonathan Nieder <jrnieder@gmail.com>:
>> +++ b/cache.h
>> @@ -179,8 +179,7 @@ struct cache_entry {
>>  #define CE_UNHASHED  (0x200000)
>>  #define CE_CONFLICTED (0x800000)
>>
>> -/* Only remove in work directory, not index */
>> -#define CE_WT_REMOVE (0x400000)
>> +#define CE_WT_REMOVE (0x400000) /* remove in work directory */
>
> Before[1], CE_REMOVE was used by read-tree et al in core to represent
> something to be removed from the index file and work tree (e.g., when
> switching branches).
>
> In the new order, one uses CE_REMOVE|CE_WT_REMOVE for that, right?

For unpack_trees() only, yes. You made me grep for CE_REMOVE and found
that do_diff_cache() may turn CE_REMOVE on. The function is used by
blame and reset.

Hmm.. It's for removing staged entries. So it's probably fine.

>> +++ b/t/t1011-read-tree-sparse-checkout.sh
>> @@ -147,4 +147,11 @@ test_expect_success 'read-tree adds to worktree, dirty case' '
>>       grep -q dirty sub/added
>>  '
>>
>> +test_expect_success 'read-tree --reset removes outside worktree' '
>> +     echo init.t > .git/info/sparse-checkout &&
>> +     git checkout -f top &&
>> +     git reset --hard removed &&
>> +     test -z "$(git ls-files sub/added)"
>> +'
>> +
>
> Using reset --hard to remove a file outside the sparse checkout.
> A sane, simple test; thanks.  (Nitpick: I would have used
>
>        >empty &&
>        git ls-files sub/added >output &&
>        test_cmp empty output
>
> even though that does not produce any more helpful output in this
> case.)

Thanks. That looks better.

>
>> +++ b/unpack-trees.c
>> @@ -53,6 +53,9 @@ static void add_entry(struct unpack_trees_options *o, struct cache_entry *ce,
>>
>>       clear |= CE_HASHED | CE_UNHASHED;
>>
>> +     if (set & CE_REMOVE)
>> +             set |= CE_WT_REMOVE;
>> +
>
> A bridge between the old and new worlds.
>
> I would have added CE_WT_REMOVE to callers instead (they all pass
> constants more or less), but I suppose this way makes the patch
> shorter.

It'd better be done in one place. I don't expect anybody to set
CE_REMOVE without CE_WT_REMOVE any time soon. Adding it the the
callers, the new callers might miss it.

>> @@ -799,10 +796,15 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
>>                       /*
>>                        * Merge strategies may set CE_UPDATE|CE_REMOVE outside checkout
>>                        * area as a result of ce_skip_worktree() shortcuts in
>> -                      * verify_absent() and verify_uptodate(). Clear them.
>> +                      * verify_absent() and verify_uptodate().
>> +                      * Make sure they don't modify worktree.
>>                        */
>> -                     if (ce_skip_worktree(ce))
>> -                             ce->ce_flags &= ~(CE_UPDATE | CE_REMOVE);
>> +                     if (ce_skip_worktree(ce)) {
>> +                             ce->ce_flags &= ~CE_UPDATE;
>> +
>> +                             if (ce->ce_flags & CE_REMOVE)
>> +                                     ce->ce_flags &= ~CE_WT_REMOVE;
>> +                     }
>>                       else
>>                               empty_worktree = 0;
>
> Ah, and at last we come to the fix. :)
>
> This is a little tricky: the CE_WT_REMOVE case (without CE_REMOVE)
> represents a narrowing of the checkout and should be preserved,
> while CE_WT_REMOVE|CE_REMOVE represents a removed index entry and
> should be changed to just CE_REMOVE.

Yeah. I did wonder if it's worth a comment to explain. I forget why I
did not add that comment now.
-- 
Duy

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 1/3] Fix sparse checkout not removing files from index
  2010-07-30  8:24   ` Nguyen Thai Ngoc Duy
@ 2010-07-30 19:50     ` Jonathan Nieder
  2010-07-31  1:04       ` Jonathan Nieder
  0 siblings, 1 reply; 17+ messages in thread
From: Jonathan Nieder @ 2010-07-30 19:50 UTC (permalink / raw)
  To: Nguyen Thai Ngoc Duy; +Cc: git, Junio C Hamano

Nguyen Thai Ngoc Duy wrote:

> For unpack_trees() only, yes.

Ah, that is what I was missing.

So would the following be correct?

	Subject: unpack-trees: let read-tree -u remove index entries outside sparse area

	To avoid touching the worktree outside a sparse checkout,
	when the update flag is enabled unpack_trees() clears the
	CE_UPDATE and CE_REMOVE flags on entries that do not match the
	sparse pattern before actually committing any updates to the
	index file or worktree.

	The effect on the index was unintentional; sparse checkout was
	never meant to prevent index updates outside the area checked
	out.  And the result is very confusing: for example, after a
	failed merge, currently "git reset --hard" does not reset the
	state completely but an additional "git reset --mixed" will.

	So stop clearing the CE_REMOVE flag.  Instead, maintain a
	CE_WT_REMOVE flag to separately track whether a particular
	file removal should apply to the worktree in addition to the
	index or not.

	The CE_WT_REMOVE flag is used already to mark files that
	should be removed because of a narrowing checkout area.  That
	usage will still apply; do not clear the CE_WT_REMOVE flag
	in that case (detectable because the CE_REMOVE flag is not
	set).

	This bug masked some other bugs illustrated by the test
	suite, which will be addressed by later patches.

	Reported-by: Frédéric Brière <fbriere@fbriere.net>
	Fixes: http://bugs.debian.org/583699

> It'd better be done in one place.

I think I understand better now, and now I agree.  (This patch is not
a change in the semantics of CE_REMOVE, but just some new unpack-trees
bookkeeping.)

> 2010/7/30 Jonathan Nieder <jrnieder@gmail.com>:

>> This is a little tricky: the CE_WT_REMOVE case (without CE_REMOVE)
>> represents a narrowing of the checkout and should be preserved,
>> while CE_WT_REMOVE|CE_REMOVE represents a removed index entry and
>> should be changed to just CE_REMOVE.
>
> Yeah. I did wonder if it's worth a comment to explain. I forget why I
> did not add that comment now.

What happens if an index entry is removed at the same time as the
checkout is narrowed?

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 1/3] Fix sparse checkout not removing files from index
  2010-07-30 19:50     ` Jonathan Nieder
@ 2010-07-31  1:04       ` Jonathan Nieder
  2010-07-31  1:05         ` [PATCH 1/2] t1011 (sparse checkout): style nitpicks Jonathan Nieder
                           ` (2 more replies)
  0 siblings, 3 replies; 17+ messages in thread
From: Jonathan Nieder @ 2010-07-31  1:04 UTC (permalink / raw)
  To: Nguyen Thai Ngoc Duy; +Cc: git, Junio C Hamano

Jonathan Nieder wrote:

> What happens if an index entry is removed at the same time as the
> checkout is narrowed?

Hm, maybe something like this would help.

Jonathan Nieder (2):
  t1011 (sparse checkout): style nitpicks
  read-tree -m -u: always remove relevant files when narrowing checkout

 cache.h                              |    3 +-
 t/t1011-read-tree-sparse-checkout.sh |  129 +++++++++++++++++++++-------------
 unpack-trees.c                       |   42 +++--------
 3 files changed, 94 insertions(+), 80 deletions(-)

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH 1/2] t1011 (sparse checkout): style nitpicks
  2010-07-31  1:04       ` Jonathan Nieder
@ 2010-07-31  1:05         ` Jonathan Nieder
  2010-07-31  1:09         ` [PATCH 2/2] read-tree -m -u: always remove relevant files when narrowing checkout Jonathan Nieder
  2010-07-31  3:28         ` [PATCH 1/3] Fix sparse checkout not removing files from index Nguyen Thai Ngoc Duy
  2 siblings, 0 replies; 17+ messages in thread
From: Jonathan Nieder @ 2010-07-31  1:05 UTC (permalink / raw)
  To: Nguyen Thai Ngoc Duy; +Cc: git, Junio C Hamano

The exit status of "git ls-files" in

	test -z "$(git ls-files sub/added)"

is ignored.  So redirect output to a file and compare to an
empty file with test_cmp instead.

While changing that, tweak the rest of the script to more closely
follow the test style guide.  Guarding setup commands with
test_expect_success makes it easy to see the scope in which some
particular data is used; removal of whitespace after >redirection
operators is just for consistency.

Cc: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
 t/t1011-read-tree-sparse-checkout.sh |  108 ++++++++++++++++++---------------
 1 files changed, 59 insertions(+), 49 deletions(-)

diff --git a/t/t1011-read-tree-sparse-checkout.sh b/t/t1011-read-tree-sparse-checkout.sh
index 885578a..2955071 100755
--- a/t/t1011-read-tree-sparse-checkout.sh
+++ b/t/t1011-read-tree-sparse-checkout.sh
@@ -1,16 +1,30 @@
 #!/bin/sh
 
-test_description='sparse checkout tests'
+test_description='sparse checkout tests
+
+* (tag: removed, master) removed
+| D	sub/added
+* (HEAD, tag: top) modified and added
+| M	init.t
+| A	sub/added
+* (tag: init) init
+  A	init.t
+'
 
 . ./test-lib.sh
 
-cat >expected <<EOF
-100644 77f0ba1734ed79d12881f81b36ee134de6a3327b 0	init.t
-100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0	sub/added
-EOF
 test_expect_success 'setup' '
+	cat >expected <<-\EOF &&
+	100644 77f0ba1734ed79d12881f81b36ee134de6a3327b 0	init.t
+	100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0	sub/added
+	EOF
+	cat >expected.swt <<-\EOF &&
+	H init.t
+	H sub/added
+	EOF
+
 	test_commit init &&
-	echo modified >> init.t &&
+	echo modified >>init.t &&
 	mkdir sub &&
 	touch sub/added &&
 	git add init.t sub/added &&
@@ -20,26 +34,22 @@ test_expect_success 'setup' '
 	git commit -m removed &&
 	git tag removed &&
 	git checkout top &&
-	git ls-files --stage > result &&
+	git ls-files --stage >result &&
 	test_cmp expected result
 '
 
-cat >expected.swt <<EOF
-H init.t
-H sub/added
-EOF
 test_expect_success 'read-tree without .git/info/sparse-checkout' '
 	git read-tree -m -u HEAD &&
-	git ls-files --stage > result &&
+	git ls-files --stage >result &&
 	test_cmp expected result &&
-	git ls-files -t > result &&
+	git ls-files -t >result &&
 	test_cmp expected.swt result
 '
 
 test_expect_success 'read-tree with .git/info/sparse-checkout but disabled' '
-	echo > .git/info/sparse-checkout
+	echo >.git/info/sparse-checkout
 	git read-tree -m -u HEAD &&
-	git ls-files -t > result &&
+	git ls-files -t >result &&
 	test_cmp expected.swt result &&
 	test -f init.t &&
 	test -f sub/added
@@ -47,9 +57,9 @@ test_expect_success 'read-tree with .git/info/sparse-checkout but disabled' '
 
 test_expect_success 'read-tree --no-sparse-checkout with empty .git/info/sparse-checkout and enabled' '
 	git config core.sparsecheckout true &&
-	echo > .git/info/sparse-checkout &&
+	echo >.git/info/sparse-checkout &&
 	git read-tree --no-sparse-checkout -m -u HEAD &&
-	git ls-files -t > result &&
+	git ls-files -t >result &&
 	test_cmp expected.swt result &&
 	test -f init.t &&
 	test -f sub/added
@@ -57,101 +67,101 @@ test_expect_success 'read-tree --no-sparse-checkout with empty .git/info/sparse-
 
 test_expect_success 'read-tree with empty .git/info/sparse-checkout' '
 	git config core.sparsecheckout true &&
-	echo > .git/info/sparse-checkout &&
+	echo >.git/info/sparse-checkout &&
 	test_must_fail git read-tree -m -u HEAD &&
-	git ls-files --stage > result &&
+	git ls-files --stage >result &&
 	test_cmp expected result &&
-	git ls-files -t > result &&
+	git ls-files -t >result &&
 	test_cmp expected.swt result &&
 	test -f init.t &&
 	test -f sub/added
 '
 
-cat >expected.swt <<EOF
-S init.t
-H sub/added
-EOF
 test_expect_success 'match directories with trailing slash' '
+	cat >expected.swt-noinit <<-\EOF &&
+	S init.t
+	H sub/added
+	EOF
+
 	echo sub/ > .git/info/sparse-checkout &&
 	git read-tree -m -u HEAD &&
 	git ls-files -t > result &&
-	test_cmp expected.swt result &&
+	test_cmp expected.swt-noinit result &&
 	test ! -f init.t &&
 	test -f sub/added
 '
 
-cat >expected.swt <<EOF
-H init.t
-H sub/added
-EOF
 test_expect_failure 'match directories without trailing slash' '
-	echo init.t > .git/info/sparse-checkout &&
-	echo sub >> .git/info/sparse-checkout &&
+	echo init.t >.git/info/sparse-checkout &&
+	echo sub >>.git/info/sparse-checkout &&
 	git read-tree -m -u HEAD &&
-	git ls-files -t > result &&
+	git ls-files -t >result &&
 	test_cmp expected.swt result &&
 	test ! -f init.t &&
 	test -f sub/added
 '
 
-cat >expected.swt <<EOF
-H init.t
-S sub/added
-EOF
 test_expect_success 'checkout area changes' '
-	echo init.t > .git/info/sparse-checkout &&
+	cat >expected.swt-nosub <<-\EOF &&
+	H init.t
+	S sub/added
+	EOF
+
+	echo init.t >.git/info/sparse-checkout &&
 	git read-tree -m -u HEAD &&
-	git ls-files -t > result &&
-	test_cmp expected.swt result &&
+	git ls-files -t >result &&
+	test_cmp expected.swt-nosub result &&
 	test -f init.t &&
 	test ! -f sub/added
 '
 
 test_expect_success 'read-tree updates worktree, absent case' '
-	echo sub/added > .git/info/sparse-checkout &&
+	echo sub/added >.git/info/sparse-checkout &&
 	git checkout -f top &&
 	git read-tree -m -u HEAD^ &&
 	test ! -f init.t
 '
 
 test_expect_success 'read-tree updates worktree, dirty case' '
-	echo sub/added > .git/info/sparse-checkout &&
+	echo sub/added >.git/info/sparse-checkout &&
 	git checkout -f top &&
-	echo dirty > init.t &&
+	echo dirty >init.t &&
 	git read-tree -m -u HEAD^ &&
 	grep -q dirty init.t &&
 	rm init.t
 '
 
 test_expect_success 'read-tree removes worktree, dirty case' '
-	echo init.t > .git/info/sparse-checkout &&
+	echo init.t >.git/info/sparse-checkout &&
 	git checkout -f top &&
-	echo dirty > added &&
+	echo dirty >added &&
 	git read-tree -m -u HEAD^ &&
 	grep -q dirty added
 '
 
 test_expect_success 'read-tree adds to worktree, absent case' '
-	echo init.t > .git/info/sparse-checkout &&
+	echo init.t >.git/info/sparse-checkout &&
 	git checkout -f removed &&
 	git read-tree -u -m HEAD^ &&
 	test ! -f sub/added
 '
 
 test_expect_failure 'read-tree adds to worktree, dirty case' '
-	echo init.t > .git/info/sparse-checkout &&
+	echo init.t >.git/info/sparse-checkout &&
 	git checkout -f removed &&
 	mkdir sub &&
-	echo dirty > sub/added &&
+	echo dirty >sub/added &&
 	git read-tree -u -m HEAD^ &&
 	grep -q dirty sub/added
 '
 
 test_expect_success 'read-tree --reset removes outside worktree' '
-	echo init.t > .git/info/sparse-checkout &&
+	>empty &&
+	echo init.t >.git/info/sparse-checkout &&
 	git checkout -f top &&
 	git reset --hard removed &&
-	test -z "$(git ls-files sub/added)"
+	git ls-files sub/added >result &&
+	test_cmp empty result
 '
 
 test_done
-- 
1.7.2.1.544.ga752d.dirty

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH 2/2] read-tree -m -u: always remove relevant files when narrowing checkout
  2010-07-31  1:04       ` Jonathan Nieder
  2010-07-31  1:05         ` [PATCH 1/2] t1011 (sparse checkout): style nitpicks Jonathan Nieder
@ 2010-07-31  1:09         ` Jonathan Nieder
  2010-07-31  3:28         ` [PATCH 1/3] Fix sparse checkout not removing files from index Nguyen Thai Ngoc Duy
  2 siblings, 0 replies; 17+ messages in thread
From: Jonathan Nieder @ 2010-07-31  1:09 UTC (permalink / raw)
  To: Nguyen Thai Ngoc Duy; +Cc: git, Junio C Hamano

When narrowing the checkout pattern to exclude a file that the merge
was going to delete, read-tree decides two wrongs make a right and
leaves the file behind.

Or that’s what it looks like.  Actually, for some reason
apply_sparse_checkout() is not paying proper attention to entries
removed by the merge, so they look like ordinary deletions outside
the sparse area.

While fixing that, clarify the semantics of the unpack-trees flags:

 - CE_UPDATE marks an entry for which the worktree might need
   to be updated to match.  If CE_UPDATE is not set, changes
   only apply to the index file.

 - CE_REMOVE marks an entry removed by the merge (just like
   it did before).

 - CE_WT_REMOVE marks an entry that no longer matches the sparse
   pattern.

Cc: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
Sorry for the lack of clarity before.  This is just to give the
idea; I have no confidence that I am not breaking something,
especially because I have not checked why the "if" I am removing
in apply_sparse_checkout was added.

 cache.h                              |    3 +-
 t/t1011-read-tree-sparse-checkout.sh |   21 +++++++++++++++++
 unpack-trees.c                       |   42 +++++++++------------------------
 3 files changed, 35 insertions(+), 31 deletions(-)

diff --git a/cache.h b/cache.h
index 3c7fb6f..c9fa3df 100644
--- a/cache.h
+++ b/cache.h
@@ -179,7 +179,8 @@ struct cache_entry {
 #define CE_UNHASHED  (0x200000)
 #define CE_CONFLICTED (0x800000)
 
-#define CE_WT_REMOVE (0x400000) /* remove in work directory */
+/* Only remove in work directory, not index */
+#define CE_WT_REMOVE (0x400000)
 
 #define CE_UNPACKED  (0x1000000)
 
diff --git a/t/t1011-read-tree-sparse-checkout.sh b/t/t1011-read-tree-sparse-checkout.sh
index 2955071..247f295 100755
--- a/t/t1011-read-tree-sparse-checkout.sh
+++ b/t/t1011-read-tree-sparse-checkout.sh
@@ -155,6 +155,27 @@ test_expect_failure 'read-tree adds to worktree, dirty case' '
 	grep -q dirty sub/added
 '
 
+test_expect_success 'narrow checkout while removing file' '
+	echo "H init.t" >expected.swt-after &&
+	printf "%s\n" init.t sub/added | sort >expected.ls-before
+	echo init.t >expected.ls-after &&
+
+	printf "%s\n" init.t sub/added >.git/info/sparse-checkout &&
+	git checkout -f top &&
+	git ls-files -t >index.before &&
+	ls init.t sub/added | sort >worktree.before &&
+
+	echo init.t >.git/info/sparse-checkout &&
+	git read-tree -m -u HEAD^ &&
+	git ls-files -t >index.after &&
+	ls init.t sub/added | sort >worktree.after &&
+
+	test_cmp expected.swt index.before &&
+	test_cmp expected.swt-after index.after &&
+	test_cmp expected.ls-before worktree.before &&
+	test_cmp expected.ls-after worktree.after
+'
+
 test_expect_success 'read-tree --reset removes outside worktree' '
 	>empty &&
 	echo init.t >.git/info/sparse-checkout &&
diff --git a/unpack-trees.c b/unpack-trees.c
index 523e1de..deabfd9 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -53,9 +53,6 @@ static void add_entry(struct unpack_trees_options *o, struct cache_entry *ce,
 
 	clear |= CE_HASHED | CE_UNHASHED;
 
-	if (set & CE_REMOVE)
-		set |= CE_WT_REMOVE;
-
 	memcpy(new, ce, size);
 	new->next = NULL;
 	new->ce_flags = (new->ce_flags & ~clear) | set;
@@ -87,7 +84,7 @@ static int check_updates(struct unpack_trees_options *o)
 	if (o->update && o->verbose_update) {
 		for (total = cnt = 0; cnt < index->cache_nr; cnt++) {
 			struct cache_entry *ce = index->cache[cnt];
-			if (ce->ce_flags & (CE_UPDATE | CE_WT_REMOVE))
+			if (ce->ce_flags & CE_UPDATE)
 				total++;
 		}
 
@@ -101,11 +98,12 @@ static int check_updates(struct unpack_trees_options *o)
 	for (i = 0; i < index->cache_nr; i++) {
 		struct cache_entry *ce = index->cache[i];
 
-		if (ce->ce_flags & CE_WT_REMOVE) {
+		if (ce->ce_flags & CE_UPDATE &&
+		    ce->ce_flags & (CE_REMOVE | CE_WT_REMOVE)) {
 			display_progress(progress, ++cnt);
+			ce->ce_flags &= ~CE_UPDATE & ~CE_WT_REMOVE;
 			if (o->update)
 				unlink_entry(ce);
-			continue;
 		}
 	}
 	remove_marked_cache_entries(&o->result);
@@ -152,15 +150,6 @@ static int apply_sparse_checkout(struct cache_entry *ce, struct unpack_trees_opt
 	else
 		ce->ce_flags &= ~CE_SKIP_WORKTREE;
 
-	/*
-	 * We only care about files getting into the checkout area
-	 * If merge strategies want to remove some, go ahead, this
-	 * flag will be removed eventually in unpack_trees() if it's
-	 * outside checkout area.
-	 */
-	if (ce->ce_flags & CE_REMOVE)
-		return 0;
-
 	if (!was_skip_worktree && ce_skip_worktree(ce)) {
 		/*
 		 * If CE_UPDATE is set, verify_uptodate() must be called already
@@ -169,7 +158,7 @@ static int apply_sparse_checkout(struct cache_entry *ce, struct unpack_trees_opt
 		 */
 		if (!(ce->ce_flags & CE_UPDATE) && verify_uptodate_sparse(ce, o))
 			return -1;
-		ce->ce_flags |= CE_WT_REMOVE;
+		ce->ce_flags |= CE_WT_REMOVE | CE_UPDATE;
 	}
 	if (was_skip_worktree && !ce_skip_worktree(ce)) {
 		if (verify_absent_sparse(ce, "overwritten", o))
@@ -796,20 +785,13 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
 				goto done;
 			}
 			/*
-			 * Merge strategies may set CE_UPDATE|CE_REMOVE outside checkout
-			 * area as a result of ce_skip_worktree() shortcuts in
-			 * verify_absent() and verify_uptodate().
-			 * Make sure they don't modify worktree.
+			 * Do not modify worktree outside of sparse checkout area,
+			 * except to make the checkout more narrow.
 			 */
-			if (ce_skip_worktree(ce)) {
-				ce->ce_flags &= ~CE_UPDATE;
-
-				if (ce->ce_flags & CE_REMOVE)
-					ce->ce_flags &= ~CE_WT_REMOVE;
-			}
-			else
+			if (!ce_skip_worktree(ce))
 				empty_worktree = 0;
-
+			else if (!(ce->ce_flags & CE_WT_REMOVE))
+				ce->ce_flags &= ~CE_UPDATE;
 		}
 		if (o->result.cache_nr && empty_worktree) {
 			ret = unpack_failed(o, "Sparse checkout leaves no entry on working directory");
@@ -968,7 +950,7 @@ static int verify_clean_subdirectory(struct cache_entry *ce, const char *action,
 		if (!ce_stage(ce2)) {
 			if (verify_uptodate(ce2, o))
 				return -1;
-			add_entry(o, ce2, CE_REMOVE, 0);
+			add_entry(o, ce2, CE_REMOVE|CE_UPDATE, 0);
 			mark_ce_used(ce2, o);
 		}
 		cnt++;
@@ -1138,7 +1120,7 @@ static int deleted_entry(struct cache_entry *ce, struct cache_entry *old,
 	}
 	if (!(old->ce_flags & CE_CONFLICTED) && verify_uptodate(old, o))
 		return -1;
-	add_entry(o, ce, CE_REMOVE, 0);
+	add_entry(o, ce, CE_REMOVE|CE_UPDATE, 0);
 	invalidate_ce_path(ce, o);
 	return 1;
 }
-- 
1.7.2.1.544.ga752d.dirty

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* Re: [PATCH 2/3] unpack-trees.c: Do not check ce_stage in will_have_skip_worktree()
  2010-07-26  9:08 ` [PATCH 2/3] unpack-trees.c: Do not check ce_stage in will_have_skip_worktree() Nguyễn Thái Ngọc Duy
@ 2010-07-31  2:11   ` Jonathan Nieder
  2010-07-31  3:12     ` Nguyen Thai Ngoc Duy
  0 siblings, 1 reply; 17+ messages in thread
From: Jonathan Nieder @ 2010-07-31  2:11 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: git, Junio C Hamano

Nguyễn Thái Ngọc Duy wrote:

> The idea of sparse checkout is conflicted entries should always stay
> in worktree, regardless $GIT_DIR/info/sparse-checkout. Therefore,
> ce_stage(ce) usually means no CE_SKIP_WORKTREE. This is true when all
> entries have been merged into the index, and identical staged entries
> collapsed.
> 
> However, will_have_skip_worktree() since f1f523e (unpack-trees():
> ignore worktree check outside checkout area) is also used earlier in
> verify_* functions, when entries have not been merged to index yet
> and ce_stage() may not be zero. Checking ce_stage() then can make
> unnecessary verification on entries outside checkout area and error out.

s/make/provoke/?

So: conflicts from unmerged index entries are supposed to be kept in
the work tree; but unpack_trees() is checking too early and seeing
conflicts where there are none.  Do I understand correctly?

> This fixes part of test case "read-tree adds to worktree, dirty case".

A summary might help people who do not remember the test:

	So attempting to add a file outside the checkout area 
	where there is a file already present (as in
	t1011.13 "read-tree adds to worktree, dirty case")
	will no longer error out with

	error: Untracked working tree file 'sub/added' would be overwritten by merge.

	but instead will remove the file.  (The latter problem will be
	addressed in a later patch.)

> +++ a/unpack-trees.c
> +++ b/unpack-trees.c
> @@ -135,9 +135,6 @@ static int will_have_skip_worktree(const struct cache_entry *ce, struct unpack_t
>  {
>  	const char *basename;
>  
> -	if (ce_stage(ce))
> -		return 0;
> -
>  	basename = strrchr(ce->name, '/');
>  	basename = basename ? basename+1 : ce->name;
>  	return excluded_from_list(ce->name, ce_namelen(ce), basename, NULL, o->el) <= 0;
> @@ -147,7 +144,7 @@ static int apply_sparse_checkout(struct cache_entry *ce, struct unpack_trees_opt
>  {
>  	int was_skip_worktree = ce_skip_worktree(ce);
>  
> -	if (will_have_skip_worktree(ce, o))
> +	if (!ce_stage(ce) && will_have_skip_worktree(ce, o))
>  		ce->ce_flags |= CE_SKIP_WORKTREE;
>  	else
>  		ce->ce_flags &= ~CE_SKIP_WORKTREE;

Makes sense.

Thanks,
Jonathan

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 3/3] Mark new entries skip-worktree appropriately
  2010-07-26  9:08 ` [PATCH 3/3] Mark new entries skip-worktree appropriately Nguyễn Thái Ngọc Duy
@ 2010-07-31  2:32   ` Jonathan Nieder
  2010-07-31  3:13     ` Nguyen Thai Ngoc Duy
  0 siblings, 1 reply; 17+ messages in thread
From: Jonathan Nieder @ 2010-07-31  2:32 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: git, Junio C Hamano

Nguyễn Thái Ngọc Duy wrote:

> Sparse checkout updates worktree based on the old and new
> skip-worktree status when $GIT_DIR/info/sparse-checkout changes:
> 
> old = ce_skip_worktree(ce);           // current skip-worktree
> new = will_have_skip_work_tree(ce);   // from $GIT..sparse-checkout
> if (old && !new) add_file_back(ce);   // shrink checkout area
> if (!old && new) remove_file_out(ce); [1] // enlarge checkout area
> 
> New entries after merging will always have skip-worktree unset
> (i.e. old = 0). If those files are filtered out by
> $GIT_DIR/info/sparse-checkout (i.e. new != 0), then case [1] will
> happen. But there is nothing to remove because they're new.

When using unpack_trees to add a new file, there is no in-file
index entry to grab the previous skip worktree bit from.  If it
is outside the checkout area, we should pretend it was skipped before,
too; otherwise a checkout can cause a file on disk whose name
coincides with a newly added outside-worktree file to be deleted.

Do I understand correctly?

> +++ b/unpack-trees.c
> @@ -1091,6 +1091,8 @@ static int merged_entry(struct cache_entry *merge, struct cache_entry *old,
>  	if (!old) {
>  		if (verify_absent(merge, "overwritten", o))
>  			return -1;
> +		if (!o->skip_sparse_checkout && will_have_skip_worktree(merge, o))
> +			update |= CE_SKIP_WORKTREE;
>  		invalidate_ce_path(merge, o);
>  	} else if (!(old->ce_flags & CE_CONFLICTED)) {
>  		/*

Looks sane.

Thanks for the fixes,
Jonathan

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 2/3] unpack-trees.c: Do not check ce_stage in  will_have_skip_worktree()
  2010-07-31  2:11   ` Jonathan Nieder
@ 2010-07-31  3:12     ` Nguyen Thai Ngoc Duy
  0 siblings, 0 replies; 17+ messages in thread
From: Nguyen Thai Ngoc Duy @ 2010-07-31  3:12 UTC (permalink / raw)
  To: Jonathan Nieder; +Cc: git, Junio C Hamano

2010/7/31 Jonathan Nieder <jrnieder@gmail.com>:
> Nguyễn Thái Ngọc Duy wrote:
>
>> The idea of sparse checkout is conflicted entries should always stay
>> in worktree, regardless $GIT_DIR/info/sparse-checkout. Therefore,
>> ce_stage(ce) usually means no CE_SKIP_WORKTREE. This is true when all
>> entries have been merged into the index, and identical staged entries
>> collapsed.
>>
>> However, will_have_skip_worktree() since f1f523e (unpack-trees():
>> ignore worktree check outside checkout area) is also used earlier in
>> verify_* functions, when entries have not been merged to index yet
>> and ce_stage() may not be zero. Checking ce_stage() then can make
>> unnecessary verification on entries outside checkout area and error out.
>
> s/make/provoke/?
>
> So: conflicts from unmerged index entries are supposed to be kept in
> the work tree; but unpack_trees() is checking too early and seeing
> conflicts where there are none.  Do I understand correctly?

Correct.
-- 
Duy

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 3/3] Mark new entries skip-worktree appropriately
  2010-07-31  2:32   ` Jonathan Nieder
@ 2010-07-31  3:13     ` Nguyen Thai Ngoc Duy
  2010-07-31  3:29       ` Jonathan Nieder
  0 siblings, 1 reply; 17+ messages in thread
From: Nguyen Thai Ngoc Duy @ 2010-07-31  3:13 UTC (permalink / raw)
  To: Jonathan Nieder; +Cc: git, Junio C Hamano

2010/7/31 Jonathan Nieder <jrnieder@gmail.com>:
> Nguyễn Thái Ngọc Duy wrote:
>
>> Sparse checkout updates worktree based on the old and new
>> skip-worktree status when $GIT_DIR/info/sparse-checkout changes:
>>
>> old = ce_skip_worktree(ce);           // current skip-worktree
>> new = will_have_skip_work_tree(ce);   // from $GIT..sparse-checkout
>> if (old && !new) add_file_back(ce);   // shrink checkout area
>> if (!old && new) remove_file_out(ce); [1] // enlarge checkout area
>>
>> New entries after merging will always have skip-worktree unset
>> (i.e. old = 0). If those files are filtered out by
>> $GIT_DIR/info/sparse-checkout (i.e. new != 0), then case [1] will
>> happen. But there is nothing to remove because they're new.
>
> When using unpack_trees to add a new file, there is no in-file
> index entry to grab the previous skip worktree bit from.  If it
> is outside the checkout area, we should pretend it was skipped before,
> too; otherwise a checkout can cause a file on disk whose name
> coincides with a newly added outside-worktree file to be deleted.
>
> Do I understand correctly?

Yes.
-- 
Duy

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 1/3] Fix sparse checkout not removing files from index
  2010-07-31  1:04       ` Jonathan Nieder
  2010-07-31  1:05         ` [PATCH 1/2] t1011 (sparse checkout): style nitpicks Jonathan Nieder
  2010-07-31  1:09         ` [PATCH 2/2] read-tree -m -u: always remove relevant files when narrowing checkout Jonathan Nieder
@ 2010-07-31  3:28         ` Nguyen Thai Ngoc Duy
  2010-07-31  3:33           ` Jonathan Nieder
  2 siblings, 1 reply; 17+ messages in thread
From: Nguyen Thai Ngoc Duy @ 2010-07-31  3:28 UTC (permalink / raw)
  To: Jonathan Nieder; +Cc: git, Junio C Hamano

On Sat, Jul 31, 2010 at 11:04 AM, Jonathan Nieder <jrnieder@gmail.com> wrote:
> Jonathan Nieder wrote:
>
>> What happens if an index entry is removed at the same time as the
>> checkout is narrowed?
>
> Hm, maybe something like this would help.

Or maybe we should move the CE_UPDATE/CE_WT_REMOVE removal code into
apply_sparse_checkout(). That function knows when checkout area is
narrowed and CE_WT_REMOVE should not be removed in that case,
regardless CE_REMOVE|CE_WT_REMOVE combination.
-- 
Duy

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 3/3] Mark new entries skip-worktree appropriately
  2010-07-31  3:13     ` Nguyen Thai Ngoc Duy
@ 2010-07-31  3:29       ` Jonathan Nieder
  0 siblings, 0 replies; 17+ messages in thread
From: Jonathan Nieder @ 2010-07-31  3:29 UTC (permalink / raw)
  To: Nguyen Thai Ngoc Duy; +Cc: git, Junio C Hamano

Nguyen Thai Ngoc Duy wrote:
> 2010/7/31 Jonathan Nieder <jrnieder@gmail.com>:

>> Do I understand correctly?
>
> Yes.

Ah, good.

This is fun code to play with.  Thanks for putting it together.

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 1/3] Fix sparse checkout not removing files from index
  2010-07-31  3:28         ` [PATCH 1/3] Fix sparse checkout not removing files from index Nguyen Thai Ngoc Duy
@ 2010-07-31  3:33           ` Jonathan Nieder
  2010-07-31  3:48             ` Nguyen Thai Ngoc Duy
  0 siblings, 1 reply; 17+ messages in thread
From: Jonathan Nieder @ 2010-07-31  3:33 UTC (permalink / raw)
  To: Nguyen Thai Ngoc Duy; +Cc: git, Junio C Hamano

Nguyen Thai Ngoc Duy wrote:

> maybe we should move the CE_UPDATE/CE_WT_REMOVE removal code into
> apply_sparse_checkout().

Not a bad idea regardless.

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 1/3] Fix sparse checkout not removing files from index
  2010-07-31  3:33           ` Jonathan Nieder
@ 2010-07-31  3:48             ` Nguyen Thai Ngoc Duy
  0 siblings, 0 replies; 17+ messages in thread
From: Nguyen Thai Ngoc Duy @ 2010-07-31  3:48 UTC (permalink / raw)
  To: Jonathan Nieder; +Cc: git, Junio C Hamano

On Sat, Jul 31, 2010 at 1:33 PM, Jonathan Nieder <jrnieder@gmail.com> wrote:
> Nguyen Thai Ngoc Duy wrote:
>
>> maybe we should move the CE_UPDATE/CE_WT_REMOVE removal code into
>> apply_sparse_checkout().
>
> Not a bad idea regardless.

Yeah, something like this, perhaps. It's not tested. I'll repost the
patch later, incorporating all your suggestions.

diff --git a/unpack-trees.c b/unpack-trees.c
index f2d148c..48654b3 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -153,6 +153,26 @@ static int apply_sparse_checkout(struct
cache_entry *ce, struct unpack_trees_opt
 		ce->ce_flags &= ~CE_SKIP_WORKTREE;

 	/*
+	 * Merge strategies may set CE_UPDATE|CE_REMOVE outside checkout
+	 * area as a result of ce_skip_worktree() shortcuts in
+	 * verify_absent() and verify_uptodate().
+	 * Make sure they don't modify worktree if they are already
+	 * outside checkout area
+	 */
+	if (was_skip_worktree && ce_skip_worktree(ce)) {
+		ce->ce_flags &= ~CE_UPDATE;
+
+		/*
+		 * By default, when CE_REMOVE is on, CE_WT_REMOVE is also
+		 * on to get that file removed from both index and worktree.
+		 * If that file is already outside worktree area, don't
+		 * bother remove it.
+		 */
+		if (ce->ce_flags & CE_REMOVE)
+			ce->ce_flags &= ~CE_WT_REMOVE;
+	}
+
+	/*
 	 * We only care about files getting into the checkout area
 	 * If merge strategies want to remove some, go ahead, this
 	 * flag will be removed eventually in unpack_trees() if it's
@@ -793,19 +813,7 @@ int unpack_trees(unsigned len, struct tree_desc
*t, struct unpack_trees_options
 				ret = -1;
 				goto done;
 			}
-			/*
-			 * Merge strategies may set CE_UPDATE|CE_REMOVE outside checkout
-			 * area as a result of ce_skip_worktree() shortcuts in
-			 * verify_absent() and verify_uptodate().
-			 * Make sure they don't modify worktree.
-			 */
-			if (ce_skip_worktree(ce)) {
-				ce->ce_flags &= ~CE_UPDATE;
-
-				if (ce->ce_flags & CE_REMOVE)
-					ce->ce_flags &= ~CE_WT_REMOVE;
-			}
-			else
+			if (!ce_skip_worktree(ce))
 				empty_worktree = 0;

 		}
-- 
Duy

^ permalink raw reply related	[flat|nested] 17+ messages in thread

end of thread, other threads:[~2010-07-31  3:48 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-07-26  9:08 [PATCH 1/3] Fix sparse checkout not removing files from index Nguyễn Thái Ngọc Duy
2010-07-26  9:08 ` [PATCH 2/3] unpack-trees.c: Do not check ce_stage in will_have_skip_worktree() Nguyễn Thái Ngọc Duy
2010-07-31  2:11   ` Jonathan Nieder
2010-07-31  3:12     ` Nguyen Thai Ngoc Duy
2010-07-26  9:08 ` [PATCH 3/3] Mark new entries skip-worktree appropriately Nguyễn Thái Ngọc Duy
2010-07-31  2:32   ` Jonathan Nieder
2010-07-31  3:13     ` Nguyen Thai Ngoc Duy
2010-07-31  3:29       ` Jonathan Nieder
2010-07-30  1:35 ` [PATCH 1/3] Fix sparse checkout not removing files from index Jonathan Nieder
2010-07-30  8:24   ` Nguyen Thai Ngoc Duy
2010-07-30 19:50     ` Jonathan Nieder
2010-07-31  1:04       ` Jonathan Nieder
2010-07-31  1:05         ` [PATCH 1/2] t1011 (sparse checkout): style nitpicks Jonathan Nieder
2010-07-31  1:09         ` [PATCH 2/2] read-tree -m -u: always remove relevant files when narrowing checkout Jonathan Nieder
2010-07-31  3:28         ` [PATCH 1/3] Fix sparse checkout not removing files from index Nguyen Thai Ngoc Duy
2010-07-31  3:33           ` Jonathan Nieder
2010-07-31  3:48             ` Nguyen Thai Ngoc Duy

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).