Skip to content

Commit d585f0e

Browse files
derrickstoleegitster
authored andcommitted
sparse-checkout: write escaped patterns in cone mode
If a user somehow creates a directory with an asterisk (*) or backslash (\), then the "git sparse-checkout set" command will struggle to provide the correct pattern in the sparse-checkout file. When not in cone mode, the provided pattern is written directly into the sparse-checkout file. However, in cone mode we expect a list of paths to directories and then we convert those into patterns. However, there is some care needed for the timing of these escapes. The in-memory pattern list is used to update the working directory before writing the patterns to disk. Thus, we need the command to have the unescaped names in the hashsets for the cone comparisons, then escape the patterns later. Signed-off-by: Derrick Stolee <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 4f52c2c commit d585f0e

File tree

2 files changed

+29
-4
lines changed

2 files changed

+29
-4
lines changed

builtin/sparse-checkout.c

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "resolve-undo.h"
1414
#include "unpack-trees.h"
1515
#include "wt-status.h"
16+
#include "quote.h"
1617

1718
static const char *empty_base = "";
1819

@@ -140,6 +141,22 @@ static int update_working_directory(struct pattern_list *pl)
140141
return result;
141142
}
142143

144+
static char *escaped_pattern(char *pattern)
145+
{
146+
char *p = pattern;
147+
struct strbuf final = STRBUF_INIT;
148+
149+
while (*p) {
150+
if (*p == '*' || *p == '\\')
151+
strbuf_addch(&final, '\\');
152+
153+
strbuf_addch(&final, *p);
154+
p++;
155+
}
156+
157+
return strbuf_detach(&final, NULL);
158+
}
159+
143160
static void write_cone_to_file(FILE *fp, struct pattern_list *pl)
144161
{
145162
int i;
@@ -164,10 +181,11 @@ static void write_cone_to_file(FILE *fp, struct pattern_list *pl)
164181
fprintf(fp, "/*\n!/*/\n");
165182

166183
for (i = 0; i < sl.nr; i++) {
167-
char *pattern = sl.items[i].string;
184+
char *pattern = escaped_pattern(sl.items[i].string);
168185

169186
if (strlen(pattern))
170187
fprintf(fp, "%s/\n!%s/*/\n", pattern, pattern);
188+
free(pattern);
171189
}
172190

173191
string_list_clear(&sl, 0);
@@ -185,8 +203,9 @@ static void write_cone_to_file(FILE *fp, struct pattern_list *pl)
185203
string_list_remove_duplicates(&sl, 0);
186204

187205
for (i = 0; i < sl.nr; i++) {
188-
char *pattern = sl.items[i].string;
206+
char *pattern = escaped_pattern(sl.items[i].string);
189207
fprintf(fp, "%s/\n", pattern);
208+
free(pattern);
190209
}
191210
}
192211

t/t1091-sparse-checkout-builtin.sh

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,9 @@ check_read_tree_errors () {
309309
REPO=$1
310310
FILES=$2
311311
ERRORS=$3
312+
git -C $REPO -c core.sparseCheckoutCone=false read-tree -mu HEAD 2>err &&
313+
test_must_be_empty err &&
314+
check_files $REPO "$FILES" &&
312315
git -C $REPO read-tree -mu HEAD 2>err &&
313316
if test -z "$ERRORS"
314317
then
@@ -391,14 +394,17 @@ test_expect_success BSLASHPSPEC 'pattern-checks: escaped "*"' '
391394
git -C escaped reset --hard $COMMIT &&
392395
check_files escaped "a deep folder1 folder2 zbad\\dir zdoes*exist" &&
393396
git -C escaped sparse-checkout init --cone &&
394-
cat >escaped/.git/info/sparse-checkout <<-\EOF &&
397+
git -C escaped sparse-checkout set zbad\\dir/bogus "zdoes*not*exist" "zdoes*exist" &&
398+
cat >expect <<-\EOF &&
395399
/*
396400
!/*/
397401
/zbad\\dir/
398402
!/zbad\\dir/*/
399-
/zdoes\*not\*exist/
403+
/zbad\\dir/bogus/
400404
/zdoes\*exist/
405+
/zdoes\*not\*exist/
401406
EOF
407+
test_cmp expect escaped/.git/info/sparse-checkout &&
402408
check_read_tree_errors escaped "a zbad\\dir zdoes*exist"
403409
'
404410

0 commit comments

Comments
 (0)