]>
Commit | Line | Data |
---|---|---|
03eae9af | 1 | #define USE_THE_REPOSITORY_VARIABLE |
41f43b82 PS |
2 | #define DISABLE_SIGN_COMPARE_WARNINGS |
3 | ||
94c0956b DS |
4 | #include "builtin.h" |
5 | #include "config.h" | |
6 | #include "dir.h" | |
32a8f510 | 7 | #include "environment.h" |
f394e093 | 8 | #include "gettext.h" |
87bed179 | 9 | #include "object-file.h" |
dabab1d6 | 10 | #include "object-name.h" |
94c0956b | 11 | #include "parse-options.h" |
1a99fe80 | 12 | #include "path.h" |
94c0956b | 13 | #include "pathspec.h" |
94c0956b | 14 | #include "strbuf.h" |
af09ce24 | 15 | #include "string-list.h" |
e091228e | 16 | #include "lockfile.h" |
e091228e | 17 | #include "unpack-trees.h" |
d585f0e7 | 18 | #include "quote.h" |
e38da487 | 19 | #include "setup.h" |
122ba1f7 | 20 | #include "sparse-index.h" |
7316dc5f | 21 | #include "worktree.h" |
94c0956b | 22 | |
416adc87 DS |
23 | static const char *empty_base = ""; |
24 | ||
94c0956b | 25 | static char const * const builtin_sparse_checkout_usage[] = { |
00408ade | 26 | N_("git sparse-checkout (init | list | set | add | reapply | disable | check-rules) [<options>]"), |
94c0956b DS |
27 | NULL |
28 | }; | |
29 | ||
94c0956b DS |
30 | static void write_patterns_to_file(FILE *fp, struct pattern_list *pl) |
31 | { | |
32 | int i; | |
33 | ||
34 | for (i = 0; i < pl->nr; i++) { | |
35 | struct path_pattern *p = pl->patterns[i]; | |
36 | ||
37 | if (p->flags & PATTERN_FLAG_NEGATIVE) | |
38 | fprintf(fp, "!"); | |
39 | ||
40 | fprintf(fp, "%s", p->pattern); | |
41 | ||
42 | if (p->flags & PATTERN_FLAG_MUSTBEDIR) | |
43 | fprintf(fp, "/"); | |
44 | ||
45 | fprintf(fp, "\n"); | |
46 | } | |
47 | } | |
48 | ||
75d3bee1 | 49 | static char const * const builtin_sparse_checkout_list_usage[] = { |
959d670d | 50 | "git sparse-checkout list", |
75d3bee1 JK |
51 | NULL |
52 | }; | |
53 | ||
6f33d8e2 KN |
54 | static int sparse_checkout_list(int argc, const char **argv, const char *prefix, |
55 | struct repository *repo UNUSED) | |
94c0956b | 56 | { |
75d3bee1 JK |
57 | static struct option builtin_sparse_checkout_list_options[] = { |
58 | OPT_END(), | |
59 | }; | |
94c0956b DS |
60 | struct pattern_list pl; |
61 | char *sparse_filename; | |
62 | int res; | |
63 | ||
24fc2cde | 64 | setup_work_tree(); |
45c5e470 EN |
65 | if (!core_apply_sparse_checkout) |
66 | die(_("this worktree is not sparse")); | |
67 | ||
ecd2d3ef | 68 | argc = parse_options(argc, argv, prefix, |
75d3bee1 JK |
69 | builtin_sparse_checkout_list_options, |
70 | builtin_sparse_checkout_list_usage, 0); | |
71 | ||
94c0956b DS |
72 | memset(&pl, 0, sizeof(pl)); |
73 | ||
de11951b DS |
74 | pl.use_cone_patterns = core_sparse_checkout_cone; |
75 | ||
94c0956b | 76 | sparse_filename = get_sparse_checkout_filename(); |
1679d60b | 77 | res = add_patterns_from_file_to_list(sparse_filename, "", 0, &pl, NULL, 0); |
94c0956b DS |
78 | free(sparse_filename); |
79 | ||
80 | if (res < 0) { | |
81 | warning(_("this worktree is not sparse (sparse-checkout file may not exist)")); | |
82 | return 0; | |
83 | } | |
84 | ||
de11951b DS |
85 | if (pl.use_cone_patterns) { |
86 | int i; | |
87 | struct pattern_entry *pe; | |
88 | struct hashmap_iter iter; | |
89 | struct string_list sl = STRING_LIST_INIT_DUP; | |
90 | ||
91 | hashmap_for_each_entry(&pl.recursive_hashmap, &iter, pe, ent) { | |
92 | /* pe->pattern starts with "/", skip it */ | |
93 | string_list_insert(&sl, pe->pattern + 1); | |
94 | } | |
95 | ||
96 | string_list_sort(&sl); | |
97 | ||
e55682ea DS |
98 | for (i = 0; i < sl.nr; i++) { |
99 | quote_c_style(sl.items[i].string, NULL, stdout, 0); | |
100 | printf("\n"); | |
101 | } | |
a544b7da JK |
102 | |
103 | string_list_clear(&sl, 0); | |
521e04e6 JK |
104 | } else { |
105 | write_patterns_to_file(stdout, &pl); | |
de11951b DS |
106 | } |
107 | ||
94c0956b DS |
108 | clear_pattern_list(&pl); |
109 | ||
110 | return 0; | |
111 | } | |
112 | ||
55dfcf95 DS |
113 | static void clean_tracked_sparse_directories(struct repository *r) |
114 | { | |
115 | int i, was_full = 0; | |
116 | struct strbuf path = STRBUF_INIT; | |
117 | size_t pathlen; | |
118 | struct string_list_item *item; | |
119 | struct string_list sparse_dirs = STRING_LIST_INIT_DUP; | |
120 | ||
121 | /* | |
122 | * If we are not using cone mode patterns, then we cannot | |
123 | * delete directories outside of the sparse cone. | |
124 | */ | |
125 | if (!r || !r->index || !r->worktree) | |
126 | return; | |
127 | if (init_sparse_checkout_patterns(r->index) || | |
128 | !r->index->sparse_checkout_patterns->use_cone_patterns) | |
129 | return; | |
130 | ||
131 | /* | |
132 | * Use the sparse index as a data structure to assist finding | |
133 | * directories that are safe to delete. This conversion to a | |
134 | * sparse index will not delete directories that contain | |
135 | * conflicted entries or submodules. | |
136 | */ | |
9fadb373 | 137 | if (r->index->sparse_index == INDEX_EXPANDED) { |
55dfcf95 DS |
138 | /* |
139 | * If something, such as a merge conflict or other concern, | |
140 | * prevents us from converting to a sparse index, then do | |
141 | * not try deleting files. | |
142 | */ | |
143 | if (convert_to_sparse(r->index, SPARSE_INDEX_MEMORY_ONLY)) | |
144 | return; | |
145 | was_full = 1; | |
146 | } | |
147 | ||
148 | strbuf_addstr(&path, r->worktree); | |
149 | strbuf_complete(&path, '/'); | |
150 | pathlen = path.len; | |
151 | ||
152 | /* | |
153 | * Collect directories that have gone out of scope but also | |
154 | * exist on disk, so there is some work to be done. We need to | |
155 | * store the entries in a list before exploring, since that might | |
156 | * expand the sparse-index again. | |
157 | */ | |
158 | for (i = 0; i < r->index->cache_nr; i++) { | |
159 | struct cache_entry *ce = r->index->cache[i]; | |
160 | ||
161 | if (S_ISSPARSEDIR(ce->ce_mode) && | |
162 | repo_file_exists(r, ce->name)) | |
163 | string_list_append(&sparse_dirs, ce->name); | |
164 | } | |
165 | ||
166 | for_each_string_list_item(item, &sparse_dirs) { | |
167 | struct dir_struct dir = DIR_INIT; | |
168 | struct pathspec p = { 0 }; | |
169 | struct strvec s = STRVEC_INIT; | |
170 | ||
171 | strbuf_setlen(&path, pathlen); | |
172 | strbuf_addstr(&path, item->string); | |
173 | ||
174 | dir.flags |= DIR_SHOW_IGNORED_TOO; | |
175 | ||
176 | setup_standard_excludes(&dir); | |
177 | strvec_push(&s, path.buf); | |
178 | ||
179 | parse_pathspec(&p, PATHSPEC_GLOB, 0, NULL, s.v); | |
180 | fill_directory(&dir, r->index, &p); | |
181 | ||
182 | if (dir.nr) { | |
183 | warning(_("directory '%s' contains untracked files," | |
184 | " but is not in the sparse-checkout cone"), | |
185 | item->string); | |
186 | } else if (remove_dir_recursively(&path, 0)) { | |
187 | /* | |
188 | * Removal is "best effort". If something blocks | |
189 | * the deletion, then continue with a warning. | |
190 | */ | |
191 | warning(_("failed to remove directory '%s'"), | |
192 | item->string); | |
193 | } | |
194 | ||
0f03f04c EN |
195 | strvec_clear(&s); |
196 | clear_pathspec(&p); | |
55dfcf95 DS |
197 | dir_clear(&dir); |
198 | } | |
199 | ||
200 | string_list_clear(&sparse_dirs, 0); | |
201 | strbuf_release(&path); | |
202 | ||
203 | if (was_full) | |
204 | ensure_full_index(r->index); | |
205 | } | |
206 | ||
e091228e | 207 | static int update_working_directory(struct pattern_list *pl) |
bab3c359 | 208 | { |
f56f31af | 209 | enum update_sparsity_result result; |
e091228e DS |
210 | struct unpack_trees_options o; |
211 | struct lock_file lock_file = LOCK_INIT; | |
e091228e | 212 | struct repository *r = the_repository; |
a14d49ca | 213 | struct pattern_list *old_pl; |
bab3c359 | 214 | |
b5bfc08a EN |
215 | /* If no branch has been checked out, there are no updates to make. */ |
216 | if (is_index_unborn(r->index)) | |
217 | return UPDATE_SPARSITY_SUCCESS; | |
218 | ||
a14d49ca | 219 | old_pl = r->index->sparse_checkout_patterns; |
836e25c5 DS |
220 | r->index->sparse_checkout_patterns = pl; |
221 | ||
e091228e DS |
222 | memset(&o, 0, sizeof(o)); |
223 | o.verbose_update = isatty(2); | |
e091228e | 224 | o.update = 1; |
e091228e DS |
225 | o.head_idx = -1; |
226 | o.src_index = r->index; | |
227 | o.dst_index = r->index; | |
228 | o.skip_sparse_checkout = 0; | |
e091228e | 229 | |
e091228e DS |
230 | setup_work_tree(); |
231 | ||
e091228e DS |
232 | repo_hold_locked_index(r, &lock_file, LOCK_DIE_ON_ERROR); |
233 | ||
4ee5d50f | 234 | setup_unpack_trees_porcelain(&o, "sparse-checkout"); |
1147c56f | 235 | result = update_sparsity(&o, pl); |
4ee5d50f | 236 | clear_unpack_trees_porcelain(&o); |
e091228e | 237 | |
f56f31af EN |
238 | if (result == UPDATE_SPARSITY_WARNINGS) |
239 | /* | |
240 | * We don't do any special handling of warnings from untracked | |
241 | * files in the way or dirty entries that can't be removed. | |
242 | */ | |
243 | result = UPDATE_SPARSITY_SUCCESS; | |
244 | if (result == UPDATE_SPARSITY_SUCCESS) | |
e091228e | 245 | write_locked_index(r->index, &lock_file, COMMIT_LOCK); |
f56f31af | 246 | else |
e091228e | 247 | rollback_lock_file(&lock_file); |
bab3c359 | 248 | |
55dfcf95 DS |
249 | clean_tracked_sparse_directories(r); |
250 | ||
a14d49ca JK |
251 | if (r->index->sparse_checkout_patterns != pl) { |
252 | clear_pattern_list(r->index->sparse_checkout_patterns); | |
253 | FREE_AND_NULL(r->index->sparse_checkout_patterns); | |
254 | } | |
255 | r->index->sparse_checkout_patterns = old_pl; | |
256 | ||
bab3c359 DS |
257 | return result; |
258 | } | |
259 | ||
d585f0e7 DS |
260 | static char *escaped_pattern(char *pattern) |
261 | { | |
262 | char *p = pattern; | |
263 | struct strbuf final = STRBUF_INIT; | |
264 | ||
265 | while (*p) { | |
e53ffe27 | 266 | if (is_glob_special(*p)) |
d585f0e7 DS |
267 | strbuf_addch(&final, '\\'); |
268 | ||
269 | strbuf_addch(&final, *p); | |
270 | p++; | |
271 | } | |
272 | ||
273 | return strbuf_detach(&final, NULL); | |
274 | } | |
275 | ||
af09ce24 DS |
276 | static void write_cone_to_file(FILE *fp, struct pattern_list *pl) |
277 | { | |
278 | int i; | |
279 | struct pattern_entry *pe; | |
280 | struct hashmap_iter iter; | |
281 | struct string_list sl = STRING_LIST_INIT_DUP; | |
e9de487a | 282 | struct strbuf parent_pattern = STRBUF_INIT; |
af09ce24 | 283 | |
e9de487a DS |
284 | hashmap_for_each_entry(&pl->parent_hashmap, &iter, pe, ent) { |
285 | if (hashmap_get_entry(&pl->recursive_hashmap, pe, ent, NULL)) | |
286 | continue; | |
287 | ||
288 | if (!hashmap_contains_parent(&pl->recursive_hashmap, | |
289 | pe->pattern, | |
290 | &parent_pattern)) | |
291 | string_list_insert(&sl, pe->pattern); | |
292 | } | |
af09ce24 DS |
293 | |
294 | string_list_sort(&sl); | |
295 | string_list_remove_duplicates(&sl, 0); | |
296 | ||
297 | fprintf(fp, "/*\n!/*/\n"); | |
298 | ||
299 | for (i = 0; i < sl.nr; i++) { | |
d585f0e7 | 300 | char *pattern = escaped_pattern(sl.items[i].string); |
af09ce24 DS |
301 | |
302 | if (strlen(pattern)) | |
303 | fprintf(fp, "%s/\n!%s/*/\n", pattern, pattern); | |
d585f0e7 | 304 | free(pattern); |
af09ce24 DS |
305 | } |
306 | ||
307 | string_list_clear(&sl, 0); | |
308 | ||
e9de487a DS |
309 | hashmap_for_each_entry(&pl->recursive_hashmap, &iter, pe, ent) { |
310 | if (!hashmap_contains_parent(&pl->recursive_hashmap, | |
311 | pe->pattern, | |
312 | &parent_pattern)) | |
313 | string_list_insert(&sl, pe->pattern); | |
314 | } | |
315 | ||
316 | strbuf_release(&parent_pattern); | |
af09ce24 DS |
317 | |
318 | string_list_sort(&sl); | |
319 | string_list_remove_duplicates(&sl, 0); | |
320 | ||
321 | for (i = 0; i < sl.nr; i++) { | |
d585f0e7 | 322 | char *pattern = escaped_pattern(sl.items[i].string); |
af09ce24 | 323 | fprintf(fp, "%s/\n", pattern); |
d585f0e7 | 324 | free(pattern); |
af09ce24 | 325 | } |
2181fe6e JK |
326 | |
327 | string_list_clear(&sl, 0); | |
af09ce24 DS |
328 | } |
329 | ||
330 | static int write_patterns_and_update(struct pattern_list *pl) | |
331 | { | |
332 | char *sparse_filename; | |
333 | FILE *fp; | |
fb10ca5b | 334 | struct lock_file lk = LOCK_INIT; |
e091228e DS |
335 | int result; |
336 | ||
fb10ca5b | 337 | sparse_filename = get_sparse_checkout_filename(); |
3c754067 | 338 | |
1a99fe80 | 339 | if (safe_create_leading_directories(the_repository, sparse_filename)) |
3c754067 DS |
340 | die(_("failed to create directory for sparse-checkout file")); |
341 | ||
a71c4782 | 342 | hold_lock_file_for_update(&lk, sparse_filename, LOCK_DIE_ON_ERROR); |
e091228e | 343 | |
fb10ca5b | 344 | result = update_working_directory(pl); |
e091228e | 345 | if (result) { |
fb10ca5b | 346 | rollback_lock_file(&lk); |
e091228e | 347 | update_working_directory(NULL); |
d39cc718 | 348 | goto out; |
e091228e | 349 | } |
af09ce24 | 350 | |
a71c4782 JK |
351 | fp = fdopen_lock_file(&lk, "w"); |
352 | if (!fp) | |
353 | die_errno(_("unable to fdopen %s"), get_lock_file_path(&lk)); | |
af09ce24 DS |
354 | |
355 | if (core_sparse_checkout_cone) | |
356 | write_cone_to_file(fp, pl); | |
357 | else | |
358 | write_patterns_to_file(fp, pl); | |
359 | ||
19ace71d JK |
360 | if (commit_lock_file(&lk)) |
361 | die_errno(_("unable to write %s"), sparse_filename); | |
e091228e | 362 | |
d39cc718 | 363 | out: |
e091228e | 364 | clear_pattern_list(pl); |
19ace71d | 365 | free(sparse_filename); |
d39cc718 | 366 | return result; |
af09ce24 DS |
367 | } |
368 | ||
bab3c359 DS |
369 | enum sparse_checkout_mode { |
370 | MODE_NO_PATTERNS = 0, | |
371 | MODE_ALL_PATTERNS = 1, | |
af09ce24 | 372 | MODE_CONE_PATTERNS = 2, |
bab3c359 DS |
373 | }; |
374 | ||
375 | static int set_config(enum sparse_checkout_mode mode) | |
376 | { | |
7316dc5f DS |
377 | /* Update to use worktree config, if not already. */ |
378 | if (init_worktree_config(the_repository)) { | |
379 | error(_("failed to initialize worktree config")); | |
bab3c359 DS |
380 | return 1; |
381 | } | |
382 | ||
7316dc5f DS |
383 | if (repo_config_set_worktree_gently(the_repository, |
384 | "core.sparseCheckout", | |
385 | mode ? "true" : "false") || | |
386 | repo_config_set_worktree_gently(the_repository, | |
387 | "core.sparseCheckoutCone", | |
388 | mode == MODE_CONE_PATTERNS ? | |
389 | "true" : "false")) | |
390 | return 1; | |
af09ce24 | 391 | |
dcc5fd5f | 392 | if (mode == MODE_NO_PATTERNS) |
7316dc5f | 393 | return set_sparse_index_config(the_repository, 0); |
dcc5fd5f | 394 | |
bab3c359 DS |
395 | return 0; |
396 | } | |
397 | ||
00408ade | 398 | static enum sparse_checkout_mode update_cone_mode(int *cone_mode) { |
be61fd11 EN |
399 | /* If not specified, use previous definition of cone mode */ |
400 | if (*cone_mode == -1 && core_apply_sparse_checkout) | |
401 | *cone_mode = core_sparse_checkout_cone; | |
402 | ||
403 | /* Set cone/non-cone mode appropriately */ | |
404 | core_apply_sparse_checkout = 1; | |
2d95707a | 405 | if (*cone_mode == 1 || *cone_mode == -1) { |
be61fd11 | 406 | core_sparse_checkout_cone = 1; |
00408ade | 407 | return MODE_CONE_PATTERNS; |
be61fd11 | 408 | } |
00408ade WS |
409 | core_sparse_checkout_cone = 0; |
410 | return MODE_ALL_PATTERNS; | |
411 | } | |
412 | ||
413 | static int update_modes(int *cone_mode, int *sparse_index) | |
414 | { | |
415 | int mode, record_mode; | |
416 | ||
417 | /* Determine if we need to record the mode; ensure sparse checkout on */ | |
418 | record_mode = (*cone_mode != -1) || !core_apply_sparse_checkout; | |
419 | ||
420 | mode = update_cone_mode(cone_mode); | |
be61fd11 EN |
421 | if (record_mode && set_config(mode)) |
422 | return 1; | |
423 | ||
424 | /* Set sparse-index/non-sparse-index mode if specified */ | |
425 | if (*sparse_index >= 0) { | |
426 | if (set_sparse_index_config(the_repository, *sparse_index) < 0) | |
427 | die(_("failed to modify sparse-index config")); | |
428 | ||
429 | /* force an index rewrite */ | |
430 | repo_read_index(the_repository); | |
431 | the_repository->index->updated_workdir = 1; | |
2d443389 DS |
432 | |
433 | if (!*sparse_index) | |
434 | ensure_full_index(the_repository->index); | |
be61fd11 EN |
435 | } |
436 | ||
437 | return 0; | |
438 | } | |
439 | ||
af09ce24 | 440 | static char const * const builtin_sparse_checkout_init_usage[] = { |
959d670d | 441 | "git sparse-checkout init [--cone] [--[no-]sparse-index]", |
af09ce24 DS |
442 | NULL |
443 | }; | |
444 | ||
445 | static struct sparse_checkout_init_opts { | |
446 | int cone_mode; | |
122ba1f7 | 447 | int sparse_index; |
af09ce24 DS |
448 | } init_opts; |
449 | ||
6f33d8e2 KN |
450 | static int sparse_checkout_init(int argc, const char **argv, const char *prefix, |
451 | struct repository *repo UNUSED) | |
bab3c359 DS |
452 | { |
453 | struct pattern_list pl; | |
454 | char *sparse_filename; | |
bab3c359 | 455 | int res; |
d89f09c8 | 456 | struct object_id oid; |
bab3c359 | 457 | |
af09ce24 DS |
458 | static struct option builtin_sparse_checkout_init_options[] = { |
459 | OPT_BOOL(0, "cone", &init_opts.cone_mode, | |
460 | N_("initialize the sparse-checkout in cone mode")), | |
122ba1f7 DS |
461 | OPT_BOOL(0, "sparse-index", &init_opts.sparse_index, |
462 | N_("toggle the use of a sparse index")), | |
af09ce24 DS |
463 | OPT_END(), |
464 | }; | |
465 | ||
24fc2cde | 466 | setup_work_tree(); |
cff4e913 | 467 | repo_read_index(the_repository); |
cff4e913 | 468 | |
be61fd11 | 469 | init_opts.cone_mode = -1; |
122ba1f7 DS |
470 | init_opts.sparse_index = -1; |
471 | ||
ecd2d3ef | 472 | argc = parse_options(argc, argv, prefix, |
af09ce24 DS |
473 | builtin_sparse_checkout_init_options, |
474 | builtin_sparse_checkout_init_usage, 0); | |
475 | ||
be61fd11 | 476 | if (update_modes(&init_opts.cone_mode, &init_opts.sparse_index)) |
bab3c359 DS |
477 | return 1; |
478 | ||
479 | memset(&pl, 0, sizeof(pl)); | |
480 | ||
481 | sparse_filename = get_sparse_checkout_filename(); | |
1679d60b | 482 | res = add_patterns_from_file_to_list(sparse_filename, "", 0, &pl, NULL, 0); |
bab3c359 DS |
483 | |
484 | /* If we already have a sparse-checkout file, use it. */ | |
485 | if (res >= 0) { | |
486 | free(sparse_filename); | |
db83b64c | 487 | clear_pattern_list(&pl); |
416adc87 | 488 | return update_working_directory(NULL); |
bab3c359 DS |
489 | } |
490 | ||
d850b7a5 | 491 | if (repo_get_oid(the_repository, "HEAD", &oid)) { |
416adc87 | 492 | FILE *fp; |
bab3c359 | 493 | |
416adc87 | 494 | /* assume we are in a fresh repo, but update the sparse-checkout file */ |
1a99fe80 | 495 | if (safe_create_leading_directories(the_repository, sparse_filename)) |
7f44842a JT |
496 | die(_("unable to create leading directories of %s"), |
497 | sparse_filename); | |
416adc87 DS |
498 | fp = xfopen(sparse_filename, "w"); |
499 | if (!fp) | |
500 | die(_("failed to open '%s'"), sparse_filename); | |
bab3c359 | 501 | |
416adc87 DS |
502 | free(sparse_filename); |
503 | fprintf(fp, "/*\n!/*/\n"); | |
504 | fclose(fp); | |
d89f09c8 DS |
505 | return 0; |
506 | } | |
507 | ||
008f59d2 JK |
508 | free(sparse_filename); |
509 | ||
4d7f95ed JK |
510 | add_pattern("/*", empty_base, 0, &pl, 0); |
511 | add_pattern("!/*/", empty_base, 0, &pl, 0); | |
dcc5fd5f | 512 | pl.use_cone_patterns = init_opts.cone_mode; |
416adc87 DS |
513 | |
514 | return write_patterns_and_update(&pl); | |
bab3c359 DS |
515 | } |
516 | ||
af09ce24 | 517 | static void insert_recursive_pattern(struct pattern_list *pl, struct strbuf *path) |
f6039a94 | 518 | { |
af09ce24 DS |
519 | struct pattern_entry *e = xmalloc(sizeof(*e)); |
520 | e->patternlen = path->len; | |
521 | e->pattern = strbuf_detach(path, NULL); | |
74318423 | 522 | hashmap_entry_init(&e->ent, fspathhash(e->pattern)); |
f6039a94 | 523 | |
af09ce24 | 524 | hashmap_add(&pl->recursive_hashmap, &e->ent); |
f6039a94 | 525 | |
af09ce24 DS |
526 | while (e->patternlen) { |
527 | char *slash = strrchr(e->pattern, '/'); | |
528 | char *oldpattern = e->pattern; | |
529 | size_t newlen; | |
6d107751 | 530 | struct pattern_entry *dup; |
af09ce24 | 531 | |
391c3a10 | 532 | if (!slash || slash == e->pattern) |
af09ce24 DS |
533 | break; |
534 | ||
535 | newlen = slash - e->pattern; | |
536 | e = xmalloc(sizeof(struct pattern_entry)); | |
537 | e->patternlen = newlen; | |
538 | e->pattern = xstrndup(oldpattern, newlen); | |
74318423 | 539 | hashmap_entry_init(&e->ent, fspathhash(e->pattern)); |
af09ce24 | 540 | |
6d107751 JK |
541 | dup = hashmap_get_entry(&pl->parent_hashmap, e, ent, NULL); |
542 | if (!dup) { | |
af09ce24 | 543 | hashmap_add(&pl->parent_hashmap, &e->ent); |
6d107751 JK |
544 | } else { |
545 | free(e->pattern); | |
546 | free(e); | |
547 | e = dup; | |
548 | } | |
af09ce24 DS |
549 | } |
550 | } | |
551 | ||
552 | static void strbuf_to_cone_pattern(struct strbuf *line, struct pattern_list *pl) | |
553 | { | |
554 | strbuf_trim(line); | |
555 | ||
556 | strbuf_trim_trailing_dir_sep(line); | |
557 | ||
ef076599 DS |
558 | if (strbuf_normalize_path(line)) |
559 | die(_("could not normalize path %s"), line->buf); | |
560 | ||
af09ce24 DS |
561 | if (!line->len) |
562 | return; | |
563 | ||
564 | if (line->buf[0] != '/') | |
a91cc7fa | 565 | strbuf_insertstr(line, 0, "/"); |
af09ce24 DS |
566 | |
567 | insert_recursive_pattern(pl, line); | |
f6039a94 DS |
568 | } |
569 | ||
6fb705ab | 570 | static void add_patterns_from_input(struct pattern_list *pl, |
1530ff35 | 571 | int argc, const char **argv, |
00408ade | 572 | FILE *file) |
f6039a94 | 573 | { |
f6039a94 | 574 | int i; |
af09ce24 | 575 | if (core_sparse_checkout_cone) { |
7bffca95 DS |
576 | struct strbuf line = STRBUF_INIT; |
577 | ||
6fb705ab DS |
578 | hashmap_init(&pl->recursive_hashmap, pl_hashmap_cmp, NULL, 0); |
579 | hashmap_init(&pl->parent_hashmap, pl_hashmap_cmp, NULL, 0); | |
580 | pl->use_cone_patterns = 1; | |
af09ce24 | 581 | |
00408ade | 582 | if (file) { |
bd64de42 | 583 | struct strbuf unquoted = STRBUF_INIT; |
00408ade | 584 | while (!strbuf_getline(&line, file)) { |
bd64de42 DS |
585 | if (line.buf[0] == '"') { |
586 | strbuf_reset(&unquoted); | |
587 | if (unquote_c_style(&unquoted, line.buf, NULL)) | |
588 | die(_("unable to unquote C-style string '%s'"), | |
589 | line.buf); | |
590 | ||
591 | strbuf_swap(&unquoted, &line); | |
592 | } | |
593 | ||
6fb705ab | 594 | strbuf_to_cone_pattern(&line, pl); |
bd64de42 DS |
595 | } |
596 | ||
597 | strbuf_release(&unquoted); | |
af09ce24 DS |
598 | } else { |
599 | for (i = 0; i < argc; i++) { | |
600 | strbuf_setlen(&line, 0); | |
601 | strbuf_addstr(&line, argv[i]); | |
6fb705ab | 602 | strbuf_to_cone_pattern(&line, pl); |
af09ce24 | 603 | } |
7bffca95 | 604 | } |
d765fa03 | 605 | strbuf_release(&line); |
7bffca95 | 606 | } else { |
00408ade | 607 | if (file) { |
af09ce24 DS |
608 | struct strbuf line = STRBUF_INIT; |
609 | ||
c3324649 JK |
610 | while (!strbuf_getline(&line, file)) |
611 | add_pattern(line.buf, empty_base, 0, pl, 0); | |
612 | ||
613 | strbuf_release(&line); | |
af09ce24 DS |
614 | } else { |
615 | for (i = 0; i < argc; i++) | |
6fb705ab | 616 | add_pattern(argv[i], empty_base, 0, pl, 0); |
af09ce24 | 617 | } |
7bffca95 | 618 | } |
6fb705ab DS |
619 | } |
620 | ||
4bf0c06c DS |
621 | enum modify_type { |
622 | REPLACE, | |
2631dc87 | 623 | ADD, |
4bf0c06c DS |
624 | }; |
625 | ||
2631dc87 | 626 | static void add_patterns_cone_mode(int argc, const char **argv, |
1530ff35 EN |
627 | struct pattern_list *pl, |
628 | int use_stdin) | |
2631dc87 DS |
629 | { |
630 | struct strbuf buffer = STRBUF_INIT; | |
631 | struct pattern_entry *pe; | |
632 | struct hashmap_iter iter; | |
633 | struct pattern_list existing; | |
634 | char *sparse_filename = get_sparse_checkout_filename(); | |
635 | ||
00408ade WS |
636 | add_patterns_from_input(pl, argc, argv, |
637 | use_stdin ? stdin : NULL); | |
2631dc87 DS |
638 | |
639 | memset(&existing, 0, sizeof(existing)); | |
640 | existing.use_cone_patterns = core_sparse_checkout_cone; | |
641 | ||
642 | if (add_patterns_from_file_to_list(sparse_filename, "", 0, | |
1679d60b | 643 | &existing, NULL, 0)) |
2631dc87 DS |
644 | die(_("unable to load existing sparse-checkout patterns")); |
645 | free(sparse_filename); | |
646 | ||
a3eca584 DS |
647 | if (!existing.use_cone_patterns) |
648 | die(_("existing sparse-checkout patterns do not use cone mode")); | |
649 | ||
2631dc87 DS |
650 | hashmap_for_each_entry(&existing.recursive_hashmap, &iter, pe, ent) { |
651 | if (!hashmap_contains_parent(&pl->recursive_hashmap, | |
652 | pe->pattern, &buffer) || | |
653 | !hashmap_contains_parent(&pl->parent_hashmap, | |
654 | pe->pattern, &buffer)) { | |
655 | strbuf_reset(&buffer); | |
656 | strbuf_addstr(&buffer, pe->pattern); | |
657 | insert_recursive_pattern(pl, &buffer); | |
658 | } | |
659 | } | |
660 | ||
661 | clear_pattern_list(&existing); | |
662 | strbuf_release(&buffer); | |
663 | } | |
664 | ||
665 | static void add_patterns_literal(int argc, const char **argv, | |
1530ff35 EN |
666 | struct pattern_list *pl, |
667 | int use_stdin) | |
2631dc87 DS |
668 | { |
669 | char *sparse_filename = get_sparse_checkout_filename(); | |
670 | if (add_patterns_from_file_to_list(sparse_filename, "", 0, | |
1679d60b | 671 | pl, NULL, 0)) |
2631dc87 DS |
672 | die(_("unable to load existing sparse-checkout patterns")); |
673 | free(sparse_filename); | |
00408ade | 674 | add_patterns_from_input(pl, argc, argv, use_stdin ? stdin : NULL); |
2631dc87 DS |
675 | } |
676 | ||
58e7568c | 677 | static int modify_pattern_list(struct strvec *args, int use_stdin, |
1530ff35 | 678 | enum modify_type m) |
6fb705ab | 679 | { |
6fb705ab DS |
680 | int result; |
681 | int changed_config = 0; | |
836e25c5 | 682 | struct pattern_list *pl = xcalloc(1, sizeof(*pl)); |
6fb705ab | 683 | |
2631dc87 DS |
684 | switch (m) { |
685 | case ADD: | |
686 | if (core_sparse_checkout_cone) | |
58e7568c | 687 | add_patterns_cone_mode(args->nr, args->v, pl, use_stdin); |
2631dc87 | 688 | else |
58e7568c | 689 | add_patterns_literal(args->nr, args->v, pl, use_stdin); |
2631dc87 DS |
690 | break; |
691 | ||
692 | case REPLACE: | |
58e7568c | 693 | add_patterns_from_input(pl, args->nr, args->v, |
00408ade | 694 | use_stdin ? stdin : NULL); |
2631dc87 DS |
695 | break; |
696 | } | |
f6039a94 DS |
697 | |
698 | if (!core_apply_sparse_checkout) { | |
699 | set_config(MODE_ALL_PATTERNS); | |
700 | core_apply_sparse_checkout = 1; | |
701 | changed_config = 1; | |
702 | } | |
703 | ||
836e25c5 | 704 | result = write_patterns_and_update(pl); |
f6039a94 DS |
705 | |
706 | if (result && changed_config) | |
707 | set_config(MODE_NO_PATTERNS); | |
708 | ||
836e25c5 DS |
709 | clear_pattern_list(pl); |
710 | free(pl); | |
f6039a94 DS |
711 | return result; |
712 | } | |
713 | ||
58e7568c | 714 | static void sanitize_paths(struct strvec *args, |
4ce50436 | 715 | const char *prefix, int skip_checks) |
bb8b5e9a | 716 | { |
4ce50436 EN |
717 | int i; |
718 | ||
58e7568c | 719 | if (!args->nr) |
bb8b5e9a EN |
720 | return; |
721 | ||
722 | if (prefix && *prefix && core_sparse_checkout_cone) { | |
723 | /* | |
724 | * The args are not pathspecs, so unfortunately we | |
725 | * cannot imitate how cmd_add() uses parse_pathspec(). | |
726 | */ | |
bb8b5e9a EN |
727 | int prefix_len = strlen(prefix); |
728 | ||
58e7568c PS |
729 | for (i = 0; i < args->nr; i++) { |
730 | char *prefixed_path = prefix_path(prefix, prefix_len, args->v[i]); | |
731 | strvec_replace(args, i, prefixed_path); | |
732 | free(prefixed_path); | |
733 | } | |
bb8b5e9a EN |
734 | } |
735 | ||
4ce50436 EN |
736 | if (skip_checks) |
737 | return; | |
738 | ||
bb8b5e9a EN |
739 | if (prefix && *prefix && !core_sparse_checkout_cone) |
740 | die(_("please run from the toplevel directory in non-cone mode")); | |
741 | ||
8dd7c473 | 742 | if (core_sparse_checkout_cone) { |
58e7568c PS |
743 | for (i = 0; i < args->nr; i++) { |
744 | if (args->v[i][0] == '/') | |
8dd7c473 | 745 | die(_("specify directories rather than patterns (no leading slash)")); |
58e7568c | 746 | if (args->v[i][0] == '!') |
8dd7c473 | 747 | die(_("specify directories rather than patterns. If your directory starts with a '!', pass --skip-checks")); |
58e7568c | 748 | if (strpbrk(args->v[i], "*?[]")) |
8dd7c473 EN |
749 | die(_("specify directories rather than patterns. If your directory really has any of '*?[]\\' in it, pass --skip-checks")); |
750 | } | |
751 | } | |
752 | ||
58e7568c | 753 | for (i = 0; i < args->nr; i++) { |
4ce50436 EN |
754 | struct cache_entry *ce; |
755 | struct index_state *index = the_repository->index; | |
58e7568c | 756 | int pos = index_name_pos(index, args->v[i], strlen(args->v[i])); |
4ce50436 EN |
757 | |
758 | if (pos < 0) | |
759 | continue; | |
760 | ce = index->cache[pos]; | |
761 | if (S_ISSPARSEDIR(ce->ce_mode)) | |
762 | continue; | |
763 | ||
764 | if (core_sparse_checkout_cone) | |
58e7568c | 765 | die(_("'%s' is not a directory; to treat it as a directory anyway, rerun with --skip-checks"), args->v[i]); |
4ce50436 | 766 | else |
58e7568c | 767 | warning(_("pass a leading slash before paths such as '%s' if you want a single file (see NON-CONE PROBLEMS in the git-sparse-checkout manual)."), args->v[i]); |
4ce50436 | 768 | } |
bb8b5e9a EN |
769 | } |
770 | ||
0b624e03 | 771 | static char const * const builtin_sparse_checkout_add_usage[] = { |
4ce50436 | 772 | N_("git sparse-checkout add [--skip-checks] (--stdin | <patterns>)"), |
0b624e03 EN |
773 | NULL |
774 | }; | |
775 | ||
776 | static struct sparse_checkout_add_opts { | |
4ce50436 | 777 | int skip_checks; |
0b624e03 EN |
778 | int use_stdin; |
779 | } add_opts; | |
780 | ||
6f33d8e2 KN |
781 | static int sparse_checkout_add(int argc, const char **argv, const char *prefix, |
782 | struct repository *repo UNUSED) | |
0b624e03 EN |
783 | { |
784 | static struct option builtin_sparse_checkout_add_options[] = { | |
4ce50436 EN |
785 | OPT_BOOL_F(0, "skip-checks", &add_opts.skip_checks, |
786 | N_("skip some sanity checks on the given paths that might give false positives"), | |
787 | PARSE_OPT_NONEG), | |
0b624e03 EN |
788 | OPT_BOOL(0, "stdin", &add_opts.use_stdin, |
789 | N_("read patterns from standard in")), | |
790 | OPT_END(), | |
791 | }; | |
58e7568c PS |
792 | struct strvec patterns = STRVEC_INIT; |
793 | int ret; | |
0b624e03 | 794 | |
24fc2cde | 795 | setup_work_tree(); |
45c5e470 EN |
796 | if (!core_apply_sparse_checkout) |
797 | die(_("no sparse-checkout to add to")); | |
798 | ||
0b624e03 EN |
799 | repo_read_index(the_repository); |
800 | ||
801 | argc = parse_options(argc, argv, prefix, | |
802 | builtin_sparse_checkout_add_options, | |
f8ab66f9 | 803 | builtin_sparse_checkout_add_usage, 0); |
0b624e03 | 804 | |
58e7568c PS |
805 | for (int i = 0; i < argc; i++) |
806 | strvec_push(&patterns, argv[i]); | |
807 | sanitize_paths(&patterns, prefix, add_opts.skip_checks); | |
808 | ||
809 | ret = modify_pattern_list(&patterns, add_opts.use_stdin, ADD); | |
bb8b5e9a | 810 | |
58e7568c PS |
811 | strvec_clear(&patterns); |
812 | return ret; | |
0b624e03 EN |
813 | } |
814 | ||
815 | static char const * const builtin_sparse_checkout_set_usage[] = { | |
4ce50436 | 816 | N_("git sparse-checkout set [--[no-]cone] [--[no-]sparse-index] [--skip-checks] (--stdin | <patterns>)"), |
0b624e03 EN |
817 | NULL |
818 | }; | |
819 | ||
820 | static struct sparse_checkout_set_opts { | |
f2e3a218 EN |
821 | int cone_mode; |
822 | int sparse_index; | |
4ce50436 | 823 | int skip_checks; |
0b624e03 EN |
824 | int use_stdin; |
825 | } set_opts; | |
826 | ||
6f33d8e2 KN |
827 | static int sparse_checkout_set(int argc, const char **argv, const char *prefix, |
828 | struct repository *repo UNUSED) | |
4bf0c06c | 829 | { |
f2e3a218 EN |
830 | int default_patterns_nr = 2; |
831 | const char *default_patterns[] = {"/*", "!/*/", NULL}; | |
832 | ||
4bf0c06c | 833 | static struct option builtin_sparse_checkout_set_options[] = { |
f2e3a218 EN |
834 | OPT_BOOL(0, "cone", &set_opts.cone_mode, |
835 | N_("initialize the sparse-checkout in cone mode")), | |
836 | OPT_BOOL(0, "sparse-index", &set_opts.sparse_index, | |
837 | N_("toggle the use of a sparse index")), | |
4ce50436 EN |
838 | OPT_BOOL_F(0, "skip-checks", &set_opts.skip_checks, |
839 | N_("skip some sanity checks on the given paths that might give false positives"), | |
840 | PARSE_OPT_NONEG), | |
f85751a1 EN |
841 | OPT_BOOL_F(0, "stdin", &set_opts.use_stdin, |
842 | N_("read patterns from standard in"), | |
843 | PARSE_OPT_NONEG), | |
4bf0c06c DS |
844 | OPT_END(), |
845 | }; | |
58e7568c PS |
846 | struct strvec patterns = STRVEC_INIT; |
847 | int ret; | |
4bf0c06c | 848 | |
24fc2cde | 849 | setup_work_tree(); |
4bf0c06c | 850 | repo_read_index(the_repository); |
4bf0c06c | 851 | |
f2e3a218 EN |
852 | set_opts.cone_mode = -1; |
853 | set_opts.sparse_index = -1; | |
854 | ||
4bf0c06c DS |
855 | argc = parse_options(argc, argv, prefix, |
856 | builtin_sparse_checkout_set_options, | |
f8ab66f9 | 857 | builtin_sparse_checkout_set_usage, 0); |
4bf0c06c | 858 | |
f2e3a218 EN |
859 | if (update_modes(&set_opts.cone_mode, &set_opts.sparse_index)) |
860 | return 1; | |
861 | ||
862 | /* | |
863 | * Cone mode automatically specifies the toplevel directory. For | |
864 | * non-cone mode, if nothing is specified, manually select just the | |
865 | * top-level directory (much as 'init' would do). | |
866 | */ | |
53ded839 | 867 | if (!core_sparse_checkout_cone && !set_opts.use_stdin && argc == 0) { |
58e7568c PS |
868 | for (int i = 0; i < default_patterns_nr; i++) |
869 | strvec_push(&patterns, default_patterns[i]); | |
bb8b5e9a | 870 | } else { |
58e7568c PS |
871 | for (int i = 0; i < argc; i++) |
872 | strvec_push(&patterns, argv[i]); | |
873 | sanitize_paths(&patterns, prefix, set_opts.skip_checks); | |
f2e3a218 EN |
874 | } |
875 | ||
58e7568c PS |
876 | ret = modify_pattern_list(&patterns, set_opts.use_stdin, REPLACE); |
877 | ||
878 | strvec_clear(&patterns); | |
879 | return ret; | |
4bf0c06c DS |
880 | } |
881 | ||
75d3bee1 | 882 | static char const * const builtin_sparse_checkout_reapply_usage[] = { |
959d670d | 883 | "git sparse-checkout reapply [--[no-]cone] [--[no-]sparse-index]", |
75d3bee1 JK |
884 | NULL |
885 | }; | |
886 | ||
4e256731 EN |
887 | static struct sparse_checkout_reapply_opts { |
888 | int cone_mode; | |
889 | int sparse_index; | |
890 | } reapply_opts; | |
891 | ||
1c3502b1 | 892 | static int sparse_checkout_reapply(int argc, const char **argv, |
6f33d8e2 KN |
893 | const char *prefix, |
894 | struct repository *repo UNUSED) | |
5644ca28 | 895 | { |
75d3bee1 | 896 | static struct option builtin_sparse_checkout_reapply_options[] = { |
4e256731 EN |
897 | OPT_BOOL(0, "cone", &reapply_opts.cone_mode, |
898 | N_("initialize the sparse-checkout in cone mode")), | |
899 | OPT_BOOL(0, "sparse-index", &reapply_opts.sparse_index, | |
900 | N_("toggle the use of a sparse index")), | |
75d3bee1 JK |
901 | OPT_END(), |
902 | }; | |
903 | ||
24fc2cde | 904 | setup_work_tree(); |
45c5e470 EN |
905 | if (!core_apply_sparse_checkout) |
906 | die(_("must be in a sparse-checkout to reapply sparsity patterns")); | |
907 | ||
f748012e EN |
908 | reapply_opts.cone_mode = -1; |
909 | reapply_opts.sparse_index = -1; | |
910 | ||
ecd2d3ef | 911 | argc = parse_options(argc, argv, prefix, |
75d3bee1 JK |
912 | builtin_sparse_checkout_reapply_options, |
913 | builtin_sparse_checkout_reapply_usage, 0); | |
914 | ||
5644ca28 | 915 | repo_read_index(the_repository); |
4e256731 | 916 | |
4e256731 EN |
917 | if (update_modes(&reapply_opts.cone_mode, &reapply_opts.sparse_index)) |
918 | return 1; | |
919 | ||
5644ca28 EN |
920 | return update_working_directory(NULL); |
921 | } | |
922 | ||
75d3bee1 | 923 | static char const * const builtin_sparse_checkout_disable_usage[] = { |
959d670d | 924 | "git sparse-checkout disable", |
75d3bee1 JK |
925 | NULL |
926 | }; | |
927 | ||
1c3502b1 | 928 | static int sparse_checkout_disable(int argc, const char **argv, |
6f33d8e2 KN |
929 | const char *prefix, |
930 | struct repository *repo UNUSED) | |
72918c1a | 931 | { |
75d3bee1 JK |
932 | static struct option builtin_sparse_checkout_disable_options[] = { |
933 | OPT_END(), | |
934 | }; | |
99dfa6f9 | 935 | struct pattern_list pl; |
72918c1a | 936 | |
45c5e470 EN |
937 | /* |
938 | * We do not exit early if !core_apply_sparse_checkout; due to the | |
939 | * ability for users to manually muck things up between | |
940 | * direct editing of .git/info/sparse-checkout | |
941 | * running read-tree -m u HEAD or update-index --skip-worktree | |
942 | * direct toggling of config options | |
943 | * users might end up with an index with SKIP_WORKTREE bit set on | |
944 | * some files and not know how to undo it. So, here we just | |
945 | * forcibly return to a dense checkout regardless of initial state. | |
946 | */ | |
947 | ||
24fc2cde | 948 | setup_work_tree(); |
ecd2d3ef | 949 | argc = parse_options(argc, argv, prefix, |
75d3bee1 JK |
950 | builtin_sparse_checkout_disable_options, |
951 | builtin_sparse_checkout_disable_usage, 0); | |
952 | ||
537e516a DS |
953 | /* |
954 | * Disable the advice message for expanding a sparse index, as we | |
955 | * are expecting to do that when disabling sparse-checkout. | |
956 | */ | |
957 | give_advice_on_expansion = 0; | |
cff4e913 | 958 | repo_read_index(the_repository); |
cff4e913 | 959 | |
99dfa6f9 DS |
960 | memset(&pl, 0, sizeof(pl)); |
961 | hashmap_init(&pl.recursive_hashmap, pl_hashmap_cmp, NULL, 0); | |
962 | hashmap_init(&pl.parent_hashmap, pl_hashmap_cmp, NULL, 0); | |
963 | pl.use_cone_patterns = 0; | |
964 | core_apply_sparse_checkout = 1; | |
72918c1a | 965 | |
4d7f95ed | 966 | add_pattern("/*", empty_base, 0, &pl, 0); |
72918c1a | 967 | |
dcc5fd5f DS |
968 | prepare_repo_settings(the_repository); |
969 | the_repository->settings.sparse_index = 0; | |
970 | ||
99dfa6f9 | 971 | if (update_working_directory(&pl)) |
72918c1a DS |
972 | die(_("error while refreshing working directory")); |
973 | ||
99dfa6f9 | 974 | clear_pattern_list(&pl); |
72918c1a DS |
975 | return set_config(MODE_NO_PATTERNS); |
976 | } | |
977 | ||
00408ade WS |
978 | static char const * const builtin_sparse_checkout_check_rules_usage[] = { |
979 | N_("git sparse-checkout check-rules [-z] [--skip-checks]" | |
980 | "[--[no-]cone] [--rules-file <file>]"), | |
981 | NULL | |
982 | }; | |
983 | ||
984 | static struct sparse_checkout_check_rules_opts { | |
985 | int cone_mode; | |
986 | int null_termination; | |
987 | char *rules_file; | |
988 | } check_rules_opts; | |
989 | ||
990 | static int check_rules(struct pattern_list *pl, int null_terminated) { | |
991 | struct strbuf line = STRBUF_INIT; | |
992 | struct strbuf unquoted = STRBUF_INIT; | |
993 | char *path; | |
994 | int line_terminator = null_terminated ? 0 : '\n'; | |
995 | strbuf_getline_fn getline_fn = null_terminated ? strbuf_getline_nul | |
996 | : strbuf_getline; | |
997 | the_repository->index->sparse_checkout_patterns = pl; | |
998 | while (!getline_fn(&line, stdin)) { | |
999 | path = line.buf; | |
1000 | if (!null_terminated && line.buf[0] == '"') { | |
1001 | strbuf_reset(&unquoted); | |
1002 | if (unquote_c_style(&unquoted, line.buf, NULL)) | |
1003 | die(_("unable to unquote C-style string '%s'"), | |
1004 | line.buf); | |
1005 | ||
1006 | path = unquoted.buf; | |
1007 | } | |
1008 | ||
1009 | if (path_in_sparse_checkout(path, the_repository->index)) | |
1010 | write_name_quoted(path, stdout, line_terminator); | |
1011 | } | |
1012 | strbuf_release(&line); | |
1013 | strbuf_release(&unquoted); | |
1014 | ||
1015 | return 0; | |
1016 | } | |
1017 | ||
6f33d8e2 KN |
1018 | static int sparse_checkout_check_rules(int argc, const char **argv, const char *prefix, |
1019 | struct repository *repo UNUSED) | |
00408ade WS |
1020 | { |
1021 | static struct option builtin_sparse_checkout_check_rules_options[] = { | |
1022 | OPT_BOOL('z', NULL, &check_rules_opts.null_termination, | |
1023 | N_("terminate input and output files by a NUL character")), | |
1024 | OPT_BOOL(0, "cone", &check_rules_opts.cone_mode, | |
1025 | N_("when used with --rules-file interpret patterns as cone mode patterns")), | |
1026 | OPT_FILENAME(0, "rules-file", &check_rules_opts.rules_file, | |
1027 | N_("use patterns in <file> instead of the current ones.")), | |
1028 | OPT_END(), | |
1029 | }; | |
1030 | ||
1031 | FILE *fp; | |
1032 | int ret; | |
1033 | struct pattern_list pl = {0}; | |
1034 | char *sparse_filename; | |
1035 | check_rules_opts.cone_mode = -1; | |
1036 | ||
1037 | argc = parse_options(argc, argv, prefix, | |
1038 | builtin_sparse_checkout_check_rules_options, | |
f8ab66f9 | 1039 | builtin_sparse_checkout_check_rules_usage, 0); |
00408ade WS |
1040 | |
1041 | if (check_rules_opts.rules_file && check_rules_opts.cone_mode < 0) | |
1042 | check_rules_opts.cone_mode = 1; | |
1043 | ||
1044 | update_cone_mode(&check_rules_opts.cone_mode); | |
1045 | pl.use_cone_patterns = core_sparse_checkout_cone; | |
1046 | if (check_rules_opts.rules_file) { | |
1047 | fp = xfopen(check_rules_opts.rules_file, "r"); | |
1048 | add_patterns_from_input(&pl, argc, argv, fp); | |
1049 | fclose(fp); | |
1050 | } else { | |
1051 | sparse_filename = get_sparse_checkout_filename(); | |
1052 | if (add_patterns_from_file_to_list(sparse_filename, "", 0, &pl, | |
1053 | NULL, 0)) | |
1054 | die(_("unable to load existing sparse-checkout patterns")); | |
1055 | free(sparse_filename); | |
1056 | } | |
1057 | ||
1058 | ret = check_rules(&pl, check_rules_opts.null_termination); | |
1059 | clear_pattern_list(&pl); | |
14da2623 | 1060 | free(check_rules_opts.rules_file); |
00408ade WS |
1061 | return ret; |
1062 | } | |
1063 | ||
9b1cb507 JC |
1064 | int cmd_sparse_checkout(int argc, |
1065 | const char **argv, | |
1066 | const char *prefix, | |
6f33d8e2 | 1067 | struct repository *repo) |
94c0956b | 1068 | { |
1c3502b1 SG |
1069 | parse_opt_subcommand_fn *fn = NULL; |
1070 | struct option builtin_sparse_checkout_options[] = { | |
1071 | OPT_SUBCOMMAND("list", &fn, sparse_checkout_list), | |
1072 | OPT_SUBCOMMAND("init", &fn, sparse_checkout_init), | |
1073 | OPT_SUBCOMMAND("set", &fn, sparse_checkout_set), | |
1074 | OPT_SUBCOMMAND("add", &fn, sparse_checkout_add), | |
1075 | OPT_SUBCOMMAND("reapply", &fn, sparse_checkout_reapply), | |
1076 | OPT_SUBCOMMAND("disable", &fn, sparse_checkout_disable), | |
00408ade | 1077 | OPT_SUBCOMMAND("check-rules", &fn, sparse_checkout_check_rules), |
94c0956b DS |
1078 | OPT_END(), |
1079 | }; | |
1080 | ||
94c0956b DS |
1081 | argc = parse_options(argc, argv, prefix, |
1082 | builtin_sparse_checkout_options, | |
1c3502b1 | 1083 | builtin_sparse_checkout_usage, 0); |
94c0956b DS |
1084 | |
1085 | git_config(git_default_config, NULL); | |
1086 | ||
598b1e7d DS |
1087 | prepare_repo_settings(the_repository); |
1088 | the_repository->settings.command_requires_full_index = 0; | |
1089 | ||
6f33d8e2 | 1090 | return fn(argc, argv, prefix, repo); |
94c0956b | 1091 | } |