Skip to content

Commit 190a65f

Browse files
derrickstoleegitster
authored andcommitted
sparse-checkout: respect core.ignoreCase in cone mode
When a user uses the sparse-checkout feature in cone mode, they add patterns using "git sparse-checkout set <dir1> <dir2> ..." or by using "--stdin" to provide the directories line-by-line over stdin. This behaviour naturally looks a lot like the way a user would type "git add <dir1> <dir2> ..." If core.ignoreCase is enabled, then "git add" will match the input using a case-insensitive match. Do the same for the sparse-checkout feature. Perform case-insensitive checks while updating the skip-worktree bits during unpack_trees(). This is done by changing the hash algorithm and hashmap comparison methods to optionally use case- insensitive methods. When this is enabled, there is a small performance cost in the hashing algorithm. To tease out the worst possible case, the following was run on a repo with a deep directory structure: git ls-tree -d -r --name-only HEAD | git sparse-checkout set --stdin The 'set' command was timed with core.ignoreCase disabled or enabled. For the repo with a deep history, the numbers were core.ignoreCase=false: 62s core.ignoreCase=true: 74s (+19.3%) For reproducibility, the equivalent test on the Linux kernel repository had these numbers: core.ignoreCase=false: 3.1s core.ignoreCase=true: 3.6s (+16%) Now, this is not an entirely fair comparison, as most users will define their sparse cone using more shallow directories, and the performance improvement from eb42fec ("unpack-trees: hash less in cone mode" 2019-11-21) can remove most of the hash cost. For a more realistic test, drop the "-r" from the ls-tree command to store only the first-level directories. In that case, the Linux kernel repository takes 0.2-0.25s in each case, and the deep repository takes one second, plus or minus 0.05s, in each case. Thus, we _can_ demonstrate a cost to this change, but it is unlikely to matter to any reasonable sparse-checkout cone. Signed-off-by: Derrick Stolee <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent cff4e91 commit 190a65f

File tree

4 files changed

+42
-5
lines changed

4 files changed

+42
-5
lines changed

Documentation/git-sparse-checkout.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,11 @@ expecting patterns of these types. Git will warn if the patterns do not match.
150150
If the patterns do match the expected format, then Git will use faster hash-
151151
based algorithms to compute inclusion in the sparse-checkout.
152152

153+
If `core.ignoreCase=true`, then the pattern-matching algorithm will use a
154+
case-insensitive check. This corrects for case mismatched filenames in the
155+
'git sparse-checkout set' command to reflect the expected cone in the working
156+
directory.
157+
153158
SEE ALSO
154159
--------
155160

builtin/sparse-checkout.c

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,10 @@ static void insert_recursive_pattern(struct pattern_list *pl, struct strbuf *pat
313313
struct pattern_entry *e = xmalloc(sizeof(*e));
314314
e->patternlen = path->len;
315315
e->pattern = strbuf_detach(path, NULL);
316-
hashmap_entry_init(&e->ent, memhash(e->pattern, e->patternlen));
316+
hashmap_entry_init(&e->ent,
317+
ignore_case ?
318+
strihash(e->pattern) :
319+
strhash(e->pattern));
317320

318321
hashmap_add(&pl->recursive_hashmap, &e->ent);
319322

@@ -329,7 +332,10 @@ static void insert_recursive_pattern(struct pattern_list *pl, struct strbuf *pat
329332
e = xmalloc(sizeof(struct pattern_entry));
330333
e->patternlen = newlen;
331334
e->pattern = xstrndup(oldpattern, newlen);
332-
hashmap_entry_init(&e->ent, memhash(e->pattern, e->patternlen));
335+
hashmap_entry_init(&e->ent,
336+
ignore_case ?
337+
strihash(e->pattern) :
338+
strhash(e->pattern));
333339

334340
if (!hashmap_get_entry(&pl->parent_hashmap, e, ent, NULL))
335341
hashmap_add(&pl->parent_hashmap, &e->ent);

dir.c

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -625,6 +625,8 @@ int pl_hashmap_cmp(const void *unused_cmp_data,
625625
? ee1->patternlen
626626
: ee2->patternlen;
627627

628+
if (ignore_case)
629+
return strncasecmp(ee1->pattern, ee2->pattern, min_len);
628630
return strncmp(ee1->pattern, ee2->pattern, min_len);
629631
}
630632

@@ -665,7 +667,9 @@ static void add_pattern_to_hashsets(struct pattern_list *pl, struct path_pattern
665667
translated->pattern = truncated;
666668
translated->patternlen = given->patternlen - 2;
667669
hashmap_entry_init(&translated->ent,
668-
memhash(translated->pattern, translated->patternlen));
670+
ignore_case ?
671+
strihash(translated->pattern) :
672+
strhash(translated->pattern));
669673

670674
if (!hashmap_get_entry(&pl->recursive_hashmap,
671675
translated, ent, NULL)) {
@@ -694,7 +698,9 @@ static void add_pattern_to_hashsets(struct pattern_list *pl, struct path_pattern
694698
translated->pattern = xstrdup(given->pattern);
695699
translated->patternlen = given->patternlen;
696700
hashmap_entry_init(&translated->ent,
697-
memhash(translated->pattern, translated->patternlen));
701+
ignore_case ?
702+
strihash(translated->pattern) :
703+
strhash(translated->pattern));
698704

699705
hashmap_add(&pl->recursive_hashmap, &translated->ent);
700706

@@ -724,7 +730,10 @@ static int hashmap_contains_path(struct hashmap *map,
724730
/* Check straight mapping */
725731
p.pattern = pattern->buf;
726732
p.patternlen = pattern->len;
727-
hashmap_entry_init(&p.ent, memhash(p.pattern, p.patternlen));
733+
hashmap_entry_init(&p.ent,
734+
ignore_case ?
735+
strihash(p.pattern) :
736+
strhash(p.pattern));
728737
return !!hashmap_get_entry(map, &p, ent, NULL);
729738
}
730739

t/t1091-sparse-checkout-builtin.sh

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,4 +304,21 @@ test_expect_success 'sparse-checkout (init|set|disable) fails with dirty status'
304304
git -C dirty sparse-checkout disable
305305
'
306306

307+
test_expect_success 'cone mode: set with core.ignoreCase=true' '
308+
git -C repo sparse-checkout init --cone &&
309+
git -C repo -c core.ignoreCase=true sparse-checkout set folder1 &&
310+
cat >expect <<-EOF &&
311+
/*
312+
!/*/
313+
/folder1/
314+
EOF
315+
test_cmp expect repo/.git/info/sparse-checkout &&
316+
ls repo >dir &&
317+
cat >expect <<-EOF &&
318+
a
319+
folder1
320+
EOF
321+
test_cmp expect dir
322+
'
323+
307324
test_done

0 commit comments

Comments
 (0)