sparse-checkout: use in-process update for disable subcommand
[git.git] / builtin / sparse-checkout.c
bloba11ea65599f2c0e5fcfec358cb1b63585398aa4b
1 #include "builtin.h"
2 #include "config.h"
3 #include "dir.h"
4 #include "parse-options.h"
5 #include "pathspec.h"
6 #include "repository.h"
7 #include "run-command.h"
8 #include "strbuf.h"
9 #include "string-list.h"
10 #include "cache.h"
11 #include "cache-tree.h"
12 #include "lockfile.h"
13 #include "resolve-undo.h"
14 #include "unpack-trees.h"
16 static char const * const builtin_sparse_checkout_usage[] = {
17 N_("git sparse-checkout (init|list|set|disable) <options>"),
18 NULL
21 static char *get_sparse_checkout_filename(void)
23 return git_pathdup("info/sparse-checkout");
26 static void write_patterns_to_file(FILE *fp, struct pattern_list *pl)
28 int i;
30 for (i = 0; i < pl->nr; i++) {
31 struct path_pattern *p = pl->patterns[i];
33 if (p->flags & PATTERN_FLAG_NEGATIVE)
34 fprintf(fp, "!");
36 fprintf(fp, "%s", p->pattern);
38 if (p->flags & PATTERN_FLAG_MUSTBEDIR)
39 fprintf(fp, "/");
41 fprintf(fp, "\n");
45 static int sparse_checkout_list(int argc, const char **argv)
47 struct pattern_list pl;
48 char *sparse_filename;
49 int res;
51 memset(&pl, 0, sizeof(pl));
53 sparse_filename = get_sparse_checkout_filename();
54 res = add_patterns_from_file_to_list(sparse_filename, "", 0, &pl, NULL);
55 free(sparse_filename);
57 if (res < 0) {
58 warning(_("this worktree is not sparse (sparse-checkout file may not exist)"));
59 return 0;
62 write_patterns_to_file(stdout, &pl);
63 clear_pattern_list(&pl);
65 return 0;
68 static int update_working_directory(struct pattern_list *pl)
70 int result = 0;
71 struct unpack_trees_options o;
72 struct lock_file lock_file = LOCK_INIT;
73 struct object_id oid;
74 struct tree *tree;
75 struct tree_desc t;
76 struct repository *r = the_repository;
78 if (repo_read_index_unmerged(r))
79 die(_("you need to resolve your current index first"));
81 if (get_oid("HEAD", &oid))
82 return 0;
84 tree = parse_tree_indirect(&oid);
85 parse_tree(tree);
86 init_tree_desc(&t, tree->buffer, tree->size);
88 memset(&o, 0, sizeof(o));
89 o.verbose_update = isatty(2);
90 o.merge = 1;
91 o.update = 1;
92 o.fn = oneway_merge;
93 o.head_idx = -1;
94 o.src_index = r->index;
95 o.dst_index = r->index;
96 o.skip_sparse_checkout = 0;
97 o.pl = pl;
98 o.keep_pattern_list = !!pl;
100 resolve_undo_clear_index(r->index);
101 setup_work_tree();
103 cache_tree_free(&r->index->cache_tree);
105 repo_hold_locked_index(r, &lock_file, LOCK_DIE_ON_ERROR);
107 core_apply_sparse_checkout = 1;
108 result = unpack_trees(1, &t, &o);
110 if (!result) {
111 prime_cache_tree(r, r->index, tree);
112 write_locked_index(r->index, &lock_file, COMMIT_LOCK);
113 } else
114 rollback_lock_file(&lock_file);
116 return result;
119 static void write_cone_to_file(FILE *fp, struct pattern_list *pl)
121 int i;
122 struct pattern_entry *pe;
123 struct hashmap_iter iter;
124 struct string_list sl = STRING_LIST_INIT_DUP;
125 struct strbuf parent_pattern = STRBUF_INIT;
127 hashmap_for_each_entry(&pl->parent_hashmap, &iter, pe, ent) {
128 if (hashmap_get_entry(&pl->recursive_hashmap, pe, ent, NULL))
129 continue;
131 if (!hashmap_contains_parent(&pl->recursive_hashmap,
132 pe->pattern,
133 &parent_pattern))
134 string_list_insert(&sl, pe->pattern);
137 string_list_sort(&sl);
138 string_list_remove_duplicates(&sl, 0);
140 fprintf(fp, "/*\n!/*/\n");
142 for (i = 0; i < sl.nr; i++) {
143 char *pattern = sl.items[i].string;
145 if (strlen(pattern))
146 fprintf(fp, "%s/\n!%s/*/\n", pattern, pattern);
149 string_list_clear(&sl, 0);
151 hashmap_for_each_entry(&pl->recursive_hashmap, &iter, pe, ent) {
152 if (!hashmap_contains_parent(&pl->recursive_hashmap,
153 pe->pattern,
154 &parent_pattern))
155 string_list_insert(&sl, pe->pattern);
158 strbuf_release(&parent_pattern);
160 string_list_sort(&sl);
161 string_list_remove_duplicates(&sl, 0);
163 for (i = 0; i < sl.nr; i++) {
164 char *pattern = sl.items[i].string;
165 fprintf(fp, "%s/\n", pattern);
169 static int write_patterns_and_update(struct pattern_list *pl)
171 char *sparse_filename;
172 FILE *fp;
173 int result;
175 result = update_working_directory(pl);
177 if (result) {
178 clear_pattern_list(pl);
179 update_working_directory(NULL);
180 return result;
183 sparse_filename = get_sparse_checkout_filename();
184 fp = fopen(sparse_filename, "w");
186 if (core_sparse_checkout_cone)
187 write_cone_to_file(fp, pl);
188 else
189 write_patterns_to_file(fp, pl);
191 fclose(fp);
193 free(sparse_filename);
194 clear_pattern_list(pl);
196 return 0;
199 enum sparse_checkout_mode {
200 MODE_NO_PATTERNS = 0,
201 MODE_ALL_PATTERNS = 1,
202 MODE_CONE_PATTERNS = 2,
205 static int set_config(enum sparse_checkout_mode mode)
207 const char *config_path;
209 if (git_config_set_gently("extensions.worktreeConfig", "true")) {
210 error(_("failed to set extensions.worktreeConfig setting"));
211 return 1;
214 config_path = git_path("config.worktree");
215 git_config_set_in_file_gently(config_path,
216 "core.sparseCheckout",
217 mode ? "true" : NULL);
219 git_config_set_in_file_gently(config_path,
220 "core.sparseCheckoutCone",
221 mode == MODE_CONE_PATTERNS ? "true" : NULL);
223 return 0;
226 static char const * const builtin_sparse_checkout_init_usage[] = {
227 N_("git sparse-checkout init [--cone]"),
228 NULL
231 static struct sparse_checkout_init_opts {
232 int cone_mode;
233 } init_opts;
235 static int sparse_checkout_init(int argc, const char **argv)
237 struct pattern_list pl;
238 char *sparse_filename;
239 FILE *fp;
240 int res;
241 struct object_id oid;
242 int mode;
244 static struct option builtin_sparse_checkout_init_options[] = {
245 OPT_BOOL(0, "cone", &init_opts.cone_mode,
246 N_("initialize the sparse-checkout in cone mode")),
247 OPT_END(),
250 argc = parse_options(argc, argv, NULL,
251 builtin_sparse_checkout_init_options,
252 builtin_sparse_checkout_init_usage, 0);
254 if (init_opts.cone_mode) {
255 mode = MODE_CONE_PATTERNS;
256 core_sparse_checkout_cone = 1;
257 } else
258 mode = MODE_ALL_PATTERNS;
260 if (set_config(mode))
261 return 1;
263 memset(&pl, 0, sizeof(pl));
265 sparse_filename = get_sparse_checkout_filename();
266 res = add_patterns_from_file_to_list(sparse_filename, "", 0, &pl, NULL);
268 /* If we already have a sparse-checkout file, use it. */
269 if (res >= 0) {
270 free(sparse_filename);
271 goto reset_dir;
274 /* initial mode: all blobs at root */
275 fp = xfopen(sparse_filename, "w");
276 if (!fp)
277 die(_("failed to open '%s'"), sparse_filename);
279 free(sparse_filename);
280 fprintf(fp, "/*\n!/*/\n");
281 fclose(fp);
283 if (get_oid("HEAD", &oid)) {
284 /* assume we are in a fresh repo */
285 return 0;
288 reset_dir:
289 core_apply_sparse_checkout = 1;
290 return update_working_directory(NULL);
293 static void insert_recursive_pattern(struct pattern_list *pl, struct strbuf *path)
295 struct pattern_entry *e = xmalloc(sizeof(*e));
296 e->patternlen = path->len;
297 e->pattern = strbuf_detach(path, NULL);
298 hashmap_entry_init(&e->ent, memhash(e->pattern, e->patternlen));
300 hashmap_add(&pl->recursive_hashmap, &e->ent);
302 while (e->patternlen) {
303 char *slash = strrchr(e->pattern, '/');
304 char *oldpattern = e->pattern;
305 size_t newlen;
307 if (slash == e->pattern)
308 break;
310 newlen = slash - e->pattern;
311 e = xmalloc(sizeof(struct pattern_entry));
312 e->patternlen = newlen;
313 e->pattern = xstrndup(oldpattern, newlen);
314 hashmap_entry_init(&e->ent, memhash(e->pattern, e->patternlen));
316 if (!hashmap_get_entry(&pl->parent_hashmap, e, ent, NULL))
317 hashmap_add(&pl->parent_hashmap, &e->ent);
321 static void strbuf_to_cone_pattern(struct strbuf *line, struct pattern_list *pl)
323 strbuf_trim(line);
325 strbuf_trim_trailing_dir_sep(line);
327 if (!line->len)
328 return;
330 if (line->buf[0] != '/')
331 strbuf_insert(line, 0, "/", 1);
333 insert_recursive_pattern(pl, line);
336 static char const * const builtin_sparse_checkout_set_usage[] = {
337 N_("git sparse-checkout set (--stdin | <patterns>)"),
338 NULL
341 static struct sparse_checkout_set_opts {
342 int use_stdin;
343 } set_opts;
345 static int sparse_checkout_set(int argc, const char **argv, const char *prefix)
347 static const char *empty_base = "";
348 int i;
349 struct pattern_list pl;
350 int result;
351 int changed_config = 0;
353 static struct option builtin_sparse_checkout_set_options[] = {
354 OPT_BOOL(0, "stdin", &set_opts.use_stdin,
355 N_("read patterns from standard in")),
356 OPT_END(),
359 memset(&pl, 0, sizeof(pl));
361 argc = parse_options(argc, argv, prefix,
362 builtin_sparse_checkout_set_options,
363 builtin_sparse_checkout_set_usage,
364 PARSE_OPT_KEEP_UNKNOWN);
366 if (core_sparse_checkout_cone) {
367 struct strbuf line = STRBUF_INIT;
369 hashmap_init(&pl.recursive_hashmap, pl_hashmap_cmp, NULL, 0);
370 hashmap_init(&pl.parent_hashmap, pl_hashmap_cmp, NULL, 0);
371 pl.use_cone_patterns = 1;
373 if (set_opts.use_stdin) {
374 while (!strbuf_getline(&line, stdin))
375 strbuf_to_cone_pattern(&line, &pl);
376 } else {
377 for (i = 0; i < argc; i++) {
378 strbuf_setlen(&line, 0);
379 strbuf_addstr(&line, argv[i]);
380 strbuf_to_cone_pattern(&line, &pl);
383 } else {
384 if (set_opts.use_stdin) {
385 struct strbuf line = STRBUF_INIT;
387 while (!strbuf_getline(&line, stdin)) {
388 size_t len;
389 char *buf = strbuf_detach(&line, &len);
390 add_pattern(buf, empty_base, 0, &pl, 0);
392 } else {
393 for (i = 0; i < argc; i++)
394 add_pattern(argv[i], empty_base, 0, &pl, 0);
398 if (!core_apply_sparse_checkout) {
399 set_config(MODE_ALL_PATTERNS);
400 core_apply_sparse_checkout = 1;
401 changed_config = 1;
404 result = write_patterns_and_update(&pl);
406 if (result && changed_config)
407 set_config(MODE_NO_PATTERNS);
409 clear_pattern_list(&pl);
410 return result;
413 static int sparse_checkout_disable(int argc, const char **argv)
415 static const char *empty_base = "";
416 struct pattern_list pl;
417 struct strbuf match_all = STRBUF_INIT;
419 memset(&pl, 0, sizeof(pl));
420 hashmap_init(&pl.recursive_hashmap, pl_hashmap_cmp, NULL, 0);
421 hashmap_init(&pl.parent_hashmap, pl_hashmap_cmp, NULL, 0);
422 pl.use_cone_patterns = 0;
423 core_apply_sparse_checkout = 1;
425 strbuf_addstr(&match_all, "/*");
426 add_pattern(strbuf_detach(&match_all, NULL), empty_base, 0, &pl, 0);
428 if (update_working_directory(&pl))
429 die(_("error while refreshing working directory"));
431 clear_pattern_list(&pl);
432 return set_config(MODE_NO_PATTERNS);
435 int cmd_sparse_checkout(int argc, const char **argv, const char *prefix)
437 static struct option builtin_sparse_checkout_options[] = {
438 OPT_END(),
441 if (argc == 2 && !strcmp(argv[1], "-h"))
442 usage_with_options(builtin_sparse_checkout_usage,
443 builtin_sparse_checkout_options);
445 argc = parse_options(argc, argv, prefix,
446 builtin_sparse_checkout_options,
447 builtin_sparse_checkout_usage,
448 PARSE_OPT_STOP_AT_NON_OPTION);
450 git_config(git_default_config, NULL);
452 if (argc > 0) {
453 if (!strcmp(argv[0], "list"))
454 return sparse_checkout_list(argc, argv);
455 if (!strcmp(argv[0], "init"))
456 return sparse_checkout_init(argc, argv);
457 if (!strcmp(argv[0], "set"))
458 return sparse_checkout_set(argc, argv, prefix);
459 if (!strcmp(argv[0], "disable"))
460 return sparse_checkout_disable(argc, argv);
463 usage_with_options(builtin_sparse_checkout_usage,
464 builtin_sparse_checkout_options);