Skip to content

Commit 568fda2

Browse files
committed
sparse-checkout: init and add in cone mode
To make the cone pattern set easy to use, update the behavior of 'git sparse-checkout [init|add]'. Add '--cone' flag to 'git sparse-checkout init' to set the config option 'core.sparseCheckout=cone'. When running 'git sparse-checkout add' in cone mode, a user only needs to supply a list of recursive folder matches. Git will automatically add the necessary parent matches for the leading directories. Signed-off-by: Derrick Stolee <[email protected]>
1 parent b99acea commit 568fda2

File tree

2 files changed

+164
-5
lines changed

2 files changed

+164
-5
lines changed

builtin/sparse-checkout.c

Lines changed: 129 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,22 @@
66
#include "repository.h"
77
#include "run-command.h"
88
#include "strbuf.h"
9+
#include "string-list.h"
910

1011
static char const * const builtin_sparse_checkout_usage[] = {
1112
N_("git sparse-checkout [init|add|list|disable]"),
1213
NULL
1314
};
1415

16+
static const char * const builtin_sparse_checkout_init_usage[] = {
17+
N_("git sparse-checkout init [--cone]"),
18+
NULL
19+
};
20+
1521
struct opts_sparse_checkout {
1622
const char *subcommand;
1723
int read_stdin;
24+
int cone;
1825
} opts;
1926

2027
static char *get_sparse_checkout_filename(void)
@@ -41,6 +48,60 @@ static void write_excludes_to_file(FILE *fp, struct exclude_list *el)
4148
}
4249
}
4350

51+
static void write_cone_to_file(FILE *fp, struct exclude_list *el)
52+
{
53+
int i;
54+
struct exclude_entry *entry;
55+
struct hashmap_iter iter;
56+
struct string_list sl = STRING_LIST_INIT_DUP;
57+
58+
hashmap_iter_init(&el->parent_hashmap, &iter);
59+
while ((entry = hashmap_iter_next(&iter))) {
60+
char *pattern = xstrdup(entry->pattern);
61+
char *converted = pattern;
62+
if (pattern[0] == '/')
63+
converted++;
64+
if (pattern[entry->patternlen - 1] == '/')
65+
pattern[entry->patternlen - 1] = 0;
66+
string_list_insert(&sl, converted);
67+
free(pattern);
68+
}
69+
70+
string_list_sort(&sl);
71+
string_list_remove_duplicates(&sl, 0);
72+
73+
for (i = 0; i < sl.nr; i++) {
74+
char *pattern = sl.items[i].string;
75+
76+
if (!strcmp(pattern, ""))
77+
fprintf(fp, "/*\n!/*/*\n");
78+
else
79+
fprintf(fp, "/%s/*\n!/%s/*/*\n", pattern, pattern);
80+
}
81+
82+
string_list_clear(&sl, 0);
83+
84+
hashmap_iter_init(&el->recursive_hashmap, &iter);
85+
while ((entry = hashmap_iter_next(&iter))) {
86+
char *pattern = xstrdup(entry->pattern);
87+
char *converted = pattern;
88+
if (pattern[0] == '/')
89+
converted++;
90+
if (pattern[entry->patternlen - 1] == '/')
91+
pattern[entry->patternlen - 1] = 0;
92+
string_list_insert(&sl, converted);
93+
free(pattern);
94+
}
95+
96+
string_list_sort(&sl);
97+
string_list_remove_duplicates(&sl, 0);
98+
99+
for (i = 0; i < sl.nr; i++) {
100+
char *pattern = sl.items[i].string;
101+
fprintf(fp, "/%s/*\n", pattern);
102+
}
103+
}
104+
44105
static int sparse_checkout_list(int argc, const char **argv)
45106
{
46107
struct exclude_list el;
@@ -141,8 +202,21 @@ static int sparse_checkout_init(int argc, const char **argv)
141202
char *sparse_filename;
142203
FILE *fp;
143204
int res;
205+
enum sparse_checkout_mode mode;
144206

145-
if (sc_set_config(SPARSE_CHECKOUT_FULL))
207+
static struct option builtin_sparse_checkout_init_options[] = {
208+
OPT_BOOL(0, "cone", &opts.cone,
209+
N_("initialize the sparse-checkout in cone mode")),
210+
OPT_END(),
211+
};
212+
213+
argc = parse_options(argc, argv, NULL,
214+
builtin_sparse_checkout_init_options,
215+
builtin_sparse_checkout_init_usage, 0);
216+
217+
mode = opts.cone ? SPARSE_CHECKOUT_CONE : SPARSE_CHECKOUT_FULL;
218+
219+
if (sc_set_config(mode))
146220
return 1;
147221

148222
memset(&el, 0, sizeof(el));
@@ -183,6 +257,34 @@ static int sparse_checkout_init(int argc, const char **argv)
183257
return sc_read_tree();
184258
}
185259

