Skip to content

Commit 663c40a

Browse files
committed
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 <[email protected]>
1 parent 7990916 commit 663c40a

File tree

3 files changed

+132
-6
lines changed

3 files changed

+132
-6
lines changed

Documentation/git-sparse-checkout.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,13 @@ directories. The input format matches the output of `git ls-tree --name-only`.
5959
This includes interpreting pathnames that begin with a double quote (") as
6060
C-style quoted strings.
6161

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+
6269
'disable'::
6370
Disable the `core.sparseCheckout` config setting, and restore the
6471
working directory to include all files. Leaves the sparse-checkout

builtin/sparse-checkout.c

Lines changed: 66 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
static const char *empty_base = "";
1919

2020
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>"),
2222
NULL
2323
};
2424

@@ -404,7 +404,7 @@ static void strbuf_to_cone_pattern(struct strbuf *line, struct pattern_list *pl)
404404
}
405405

406406
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>)"),
408408
NULL
409409
};
410410

@@ -464,16 +464,73 @@ static void add_patterns_from_input(struct pattern_list *pl,
464464

465465
enum modify_type {
466466
REPLACE,
467+
ADD,
467468
};
468469

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+
469515
static int modify_pattern_list(int argc, const char **argv, enum modify_type m)
470516
{
471517
int result;
472518
int changed_config = 0;
473519
struct pattern_list pl;
474520
memset(&pl, 0, sizeof(pl));
475521

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+
}
477534

478535
if (!core_apply_sparse_checkout) {
479536
set_config(MODE_ALL_PATTERNS);
@@ -490,7 +547,8 @@ static int modify_pattern_list(int argc, const char **argv, enum modify_type m)
490547
return result;
491548
}
492549

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)
494552
{
495553
static struct option builtin_sparse_checkout_set_options[] = {
496554
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)
507565
builtin_sparse_checkout_set_usage,
508566
PARSE_OPT_KEEP_UNKNOWN);
509567

510-
return modify_pattern_list(argc, argv, REPLACE);
568+
return modify_pattern_list(argc, argv, m);
511569
}
512570

513571
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)
558616
if (!strcmp(argv[0], "init"))
559617
return sparse_checkout_init(argc, argv);
560618
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);
562622
if (!strcmp(argv[0], "disable"))
563623
return sparse_checkout_disable(argc, argv);
564624
}

t/t1091-sparse-checkout-builtin.sh

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,21 @@ test_expect_success 'set sparse-checkout using --stdin' '
141141
check_files repo "a folder1 folder2"
142142
'
143143

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+
144159
test_expect_success 'cone mode: match patterns' '
145160
git -C repo config --worktree core.sparseCheckoutCone true &&
146161
rm -rf repo/a repo/folder1 repo/folder2 &&
@@ -219,8 +234,52 @@ test_expect_success 'cone mode: set with nested folders' '
219234
test_cmp repo/.git/info/sparse-checkout expect
220235
'
221236

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+
222280
test_expect_success 'revert to old sparse-checkout on bad update' '
223281
test_when_finished git -C repo reset --hard &&
282+
git -C repo sparse-checkout set deep &&
224283
echo update >repo/deep/deeper2/a &&
225284
cp repo/.git/info/sparse-checkout expect &&
226285
test_must_fail git -C repo sparse-checkout set deep/deeper1 2>err &&

0 commit comments

Comments
 (0)