sparse-checkout: warn on globs in cone patterns
In cone mode, the sparse-checkout commmand will write patterns that allow faster pattern matching. This matching only works if the patterns in the sparse-checkout file are those written by that command. Users can edit the sparse-checkout file and create patterns that cause the cone mode matching to fail. The cone mode patterns may end in "/*" but otherwise an un-escaped asterisk or other glob character is invalid. Add checks to disable cone mode when seeing these values. A later change will properly handle escaped globs. Signed-off-by: Derrick Stolee <dstolee@microsoft.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Derrick Stolee committed
Jan 31, 2020 at 20:16 UTC
9abc60f8015d060d3f3433b105648a4725c97bd1
2 files changed
+75
dir.c
+36
@@ -635,6 +635,7 @@ static void add_pattern_to_hashsets(struct pattern_list *pl, struct path_pattern
635
struct pattern_entry *translated;
636
char *truncated;
637
char *data = NULL;
638
+ const char *prev, *cur, *next;
639
640
if (!pl->use_cone_patterns)
641
return;
@@ -652,12 +653,47 @@ static void add_pattern_to_hashsets(struct pattern_list *pl, struct path_pattern
653
}
654
655
if (given->patternlen <= 2 ||
656
+ *given->pattern == '*' ||
657
strstr(given->pattern, "**")) {
658
/* Not a cone pattern. */
659
warning(_("unrecognized pattern: '%s'"), given->pattern);
660
goto clear_hashmaps;
661
}
662
663
+ prev = given->pattern;
664
+ cur = given->pattern + 1;
665
+ next = given->pattern + 2;
666
+
667
+ while (*cur) {
668
+ /* Watch for glob characters '*', '\', '[', '?' */
669
+ if (!is_glob_special(*cur))
670
+ goto increment;
671
+
672
+ /* But only if *prev != '\\' */
673
+ if (*prev == '\\')
674
+ goto increment;
675
+
676
+ /* But allow the initial '\' */
677
+ if (*cur == '\\' &&
678
+ is_glob_special(*next))
679
+ goto increment;
680
+
681
+ /* But a trailing '/' then '*' is fine */
682
+ if (*prev == '/' &&
683
+ *cur == '*' &&
684
+ *next == 0)
685
+ goto increment;
686
+
687
+ /* Not a cone pattern. */
688
+ warning(_("unrecognized pattern: '%s'"), given->pattern);
689
+ goto clear_hashmaps;
690
+
691
+ increment:
692
+ prev++;
693
+ cur++;
694
+ next++;
695
+ }
696
+
697
if (given->patternlen > 2 &&
698
!strcmp(given->pattern + given->patternlen - 2, "/*")) {
699
if (!(given->flags & PATTERN_FLAG_NEGATIVE)) {
t/t1091-sparse-checkout-builtin.sh
+39
@@ -348,4 +348,43 @@ test_expect_success 'pattern-checks: too short' '
348
check_read_tree_errors repo "a" "disabling cone pattern matching"
349
'
350
351
+test_expect_success 'pattern-checks: trailing "*"' '
352
+ cat >repo/.git/info/sparse-checkout <<-\EOF &&
353
+ /*
354
+ !/*/
355
+ /a*
356
+ EOF
357
+ check_read_tree_errors repo "a" "disabling cone pattern matching"
358
+'
359
+
360
+test_expect_success 'pattern-checks: starting "*"' '
361
+ cat >repo/.git/info/sparse-checkout <<-\EOF &&
362
+ /*
363
+ !/*/
364
+ *eep/
365
+ EOF
366
+ check_read_tree_errors repo "a deep" "disabling cone pattern matching"
367
+'
368
+
369
+test_expect_success 'pattern-checks: contained glob characters' '
370
+ for c in "[a]" "\\" "?" "*"
371
+ do
372
+ cat >repo/.git/info/sparse-checkout <<-EOF &&
373
+ /*
374
+ !/*/
375
+ something$c-else/
376
+ EOF
377
+ check_read_tree_errors repo "a" "disabling cone pattern matching"
378
+ done
379
+'
380
+
381
+test_expect_success 'pattern-checks: escaped "*"' '
382
+ cat >repo/.git/info/sparse-checkout <<-\EOF &&
383
+ /*
384
+ !/*/
385
+ /does\*not\*exist/
386
+ EOF
387
+ check_read_tree_errors repo "a" ""
388
+'
389
+
390
test_done