Skip to content

Commit

Permalink
Merge pull request git-for-windows#203 from
Browse files Browse the repository at this point in the history
If a user provides folders A/ and A/B/ for inclusion in a cone-mode
sparse-checkout file, the parsing logic will notice that A/ appears
both as a "parent" type pattern and as a "recursive" type pattern.
This is unexpected and hence will complain via a warning and revert
to the old logic for checking sparse-checkout patterns.

Prevent this from happening accidentally by sanitizing the folders
for this type of inclusion in the 'git sparse-checkout' builtin.
This happens in two ways:

1. Do not include any parent patterns that also appear as recursive
   patterns.

2. Do not include any recursive patterns deeper than other recursive
   patterns.

**Note:** The old behavior surfaces as a performance issue, not a
correctness issue. The test checks the expected data in the
sparse-checkout file to ensure it is behaving as expected.
  • Loading branch information
derrickstolee authored Oct 1, 2019
2 parents 17088e6 + 577e7fa commit 412d869
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 18 deletions.
22 changes: 18 additions & 4 deletions builtin/sparse-checkout.c
Original file line number Diff line number Diff line change
Expand Up @@ -210,10 +210,18 @@ static void write_cone_to_file(FILE *fp, struct pattern_list *pl)
struct pattern_entry *entry;
struct hashmap_iter iter;
struct string_list sl = STRING_LIST_INIT_DUP;
struct strbuf parent_pattern = STRBUF_INIT;

hashmap_iter_init(&pl->parent_hashmap, &iter);
while ((entry = hashmap_iter_next(&iter)))
string_list_insert(&sl, entry->pattern);
while ((entry = hashmap_iter_next(&iter))) {
if (hashmap_get(&pl->recursive_hashmap, entry, NULL))
continue;

if (!hashmap_contains_parent(&pl->recursive_hashmap,
entry->pattern,
&parent_pattern))
string_list_insert(&sl, entry->pattern);
}

string_list_sort(&sl);
string_list_remove_duplicates(&sl, 0);
Expand All @@ -230,8 +238,14 @@ static void write_cone_to_file(FILE *fp, struct pattern_list *pl)
string_list_clear(&sl, 0);

hashmap_iter_init(&pl->recursive_hashmap, &iter);
while ((entry = hashmap_iter_next(&iter)))
string_list_insert(&sl, entry->pattern);
while ((entry = hashmap_iter_next(&iter))) {
if (!hashmap_contains_parent(&pl->recursive_hashmap,
entry->pattern,
&parent_pattern))
string_list_insert(&sl, entry->pattern);
}

strbuf_release(&parent_pattern);

string_list_sort(&sl);
string_list_remove_duplicates(&sl, 0);
Expand Down
40 changes: 27 additions & 13 deletions dir.c
Original file line number Diff line number Diff line change
Expand Up @@ -703,6 +703,29 @@ static int hashmap_contains_path(struct hashmap *map,
return !!hashmap_get(map, &p, NULL);
}

int hashmap_contains_parent(struct hashmap *map,
const char *path,
struct strbuf *buffer)
{
char *slash_pos;

strbuf_setlen(buffer, 0);
strbuf_addstr(buffer, path);

slash_pos = strrchr(buffer->buf, '/');

while (slash_pos > buffer->buf) {
strbuf_setlen(buffer, slash_pos - buffer->buf);

if (hashmap_contains_path(map, buffer))
return 1;

slash_pos = strrchr(buffer->buf, '/');
}

return 0;
}

void add_pattern(const char *string, const char *base,
int baselen, struct pattern_list *pl, int srcpos)
{
Expand Down Expand Up @@ -1286,19 +1309,10 @@ enum pattern_match_result path_matches_pattern_list(
goto done;
}

while (parent_pathname.len) {
if (hashmap_contains_path(&pl->recursive_hashmap,
&parent_pathname)) {
result = MATCHED_RECURSIVE;
goto done;
}

slash_pos = strrchr(parent_pathname.buf, '/');
if (slash_pos == parent_pathname.buf)
break;

strbuf_setlen(&parent_pathname, slash_pos - parent_pathname.buf);
}
if (hashmap_contains_parent(&pl->recursive_hashmap,
pathname,
&parent_pathname))
result = MATCHED_RECURSIVE;

done:
strbuf_release(&parent_pathname);
Expand Down
4 changes: 3 additions & 1 deletion dir.h
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,9 @@ int is_excluded(struct dir_struct *dir,

int pl_hashmap_cmp(const void *unused_cmp_data,
const void *a, const void *b, const void *key);

int hashmap_contains_parent(struct hashmap *map,
const char *path,
struct strbuf *buffer);
struct pattern_list *add_pattern_list(struct dir_struct *dir,
int group_type, const char *src);
int add_patterns_from_file_to_list(const char *fname, const char *base, int baselen,
Expand Down
11 changes: 11 additions & 0 deletions t/t1091-sparse-checkout-builtin.sh
Original file line number Diff line number Diff line change
Expand Up @@ -227,5 +227,16 @@ test_expect_success 'cone mode: init and set' '
test_cmp expect dir
'

test_expect_success 'cone mode: set with nested folders' '
git -C repo sparse-checkout set deep deep/deeper1/deepest 2>err &&
test_line_count = 0 err &&
cat >expect <<-EOF &&
/*
!/*/
/deep/
EOF
test_cmp repo/.git/info/sparse-checkout expect
'

test_done

0 comments on commit 412d869

Please sign in to comment.