260+
static void insert_recursive_pattern(struct exclude_list *el, struct strbuf *path)
261+
{
262+
struct exclude_entry *e = xmalloc(sizeof(struct exclude_entry));
263+
e->patternlen = path->len;
264+
e->pattern = strbuf_detach(path, NULL);
265+
hashmap_entry_init(e, memhash(e->pattern, e->patternlen));
266+
267+
hashmap_add(&el->recursive_hashmap, e);
268+
269+
while (e->patternlen) {
270+
char *slash = strrchr(e->pattern, '/');
271+
char *oldpattern = e->pattern;
272+
size_t newlen;
273+
274+
if (!slash)
275+
break;
276+
277+
newlen = slash - e->pattern;
278+
e = xmalloc(sizeof(struct exclude_entry));
279+
e->patternlen = newlen;
280+
e->pattern = xstrndup(oldpattern, newlen);
281+
hashmap_entry_init(e, memhash(e->pattern, e->patternlen));
282+
283+
if (!hashmap_get(&el->parent_hashmap, e, NULL))
284+
hashmap_add(&el->parent_hashmap, e);
285+
}
286+
}
287+
186288
static int sparse_checkout_add(int argc, const char **argv)
187289
{
188290
struct exclude_list el;
@@ -196,11 +298,33 @@ static int sparse_checkout_add(int argc, const char **argv)
196298
add_excludes_from_file_to_list(sparse_filename, "", 0, &el, NULL);
197299

198300
fp = fopen(sparse_filename, "w");
199-
write_excludes_to_file(fp, &el);
200301

201-
while (!strbuf_getline(&line, stdin)) {
202-
strbuf_trim(&line);
203-
fprintf(fp, "%s\n", line.buf);
302+
if (core_sparse_checkout == SPARSE_CHECKOUT_FULL) {
303+
write_excludes_to_file(fp, &el);
304+
305+
while (!strbuf_getline(&line, stdin)) {
306+
strbuf_trim(&line);
307+
fprintf(fp, "%s\n", line.buf);
308+
}
309+
} else if (core_sparse_checkout == SPARSE_CHECKOUT_CONE) {
310+
while (!strbuf_getline(&line, stdin)) {
311+
strbuf_trim(&line);
312+
313+
strbuf_trim_trailing_dir_sep(&line);
314+
315+
if (!line.len)
316+
continue;
317+
318+
if (line.buf[0] == '/')
319+
strbuf_remove(&line, 0, 1);
320+
321+
if (!line.len)
322+
continue;
323+
324+
insert_recursive_pattern(&el, &line);
325+
}
326+
327+
write_cone_to_file(fp, &el);
204328
}
205329

206330
fclose(fp);

t/t1091-sparse-checkout-builtin.sh

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,4 +157,39 @@ test_expect_success 'sparse-checkout disable' '
157157
test_cmp expect dir
158158
'
159159

160+
test_expect_success 'cone mode: init and add' '
161+
git -C repo sparse-checkout init --cone &&
162+
git -C repo config --list >config &&
163+
test_i18ngrep "core.sparsecheckout=cone" config &&
164+
ls repo >dir &&
165+
echo a >expect &&
166+
test_cmp expect dir &&
167+
echo deep/deeper1/deepest | git -C repo sparse-checkout add &&
168+
ls repo >dir &&
169+
cat >expect <<-EOF &&
170+
a
171+
deep
172+
EOF
173+
ls repo/deep >dir &&
174+
cat >expect <<-EOF &&
175+
a
176+
deeper1
177+
EOF
178+
ls repo/deep/deeper1 >dir &&
179+
cat >expect <<-EOF &&
180+
a
181+
deepest
182+
EOF
183+
test_cmp expect dir &&
184+
cat >expect <<-EOF &&
185+
/*
186+
!/*/*
187+
/deep/*
188+
!/deep/*/*
189+
/deep/deeper1/*
190+
!/deep/deeper1/*/*
191+
/deep/deeper1/deepest/*
192+
EOF
193+
test_cmp expect repo/.git/info/sparse-checkout
194+
'
160195
test_done

0 commit comments

Comments
 (0)