@matheusbsbs / matheus-bagliano / commits / 2631dc879d
Fork 0

sparse-checkout: create 'add' subcommand

When using the sparse-checkout feature, a user may want to incrementally grow their sparse-checkout pattern set. Allow adding patterns using a new 'add' subcommand. This is not much different from the 'set' subcommand, because we still want to allow the '--stdin' option and interpret inputs as directories when in cone mode and patterns otherwise. When in cone mode, we are growing the cone. This may actually reduce the set of patterns when adding directory A when A/B is already a directory in the cone. Test the different cases: siblings, parents, ancestors. When not in cone mode, we can only assume the patterns should be appended to the sparse-checkout file. Signed-off-by: Derrick Stolee <dstolee@microsoft.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Derrick Stolee committed Feb 11, 2020 at 15:02 UTC 2631dc879d59aa08095bc4fb5bc9bcc491a787e9
3 files changed +132 -6
Documentation/git-sparse-checkout.txt
+7
@@ -59,6 +59,13 @@ directories. The input format matches the output of `git ls-tree --name-only`.
59 This includes interpreting pathnames that begin with a double quote (") as
60 C-style quoted strings.
61
62 +'add'::
63 + Update the sparse-checkout file to include additional patterns.
64 + By default, these patterns are read from the command-line arguments,
65 + but they can be read from stdin using the `--stdin` option. When
66 + `core.sparseCheckoutCone` is enabled, the given patterns are interpreted
67 + as directory names as in the 'set' subcommand.
68 +
69 'disable'::
70 Disable the `core.sparseCheckout` config setting, and restore the
71 working directory to include all files. Leaves the sparse-checkout
builtin/sparse-checkout.c
+66 -6
@@ -18,7 +18,7 @@
18 static const char *empty_base = "";
19
20 static char const * const builtin_sparse_checkout_usage[] = {
21 - N_("git sparse-checkout (init|list|set|disable) <options>"),
21 + N_("git sparse-checkout (init|list|set|add|disable) <options>"),
22 NULL
23 };
24
@@ -404,7 +404,7 @@ static void strbuf_to_cone_pattern(struct strbuf *line, struct pattern_list *pl)
404 }
405
406 static char const * const builtin_sparse_checkout_set_usage[] = {
407 - N_("git sparse-checkout set (--stdin | <patterns>)"),
407 + N_("git sparse-checkout (set|add) (--stdin | <patterns>)"),
408 NULL
409 };
410
@@ -464,8 +464,54 @@ static void add_patterns_from_input(struct pattern_list *pl,
464
465 enum modify_type {
466 REPLACE,
467 + ADD,
468 };
469
470 +static void add_patterns_cone_mode(int argc, const char **argv,
471 + struct pattern_list *pl)
472 +{
473 + struct strbuf buffer = STRBUF_INIT;
474 + struct pattern_entry *pe;
475 + struct hashmap_iter iter;
476 + struct pattern_list existing;
477 + char *sparse_filename = get_sparse_checkout_filename();
478 +
479 + add_patterns_from_input(pl, argc, argv);
480 +
481 + memset(&existing, 0, sizeof(existing));
482 + existing.use_cone_patterns = core_sparse_checkout_cone;
483 +
484 + if (add_patterns_from_file_to_list(sparse_filename, "", 0,
485 + &existing, NULL))
486 + die(_("unable to load existing sparse-checkout patterns"));
487 + free(sparse_filename);
488 +
489 + hashmap_for_each_entry(&existing.recursive_hashmap, &iter, pe, ent) {
490 + if (!hashmap_contains_parent(&pl->recursive_hashmap,
491 + pe->pattern, &buffer) ||
492 + !hashmap_contains_parent(&pl->parent_hashmap,
493 + pe->pattern, &buffer)) {
494 + strbuf_reset(&buffer);
495 + strbuf_addstr(&buffer, pe->pattern);
496 + insert_recursive_pattern(pl, &buffer);
497 + }
498 + }
499 +
500 + clear_pattern_list(&existing);
501 + strbuf_release(&buffer);
502 +}
503 +
504 +static void add_patterns_literal(int argc, const char **argv,
505 + struct pattern_list *pl)
506 +{
507 + char *sparse_filename = get_sparse_checkout_filename();
508 + if (add_patterns_from_file_to_list(sparse_filename, "", 0,
509 + pl, NULL))
510 + die(_("unable to load existing sparse-checkout patterns"));
511 + free(sparse_filename);
512 + add_patterns_from_input(pl, argc, argv);
513 +}
514 +
515 static int modify_pattern_list(int argc, const char **argv, enum modify_type m)
516 {
517 int result;
@@ -473,7 +519,18 @@ static int modify_pattern_list(int argc, const char **argv, enum modify_type m)
519 struct pattern_list pl;
520 memset(&pl, 0, sizeof(pl));
521
476 - add_patterns_from_input(&pl, argc, argv);
522 + switch (m) {
523 + case ADD:
524 + if (core_sparse_checkout_cone)
525 + add_patterns_cone_mode(argc, argv, &pl);
526 + else
527 + add_patterns_literal(argc, argv, &pl);
528 + break;
529 +
530 + case REPLACE:
531 + add_patterns_from_input(&pl, argc, argv);
532 + break;
533 + }
534
535 if (!core_apply_sparse_checkout) {
536 set_config(MODE_ALL_PATTERNS);
@@ -490,7 +547,8 @@ static int modify_pattern_list(int argc, const char **argv, enum modify_type m)
547 return result;
548 }
549
493 -static int sparse_checkout_set(int argc, const char **argv, const char *prefix)
550 +static int sparse_checkout_set(int argc, const char **argv, const char *prefix,
551 + enum modify_type m)
552 {
553 static struct option builtin_sparse_checkout_set_options[] = {
554 OPT_BOOL(0, "stdin", &set_opts.use_stdin,
@@ -507,7 +565,7 @@ static int sparse_checkout_set(int argc, const char **argv, const char *prefix)
565 builtin_sparse_checkout_set_usage,
566 PARSE_OPT_KEEP_UNKNOWN);
567
510 - return modify_pattern_list(argc, argv, REPLACE);
568 + return modify_pattern_list(argc, argv, m);
569 }
570
571 static int sparse_checkout_disable(int argc, const char **argv)
@@ -558,7 +616,9 @@ int cmd_sparse_checkout(int argc, const char **argv, const char *prefix)
616 if (!strcmp(argv[0], "init"))
617 return sparse_checkout_init(argc, argv);
618 if (!strcmp(argv[0], "set"))
561 - return sparse_checkout_set(argc, argv, prefix);
619 + return sparse_checkout_set(argc, argv, prefix, REPLACE);
620 + if (!strcmp(argv[0], "add"))
621 + return sparse_checkout_set(argc, argv, prefix, ADD);
622 if (!strcmp(argv[0], "disable"))
623 return sparse_checkout_disable(argc, argv);
624 }
t/t1091-sparse-checkout-builtin.sh
+59
@@ -141,6 +141,21 @@ test_expect_success 'set sparse-checkout using --stdin' '
141 check_files repo "a folder1 folder2"
142 '
143
144 +test_expect_success 'add to sparse-checkout' '
145 + cat repo/.git/info/sparse-checkout >expect &&
146 + cat >add <<-\EOF &&
147 + pattern1
148 + /folder1/
149 + pattern2
150 + EOF
151 + cat add >>expect &&
152 + git -C repo sparse-checkout add --stdin <add &&
153 + git -C repo sparse-checkout list >actual &&
154 + test_cmp expect actual &&
155 + test_cmp expect repo/.git/info/sparse-checkout &&
156 + check_files repo "a folder1 folder2"
157 +'
158 +
159 test_expect_success 'cone mode: match patterns' '
160 git -C repo config --worktree core.sparseCheckoutCone true &&
161 rm -rf repo/a repo/folder1 repo/folder2 &&
@@ -219,8 +234,52 @@ test_expect_success 'cone mode: set with nested folders' '
234 test_cmp repo/.git/info/sparse-checkout expect
235 '
236
237 +test_expect_success 'cone mode: add independent path' '
238 + git -C repo sparse-checkout set deep/deeper1 &&
239 + git -C repo sparse-checkout add folder1 &&
240 + cat >expect <<-\EOF &&
241 + /*
242 + !/*/
243 + /deep/
244 + !/deep/*/
245 + /deep/deeper1/
246 + /folder1/
247 + EOF
248 + test_cmp expect repo/.git/info/sparse-checkout &&
249 + check_files repo a deep folder1
250 +'
251 +
252 +test_expect_success 'cone mode: add sibling path' '
253 + git -C repo sparse-checkout set deep/deeper1 &&
254 + git -C repo sparse-checkout add deep/deeper2 &&
255 + cat >expect <<-\EOF &&
256 + /*
257 + !/*/
258 + /deep/
259 + !/deep/*/
260 + /deep/deeper1/
261 + /deep/deeper2/
262 + EOF
263 + test_cmp expect repo/.git/info/sparse-checkout &&
264 + check_files repo a deep
265 +'
266 +
267 +test_expect_success 'cone mode: add parent path' '
268 + git -C repo sparse-checkout set deep/deeper1 folder1 &&
269 + git -C repo sparse-checkout add deep &&
270 + cat >expect <<-\EOF &&
271 + /*
272 + !/*/
273 + /deep/
274 + /folder1/
275 + EOF
276 + test_cmp expect repo/.git/info/sparse-checkout &&
277 + check_files repo a deep folder1
278 +'
279 +
280 test_expect_success 'revert to old sparse-checkout on bad update' '
281 test_when_finished git -C repo reset --hard &&
282 + git -C repo sparse-checkout set deep &&
283 echo update >repo/deep/deeper2/a &&
284 cp repo/.git/info/sparse-checkout expect &&
285 test_must_fail git -C repo sparse-checkout set deep/deeper1 2>err &&