Skip to content

Commit af09ce2

Browse files
derrickstoleegitster
authored andcommitted
sparse-checkout: init and set in cone mode
To make the cone pattern set easy to use, update the behavior of 'git sparse-checkout (init|set)'. Add '--cone' flag to 'git sparse-checkout init' to set the config option 'core.sparseCheckoutCone=true'. When running 'git sparse-checkout set' 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. When testing 'git sparse-checkout set' in cone mode, check the error stream to ensure we do not see any errors. Specifically, we want to avoid the warning that the patterns do not match the cone-mode patterns. Helped-by: Eric Wong <[email protected]> Helped-by: Johannes Schindelin <[email protected]> Signed-off-by: Derrick Stolee <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 96cc8ab commit af09ce2

File tree

4 files changed

+206
-20
lines changed

4 files changed

+206
-20
lines changed

builtin/sparse-checkout.c

Lines changed: 147 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
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|list|set|disable) <options>"),
@@ -74,9 +75,65 @@ static int update_working_directory(void)
7475
return result;
7576
}
7677

78+
static void write_cone_to_file(FILE *fp, struct pattern_list *pl)
79+
{
80+
int i;
81+
struct pattern_entry *pe;
82+
struct hashmap_iter iter;
83+
struct string_list sl = STRING_LIST_INIT_DUP;
84+
85+
hashmap_for_each_entry(&pl->parent_hashmap, &iter, pe, ent)
86+
string_list_insert(&sl, pe->pattern);
87+
88+
string_list_sort(&sl);
89+
string_list_remove_duplicates(&sl, 0);
90+
91+
fprintf(fp, "/*\n!/*/\n");
92+
93+
for (i = 0; i < sl.nr; i++) {
94+
char *pattern = sl.items[i].string;
95+
96+
if (strlen(pattern))
97+
fprintf(fp, "%s/\n!%s/*/\n", pattern, pattern);
98+
}
99+
100+
string_list_clear(&sl, 0);
101+
102+
hashmap_for_each_entry(&pl->recursive_hashmap, &iter, pe, ent)
103+
string_list_insert(&sl, pe->pattern);
104+
105+
string_list_sort(&sl);
106+
string_list_remove_duplicates(&sl, 0);
107+
108+
for (i = 0; i < sl.nr; i++) {
109+
char *pattern = sl.items[i].string;
110+
fprintf(fp, "%s/\n", pattern);
111+
}
112+
}
113+
114+
static int write_patterns_and_update(struct pattern_list *pl)
115+
{
116+
char *sparse_filename;
117+
FILE *fp;
118+
119+
sparse_filename = get_sparse_checkout_filename();
120+
fp = fopen(sparse_filename, "w");
121+
122+
if (core_sparse_checkout_cone)
123+
write_cone_to_file(fp, pl);
124+
else
125+
write_patterns_to_file(fp, pl);
126+
127+
fclose(fp);
128+
free(sparse_filename);
129+
130+
return update_working_directory();
131+
}
132+
77133
enum sparse_checkout_mode {
78134
MODE_NO_PATTERNS = 0,
79135
MODE_ALL_PATTERNS = 1,
136+
MODE_CONE_PATTERNS = 2,
80137
};
81138

82139
static int set_config(enum sparse_checkout_mode mode)
@@ -93,18 +150,44 @@ static int set_config(enum sparse_checkout_mode mode)
93150
"core.sparseCheckout",
94151
mode ? "true" : NULL);
95152

153+
git_config_set_in_file_gently(config_path,
154+
"core.sparseCheckoutCone",
155+
mode == MODE_CONE_PATTERNS ? "true" : NULL);
156+
96157
return 0;
97158
}
98159

