This series is based on ds/sparse-list-in-cone-mode. This series attempts to clean up some rough edges in the sparse-checkout feature, especially around the cone mode. Unfortunately, after the v2.25.0 release, we noticed an issue with the "git clone --sparse" option when using a URL instead of a local path. This is fixed and properly tested here. Also, let's improve Git's response to these more complicated scenarios: 1. Running "git sparse-checkout init" in a worktree would complain because the "info" dir doesn't exist. 2. Tracked paths that include "*" and "\" in their filenames. 3. If a user edits the sparse-checkout file to have non-cone pattern, such as "**" anywhere or "*" in the wrong place, then we should respond appropriately. That is: warn that the patterns are not cone-mode, then revert to the old logic. Updates in V2: * Added C-style quoting to the output of "git sparse-checkout list" in cone mode. * Improved documentation. * Responded to most style feedback. Hopefully I didn't miss anything. * I was lingering on this a little to see if I could also fix the issue raised in [1], but I have not figured that one out, yet. Update in V3: * Input now uses Peff's recommended pattern: unquote C-style strings over stdin and otherwise do not un-escape input. [1] https://lore.kernel.org/git/062301d5d0bc$c3e17760$4ba46620$@Frontier.com/ Thanks, -Stolee Derrick Stolee (11): t1091: use check_files to reduce boilerplate t1091: improve here-docs sparse-checkout: create leading directories clone: fix --sparse option with URLs sparse-checkout: cone mode does not recognize "**" sparse-checkout: detect short patterns sparse-checkout: warn on incorrect '*' in patterns sparse-checkout: properly match escaped characters sparse-checkout: write escaped patterns in cone mode sparse-checkout: use C-style quotes in 'list' subcommand sparse-checkout: improve docs around 'set' in cone mode Jeff King (1): sparse-checkout: fix documentation typo for core.sparseCheckoutCone Documentation/git-sparse-checkout.txt | 19 +- builtin/clone.c | 2 +- builtin/sparse-checkout.c | 48 +++- dir.c | 68 +++++- t/t1091-sparse-checkout-builtin.sh | 323 +++++++++++++++----------- 5 files changed, 305 insertions(+), 155 deletions(-) base-commit: 4fd683b6a35eabd23dd5183da7f654a1e1f00325 Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-513%2Fderrickstolee%2Fsparse-harden-v3 Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-513/derrickstolee/sparse-harden-v3 Pull-Request: https://github.com/gitgitgadget/git/pull/513 Range-diff vs v2: 1: 1cc825412f = 1: 1cc825412f t1091: use check_files to reduce boilerplate 2: b7a6ad145a = 2: b7a6ad145a t1091: improve here-docs 3: 5497ad8778 = 3: 5497ad8778 sparse-checkout: create leading directories 4: 4991a51f6d = 4: 4991a51f6d clone: fix --sparse option with URLs 5: ae78c3069b = 5: ae78c3069b sparse-checkout: fix documentation typo for core.sparseCheckoutCone 6: 2ad4d3e467 = 6: 2ad4d3e467 sparse-checkout: cone mode does not recognize "**" 7: aace064510 = 7: aace064510 sparse-checkout: detect short patterns 8: d2a510a3bb = 8: d2a510a3bb sparse-checkout: warn on incorrect '*' in patterns 9: 65c53d7526 ! 9: 9ea69e9069 sparse-checkout: properly match escaped characters @@ -20,7 +20,7 @@ return strncmp(ee1->pattern, ee2->pattern, min_len); } -+char *dup_and_filter_pattern(const char *pattern) ++static char *dup_and_filter_pattern(const char *pattern) +{ + char *set, *read; + char *result = xstrdup(pattern); @@ -69,18 +69,6 @@ hashmap_entry_init(&translated->ent, ignore_case ? - diff --git a/dir.h b/dir.h - --- a/dir.h - +++ b/dir.h -@@ - const struct hashmap_entry *a, - const struct hashmap_entry *b, - const void *key); -+char *dup_and_filter_pattern(const char *pattern); - int hashmap_contains_parent(struct hashmap *map, - const char *path, - struct strbuf *buffer); - diff --git a/t/t1091-sparse-checkout-builtin.sh b/t/t1091-sparse-checkout-builtin.sh --- a/t/t1091-sparse-checkout-builtin.sh +++ b/t/t1091-sparse-checkout-builtin.sh 10: c27a17a2fc ! 10: e2f9afc70c sparse-checkout: write escaped patterns in cone mode @@ -24,11 +24,24 @@ unescaped names in the hashsets for the cone comparisons, then escape the patterns later. + Use unquote_c_style() when parsing lines from stdin. Command-line + arguments will be parsed as-is, assuming the user can do the correct + level of escaping from their environment to match the exact directory + names. + Signed-off-by: Derrick Stolee <dstolee@xxxxxxxxxxxxx> diff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c --- a/builtin/sparse-checkout.c +++ b/builtin/sparse-checkout.c +@@ + #include "resolve-undo.h" + #include "unpack-trees.h" + #include "wt-status.h" ++#include "quote.h" + + static const char *empty_base = ""; + @@ return result; } @@ -77,52 +90,28 @@ } @@ - { - struct pattern_entry *e = xmalloc(sizeof(*e)); - e->patternlen = path->len; -- e->pattern = strbuf_detach(path, NULL); -+ e->pattern = dup_and_filter_pattern(path->buf); -+ strbuf_release(path); -+ - hashmap_entry_init(&e->ent, - ignore_case ? - strihash(e->pattern) : -@@ + pl.use_cone_patterns = 1; - static void strbuf_to_cone_pattern(struct strbuf *line, struct pattern_list *pl) - { -+ int i; - strbuf_trim(line); - - strbuf_trim_trailing_dir_sep(line); -@@ - if (!line->len) - return; - -+ for (i = 0; i < line->len; i++) { -+ if (line->buf[i] == '*') { -+ strbuf_insert(line, i, "\\", 1); -+ i++; -+ } + if (set_opts.use_stdin) { +- while (!strbuf_getline(&line, stdin)) ++ struct strbuf unquoted = STRBUF_INIT; ++ while (!strbuf_getline(&line, stdin)) { ++ if (line.buf[0] == '"') { ++ strbuf_setlen(&unquoted, 0); ++ if (unquote_c_style(&unquoted, line.buf, NULL)) ++ die(_("unable to unquote C-style string '%s'"), ++ line.buf); + -+ if (line->buf[i] == '\\') { -+ if (i < line->len - 1 && line->buf[i + 1] == '\\') -+ i++; -+ else -+ strbuf_insert(line, i, "\\", 1); ++ strbuf_swap(&unquoted, &line); ++ } + -+ i++; -+ } -+ } + strbuf_to_cone_pattern(&line, &pl); ++ } + -+ if (line->buf[0] == '"' && line->buf[line->len - 1] == '"') { -+ strbuf_remove(line, 0, 1); -+ strbuf_remove(line, line->len - 1, 1); -+ } -+ - if (line->buf[0] != '/') - strbuf_insert(line, 0, "/", 1); - ++ strbuf_release(&unquoted); + } else { + for (i = 0; i < argc; i++) { + strbuf_setlen(&line, 0); diff --git a/t/t1091-sparse-checkout-builtin.sh b/t/t1091-sparse-checkout-builtin.sh --- a/t/t1091-sparse-checkout-builtin.sh @@ -142,7 +131,7 @@ check_files escaped "a deep folder1 folder2 zbad\\dir zdoes*exist" && git -C escaped sparse-checkout init --cone && - cat >escaped/.git/info/sparse-checkout <<-\EOF && -+ git -C escaped sparse-checkout set zbad\\dir zdoes\*not\*exist zdoes\*exist && ++ git -C escaped sparse-checkout set zbad\\dir "zdoes*not*exist" "zdoes*exist" && + cat >expect <<-\EOF && /* !/*/ 11: 526d5becbc ! 11: ec714a4cf0 sparse-checkout: use C-style quotes in 'list' subcommand @@ -12,14 +12,6 @@ diff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c --- a/builtin/sparse-checkout.c +++ b/builtin/sparse-checkout.c -@@ - #include "resolve-undo.h" - #include "unpack-trees.h" - #include "wt-status.h" -+#include "quote.h" - - static const char *empty_base = ""; - @@ string_list_sort(&sl); 12: 1b5858adee = 12: 1867746d97 sparse-checkout: improve docs around 'set' in cone mode -- gitgitgadget