@matheusbsbs / matheus-bagliano / commits / af09ce24a9
Fork 0

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 <e@80x24.org> Helped-by: Johannes Schindelin <Johannes.Schindelin@gmx.de> Signed-off-by: Derrick Stolee <dstolee@microsoft.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Derrick Stolee committed Nov 21, 2019 at 22:04 UTC af09ce24a9c79f6efc12d1d8f1052e1d1dbe5016
4 files changed +206 -20
builtin/sparse-checkout.c
+147 -16
@@ -6,6 +6,7 @@
6 #include "repository.h"
7 #include "run-command.h"
8 #include "strbuf.h"
9 +#include "string-list.h"
10
11 static char const * const builtin_sparse_checkout_usage[] = {
12 N_("git sparse-checkout (init|list|set|disable) <options>"),
@@ -74,9 +75,65 @@ static int update_working_directory(void)
75 return result;
76 }
77
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 +
133 enum sparse_checkout_mode {
134 MODE_NO_PATTERNS = 0,
135 MODE_ALL_PATTERNS = 1,
136 + MODE_CONE_PATTERNS = 2,
137 };
138
139 static int set_config(enum sparse_checkout_mode mode)
@@ -93,9 +150,22 @@ static int set_config(enum sparse_checkout_mode mode)
150 "core.sparseCheckout",
151 mode ? "true" : NULL);
152
153 + git_config_set_in_file_gently(config_path,
154 + "core.sparseCheckoutCone",
155 + mode == MODE_CONE_PATTERNS ? "true" : NULL);
156 +
157 return 0;
158 }
159
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 +
169 static int sparse_checkout_init(int argc, const char **argv)
170 {
171 struct pattern_list pl;
@@ -103,8 +173,21 @@ static int sparse_checkout_init(int argc, const char **argv)
173 FILE *fp;
174 int res;
175 struct object_id oid;
176 + int mode;
177
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))
191 return 1;
192
193 memset(&pl, 0, sizeof(pl));
@@ -136,18 +219,47 @@ reset_dir:
219 return update_working_directory();
220 }
221
139 -static int write_patterns_and_update(struct pattern_list *pl)
222 +static void insert_recursive_pattern(struct pattern_list *pl, struct strbuf *path)
223 {
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));
228
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);
230
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);
263 }
264
265 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)
292 builtin_sparse_checkout_set_usage,
293 PARSE_OPT_KEEP_UNKNOWN);
294
183 - if (set_opts.use_stdin) {
295 + if (core_sparse_checkout_cone) {
296 struct strbuf line = STRBUF_INIT;
297
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 + }
310 }
311 } 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 + }
324 }
325
326 if (!core_apply_sparse_checkout) {
dir.c
+4 -4
@@ -611,10 +611,10 @@ void parse_path_pattern(const char **pattern,
611 *patternlen = len;
612 }
613
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)
618 {
619 const struct pattern_entry *ee1 =
620 container_of(a, struct pattern_entry, ent);
dir.h
+4
@@ -299,6 +299,10 @@ int is_excluded(struct dir_struct *dir,
299 struct index_state *istate,
300 const char *name, int *dtype);
301
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);
306 int hashmap_contains_parent(struct hashmap *map,
307 const char *path,
308 struct strbuf *buffer);
t/t1091-sparse-checkout-builtin.sh
+51
@@ -186,4 +186,55 @@ test_expect_success 'sparse-checkout disable' '
186 test_cmp expect dir
187 '
188
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 +
240 test_done