160+
static char const * const builtin_sparse_checkout_init_usage[] = {
161+
N_("git sparse-checkout init [--cone]"),
162+
NULL
163+
};
164+
165+
static struct sparse_checkout_init_opts {
166+
int cone_mode;
167+
} init_opts;
168+
99169
static int sparse_checkout_init(int argc, const char **argv)
100170
{
101171
struct pattern_list pl;
102172
char *sparse_filename;
103173
FILE *fp;
104174
int res;
105175
struct object_id oid;
176+
int mode;
106177

107-
if (set_config(MODE_ALL_PATTERNS))
178+
static struct option builtin_sparse_checkout_init_options[] = {
179+
OPT_BOOL(0, "cone", &init_opts.cone_mode,
180+
N_("initialize the sparse-checkout in cone mode")),
181+
OPT_END(),
182+
};
183+
184+
argc = parse_options(argc, argv, NULL,
185+
builtin_sparse_checkout_init_options,
186+
builtin_sparse_checkout_init_usage, 0);
187+
188+
mode = init_opts.cone_mode ? MODE_CONE_PATTERNS : MODE_ALL_PATTERNS;
189+
190+
if (set_config(mode))
108191
return 1;
109192

110193
memset(&pl, 0, sizeof(pl));
@@ -136,18 +219,47 @@ static int sparse_checkout_init(int argc, const char **argv)
136219
return update_working_directory();
137220
}
138221

139-
static int write_patterns_and_update(struct pattern_list *pl)
222+
static void insert_recursive_pattern(struct pattern_list *pl, struct strbuf *path)
140223
{
141-
char *sparse_filename;
142-
FILE *fp;
224+
struct pattern_entry *e = xmalloc(sizeof(*e));
225+
e->patternlen = path->len;
226+
e->pattern = strbuf_detach(path, NULL);
227+
hashmap_entry_init(&e->ent, memhash(e->pattern, e->patternlen));
143228

144-
sparse_filename = get_sparse_checkout_filename();
145-
fp = fopen(sparse_filename, "w");
146-
write_patterns_to_file(fp, pl);
147-
fclose(fp);
148-
free(sparse_filename);
229+
hashmap_add(&pl->recursive_hashmap, &e->ent);
149230

150-
return update_working_directory();
231+
while (e->patternlen) {
232+
char *slash = strrchr(e->pattern, '/');
233+
char *oldpattern = e->pattern;
234+
size_t newlen;
235+
236+
if (slash == e->pattern)
237+
break;
238+
239+
newlen = slash - e->pattern;
240+
e = xmalloc(sizeof(struct pattern_entry));
241+
e->patternlen = newlen;
242+
e->pattern = xstrndup(oldpattern, newlen);
243+
hashmap_entry_init(&e->ent, memhash(e->pattern, e->patternlen));
244+
245+
if (!hashmap_get_entry(&pl->parent_hashmap, e, ent, NULL))
246+
hashmap_add(&pl->parent_hashmap, &e->ent);
247+
}
248+
}
249+
250+
static void strbuf_to_cone_pattern(struct strbuf *line, struct pattern_list *pl)
251+
{
252+
strbuf_trim(line);
253+
254+
strbuf_trim_trailing_dir_sep(line);
255+
256+
if (!line->len)
257+
return;
258+
259+
if (line->buf[0] != '/')
260+
strbuf_insert(line, 0, "/", 1);
261+
262+
insert_recursive_pattern(pl, line);
151263
}
152264

153265
static char const * const builtin_sparse_checkout_set_usage[] = {
@@ -180,16 +292,35 @@ static int sparse_checkout_set(int argc, const char **argv, const char *prefix)
180292
builtin_sparse_checkout_set_usage,
181293
PARSE_OPT_KEEP_UNKNOWN);
182294

