@matheusbsbs / matheus-bagliano / commits / d585f0e799
Fork 0

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 <dstolee@microsoft.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Derrick Stolee committed Jan 31, 2020 at 20:16 UTC d585f0e7992ea7f025a5a91f46f2baa9e88f19f6
2 files changed +29 -4
builtin/sparse-checkout.c
+21 -2
@@ -13,6 +13,7 @@
13 #include "resolve-undo.h"
14 #include "unpack-trees.h"
15 #include "wt-status.h"
16 +#include "quote.h"
17
18 static const char *empty_base = "";
19
@@ -140,6 +141,22 @@ static int update_working_directory(struct pattern_list *pl)
141 return result;
142 }
143
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 +
160 static void write_cone_to_file(FILE *fp, struct pattern_list *pl)
161 {
162 int i;
@@ -164,10 +181,11 @@ static void write_cone_to_file(FILE *fp, struct pattern_list *pl)
181 fprintf(fp, "/*\n!/*/\n");
182
183 for (i = 0; i < sl.nr; i++) {
167 - char *pattern = sl.items[i].string;
184 + char *pattern = escaped_pattern(sl.items[i].string);
185
186 if (strlen(pattern))
187 fprintf(fp, "%s/\n!%s/*/\n", pattern, pattern);
188 + free(pattern);
189 }
190
191 string_list_clear(&sl, 0);
@@ -185,8 +203,9 @@ static void write_cone_to_file(FILE *fp, struct pattern_list *pl)
203 string_list_remove_duplicates(&sl, 0);
204
205 for (i = 0; i < sl.nr; i++) {
188 - char *pattern = sl.items[i].string;
206 + char *pattern = escaped_pattern(sl.items[i].string);
207 fprintf(fp, "%s/\n", pattern);
208 + free(pattern);
209 }
210 }
211
t/t1091-sparse-checkout-builtin.sh
+8 -2
@@ -309,6 +309,9 @@ check_read_tree_errors () {
309 REPO=$1
310 FILES=$2
311 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" &&
315 git -C $REPO read-tree -mu HEAD 2>err &&
316 if test -z "$ERRORS"
317 then
@@ -391,14 +394,17 @@ test_expect_success BSLASHPSPEC 'pattern-checks: escaped "*"' '
394 git -C escaped reset --hard $COMMIT &&
395 check_files escaped "a deep folder1 folder2 zbad\\dir zdoes*exist" &&
396 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 &&
399 /*
400 !/*/
401 /zbad\\dir/
402 !/zbad\\dir/*/
399 - /zdoes\*not\*exist/
403 + /zbad\\dir/bogus/
404 /zdoes\*exist/
405 + /zdoes\*not\*exist/
406 EOF
407 + test_cmp expect escaped/.git/info/sparse-checkout &&
408 check_read_tree_errors escaped "a zbad\\dir zdoes*exist"
409 '
410