Skip to content

Commit

Permalink
sparse checkout: do not eagerly decide the fate for whole directory
Browse files Browse the repository at this point in the history
Sparse-setting code follows closely how files are excluded in
read_directory(), every entry (including directories) are fed to
excluded_from_list() to decide if the entry is suitable. Directories
are treated no different than files. If a directory is matched (or
not), the whole directory is considered matched (or not) and the
process moves on.

This generally works as long as there are no patterns to exclude parts
of the directory. In case of sparse checkout code, the following patterns

  t
  !t/t0000-basic.sh

will produce a worktree with full directory "t" even if t0000-basic.sh
is requested to stay out.

By the same reasoning, if a directory is to be excluded, any rules to
re-include certain files within that directory will be ignored.

Fix it by always checking files against patterns. If no pattern can be
used to decide whether an entry is in our out
(ie. excluded_from_list() returns -1), the entry will be
included/excluded the same as their parent directory.

Noticed-by: <skillzero@gmail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
  • Loading branch information
Nguyễn Thái Ngọc Duy authored and Junio C Hamano committed May 10, 2011
1 parent d61ebbe commit 2891109
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 29 deletions.
41 changes: 41 additions & 0 deletions t/t1011-read-tree-sparse-checkout.sh
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,47 @@ test_expect_success 'match directories without trailing slash' '
test -f sub/added
'

test_expect_success 'match directories with negated patterns' '
cat >expected.swt-negation <<\EOF &&
S init.t
S sub/added
H sub/addedtoo
S subsub/added
EOF
cat >.git/info/sparse-checkout <<\EOF &&
sub
!sub/added
EOF
git read-tree -m -u HEAD &&
git ls-files -t >result &&
test_cmp expected.swt-negation result &&
test ! -f init.t &&
test ! -f sub/added &&
test -f sub/addedtoo
'

test_expect_success 'match directories with negated patterns (2)' '
cat >expected.swt-negation2 <<\EOF &&
H init.t
H sub/added
S sub/addedtoo
H subsub/added
EOF
cat >.git/info/sparse-checkout <<\EOF &&
/*
!sub
sub/added
EOF
git read-tree -m -u HEAD &&
git ls-files -t >result &&
test_cmp expected.swt-negation2 result &&
test -f init.t &&
test -f sub/added &&
test ! -f sub/addedtoo
'

test_expect_success 'match directory pattern' '
echo "s?b" >.git/info/sparse-checkout &&
git read-tree -m -u HEAD &&
Expand Down
63 changes: 34 additions & 29 deletions unpack-trees.c
Original file line number Diff line number Diff line change
Expand Up @@ -814,43 +814,45 @@ static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, str
return mask;
}

static int clear_ce_flags_1(struct cache_entry **cache, int nr,
char *prefix, int prefix_len,
int select_mask, int clear_mask,
struct exclude_list *el, int defval);

/* Whole directory matching */
static int clear_ce_flags_dir(struct cache_entry **cache, int nr,
char *prefix, int prefix_len,
char *basename,
int select_mask, int clear_mask,
struct exclude_list *el)
struct exclude_list *el, int defval)
{
struct cache_entry **cache_end = cache + nr;
struct cache_entry **cache_end;
int dtype = DT_DIR;
int ret = excluded_from_list(prefix, prefix_len, basename, &dtype, el);

prefix[prefix_len++] = '/';

/* included, no clearing for any entries under this directory */
if (!ret) {
for (; cache != cache_end; cache++) {
struct cache_entry *ce = *cache;
if (strncmp(ce->name, prefix, prefix_len))
break;
}
return nr - (cache_end - cache);
}
/* If undecided, use matching result of parent dir in defval */
if (ret < 0)
ret = defval;

/* excluded, clear all selected entries under this directory. */
if (ret == 1) {
for (; cache != cache_end; cache++) {
struct cache_entry *ce = *cache;
if (select_mask && !(ce->ce_flags & select_mask))
continue;
if (strncmp(ce->name, prefix, prefix_len))
break;
ce->ce_flags &= ~clear_mask;
}
return nr - (cache_end - cache);
for (cache_end = cache; cache_end != cache + nr; cache_end++) {
struct cache_entry *ce = *cache_end;
if (strncmp(ce->name, prefix, prefix_len))
break;
}

return 0;
/*
* TODO: check el, if there are no patterns that may conflict
* with ret (iow, we know in advance the incl/excl
* decision for the entire directory), clear flag here without
* calling clear_ce_flags_1(). That function will call
* the expensive excluded_from_list() on every entry.
*/
return clear_ce_flags_1(cache, cache_end - cache,
prefix, prefix_len,
select_mask, clear_mask,
el, ret);
}

/*
Expand All @@ -871,7 +873,7 @@ static int clear_ce_flags_dir(struct cache_entry **cache, int nr,
static int clear_ce_flags_1(struct cache_entry **cache, int nr,
char *prefix, int prefix_len,
int select_mask, int clear_mask,
struct exclude_list *el)
struct exclude_list *el, int defval)
{
struct cache_entry **cache_end = cache + nr;

Expand All @@ -882,7 +884,7 @@ static int clear_ce_flags_1(struct cache_entry **cache, int nr,
while(cache != cache_end) {
struct cache_entry *ce = *cache;
const char *name, *slash;
int len, dtype;
int len, dtype, ret;

if (select_mask && !(ce->ce_flags & select_mask)) {
cache++;
Expand Down Expand Up @@ -911,7 +913,7 @@ static int clear_ce_flags_1(struct cache_entry **cache, int nr,
prefix, prefix_len + len,
prefix + prefix_len,
select_mask, clear_mask,
el);
el, defval);

/* clear_c_f_dir eats a whole dir already? */
if (processed) {
Expand All @@ -922,13 +924,16 @@ static int clear_ce_flags_1(struct cache_entry **cache, int nr,
prefix[prefix_len + len++] = '/';
cache += clear_ce_flags_1(cache, cache_end - cache,
prefix, prefix_len + len,
select_mask, clear_mask, el);
select_mask, clear_mask, el, defval);
continue;
}

/* Non-directory */
dtype = ce_to_dtype(ce);
if (excluded_from_list(ce->name, ce_namelen(ce), name, &dtype, el) > 0)
ret = excluded_from_list(ce->name, ce_namelen(ce), name, &dtype, el);
if (ret < 0)
ret = defval;
if (ret > 0)
ce->ce_flags &= ~clear_mask;
cache++;
}
Expand All @@ -943,7 +948,7 @@ static int clear_ce_flags(struct cache_entry **cache, int nr,
return clear_ce_flags_1(cache, nr,
prefix, 0,
select_mask, clear_mask,
el);
el, 0);
}

/*
Expand Down

0 comments on commit 2891109

Please sign in to comment.