183-
if (set_opts.use_stdin) {
295+
if (core_sparse_checkout_cone) {
184296
struct strbuf line = STRBUF_INIT;
185297

186-
while (!strbuf_getline(&line, stdin)) {
187-
char *buf = strbuf_detach(&line, NULL);
188-
add_pattern(buf, empty_base, 0, &pl, 0);
298+
hashmap_init(&pl.recursive_hashmap, pl_hashmap_cmp, NULL, 0);
299+
hashmap_init(&pl.parent_hashmap, pl_hashmap_cmp, NULL, 0);
300+
301+
if (set_opts.use_stdin) {
302+
while (!strbuf_getline(&line, stdin))
303+
strbuf_to_cone_pattern(&line, &pl);
304+
} else {
305+
for (i = 0; i < argc; i++) {
306+
strbuf_setlen(&line, 0);
307+
strbuf_addstr(&line, argv[i]);
308+
strbuf_to_cone_pattern(&line, &pl);
309+
}
189310
}
190311
} else {
191-
for (i = 0; i < argc; i++)
192-
add_pattern(argv[i], empty_base, 0, &pl, 0);
312+
if (set_opts.use_stdin) {
313+
struct strbuf line = STRBUF_INIT;
314+
315+
while (!strbuf_getline(&line, stdin)) {
316+
size_t len;
317+
char *buf = strbuf_detach(&line, &len);
318+
add_pattern(buf, empty_base, 0, &pl, 0);
319+
}
320+
} else {
321+
for (i = 0; i < argc; i++)
322+
add_pattern(argv[i], empty_base, 0, &pl, 0);
323+
}
193324
}
194325

195326
if (!core_apply_sparse_checkout) {

dir.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -611,10 +611,10 @@ void parse_path_pattern(const char **pattern,
611611
*patternlen = len;
612612
}
613613

614-
static int pl_hashmap_cmp(const void *unused_cmp_data,
615-
const struct hashmap_entry *a,
616-
const struct hashmap_entry *b,
617-
const void *key)
614+
int pl_hashmap_cmp(const void *unused_cmp_data,
615+
const struct hashmap_entry *a,
616+
const struct hashmap_entry *b,
617+
const void *key)
618618
{
619619
const struct pattern_entry *ee1 =
620620
container_of(a, struct pattern_entry, ent);

dir.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,10 @@ int is_excluded(struct dir_struct *dir,
299299
struct index_state *istate,
300300
const char *name, int *dtype);
301301

302+
int pl_hashmap_cmp(const void *unused_cmp_data,
303+
const struct hashmap_entry *a,
304+
const struct hashmap_entry *b,
305+
const void *key);
302306
int hashmap_contains_parent(struct hashmap *map,
303307
const char *path,
304308
struct strbuf *buffer);

t/t1091-sparse-checkout-builtin.sh

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,4 +186,55 @@ test_expect_success 'sparse-checkout disable' '
186186
test_cmp expect dir
187187
'
188188

189+
test_expect_success 'cone mode: init and set' '
190+
git -C repo sparse-checkout init --cone &&
191+
git -C repo config --list >config &&
192+
test_i18ngrep "core.sparsecheckoutcone=true" config &&
193+
ls repo >dir &&
194+
echo a >expect &&
195+
test_cmp expect dir &&
196+
git -C repo sparse-checkout set deep/deeper1/deepest/ 2>err &&
197+
test_must_be_empty err &&
198+
ls repo >dir &&
199+
cat >expect <<-EOF &&
200+
a
201+
deep
202+
EOF
203+
test_cmp expect dir &&
204+
ls repo/deep >dir &&
205+
cat >expect <<-EOF &&
206+
a
207+
deeper1
208+
EOF
209+
test_cmp expect dir &&
210+
ls repo/deep/deeper1 >dir &&
211+
cat >expect <<-EOF &&
212+
a
213+
deepest
214+
EOF
215+
test_cmp expect dir &&
216+
cat >expect <<-EOF &&
217+
/*
218+
!/*/
219+
/deep/
220+
!/deep/*/
221+
/deep/deeper1/
222+
!/deep/deeper1/*/
223+
/deep/deeper1/deepest/
224+
EOF
225+
test_cmp expect repo/.git/info/sparse-checkout &&
226+
git -C repo sparse-checkout set --stdin 2>err <<-EOF &&
227+
folder1
228+
folder2
229+
EOF
230+
test_must_be_empty err &&
231+
cat >expect <<-EOF &&
232+
a
233+
folder1
234+
folder2
235+
EOF
236+
ls repo >dir &&
237+
test_cmp expect dir
238+
'
239+
189240
test_done

0 commit comments

Comments
 (0)