Coverage Report

Created: 2024-09-08 06:23

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