Coverage Report

Created: 2024-09-08 06:23

/src/git/builtin/checkout-index.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Check-out files from the "current cache directory"
3
 *
4
 * Copyright (C) 2005 Linus Torvalds
5
 *
6
 */
7
8
#include "builtin.h"
9
#include "config.h"
10
#include "gettext.h"
11
#include "lockfile.h"
12
#include "quote.h"
13
#include "repository.h"
14
#include "cache-tree.h"
15
#include "parse-options.h"
16
#include "entry.h"
17
#include "parallel-checkout.h"
18
#include "read-cache-ll.h"
19
#include "setup.h"
20
#include "sparse-index.h"
21
22
0
#define CHECKOUT_ALL 4
23
static int nul_term_line;
24
static int checkout_stage; /* default to checkout stage0 */
25
static int ignore_skip_worktree; /* default to 0 */
26
static int to_tempfile = -1;
27
static char topath[4][TEMPORARY_FILENAME_LENGTH + 1];
28
29
static struct checkout state = CHECKOUT_INIT;
30
31
static void write_tempfile_record(const char *name, const char *prefix)
32
0
{
33
0
  int i;
34
0
  int have_tempname = 0;
35
36
0
  if (CHECKOUT_ALL == checkout_stage) {
37
0
    for (i = 1; i < 4; i++)
38
0
      if (topath[i][0]) {
39
0
        have_tempname = 1;
40
0
        break;
41
0
      }
42
43
0
    if (have_tempname) {
44
0
      for (i = 1; i < 4; i++) {
45
0
        if (i > 1)
46
0
          putchar(' ');
47
0
        if (topath[i][0])
48
0
          fputs(topath[i], stdout);
49
0
        else
50
0
          putchar('.');
51
0
      }
52
0
    }
53
0
  } else if (topath[checkout_stage][0]) {
54
0
    have_tempname = 1;
55
0
    fputs(topath[checkout_stage], stdout);
56
0
  }
57
58
0
  if (have_tempname) {
59
0
    putchar('\t');
60
0
    write_name_quoted_relative(name, prefix, stdout,
61
0
             nul_term_line ? '\0' : '\n');
62
0
  }
63
64
0
  for (i = 0; i < 4; i++) {
65
0
    topath[i][0] = 0;
66
0
  }
67
0
}
68
69
static int checkout_file(const char *name, const char *prefix)
70
0
{
71
0
  int namelen = strlen(name);
72
0
  int pos = index_name_pos(the_repository->index, name, namelen);
73
0
  int has_same_name = 0;
74
0
  int is_file = 0;
75
0
  int is_skipped = 1;
76
0
  int did_checkout = 0;
77
0
  int errs = 0;
78
79
0
  if (pos < 0)
80
0
    pos = -pos - 1;
81
82
0
  while (pos <the_repository->index->cache_nr) {
83
0
    struct cache_entry *ce =the_repository->index->cache[pos];
84
0
    if (ce_namelen(ce) != namelen ||
85
0
        memcmp(ce->name, name, namelen))
86
0
      break;
87
0
    has_same_name = 1;
88
0
    pos++;
89
0
    if (S_ISSPARSEDIR(ce->ce_mode))
90
0
      break;
91
0
    is_file = 1;
92
0
    if (!ignore_skip_worktree && ce_skip_worktree(ce))
93
0
      break;
94
0
    is_skipped = 0;
95
0
    if (ce_stage(ce) != checkout_stage
96
0
        && (CHECKOUT_ALL != checkout_stage || !ce_stage(ce)))
97
0
      continue;
98
0
    did_checkout = 1;
99
0
    if (checkout_entry(ce, &state,
100
0
           to_tempfile ? topath[ce_stage(ce)] : NULL,
101
0
           NULL) < 0)
102
0
      errs++;
103
0
  }
104
105
0
  if (did_checkout) {
106
0
    if (to_tempfile)
107
0
      write_tempfile_record(name, prefix);
108
0
    return errs > 0 ? -1 : 0;
109
0
  }
110
111
  /*
112
   * At this point we know we didn't try to check anything out. If it was
113
   * because we did find an entry but it was stage 0, that's not an
114
   * error.
115
   */
116
0
  if (has_same_name && checkout_stage == CHECKOUT_ALL)
117
0
    return 0;
118
119
0
  if (!state.quiet) {
120
0
    fprintf(stderr, "git checkout-index: %s ", name);
121
0
    if (!has_same_name)
122
0
      fprintf(stderr, "is not in the cache");
123
0
    else if (!is_file)
124
0
      fprintf(stderr, "is a sparse directory");
125
0
    else if (is_skipped)
126
0
      fprintf(stderr, "has skip-worktree enabled; "
127
0
          "use '--ignore-skip-worktree-bits' to checkout");
128
0
    else if (checkout_stage)
129
0
      fprintf(stderr, "does not exist at stage %d",
130
0
        checkout_stage);
131
0
    else
132
0
      fprintf(stderr, "is unmerged");
133
0
    fputc('\n', stderr);
134
0
  }
135
0
  return -1;
136
0
}
137
138
static int checkout_all(const char *prefix, int prefix_length)
139
0
{
140
0
  int i, errs = 0;
141
0
  struct cache_entry *last_ce = NULL;
142
143
0
  for (i = 0; i < the_repository->index->cache_nr ; i++) {
144
0
    struct cache_entry *ce = the_repository->index->cache[i];
145
146
0
    if (S_ISSPARSEDIR(ce->ce_mode)) {
147
0
      if (!ce_skip_worktree(ce))
148
0
        BUG("sparse directory '%s' does not have skip-worktree set", ce->name);
149
150
      /*
151
       * If the current entry is a sparse directory and skip-worktree
152
       * entries are being checked out, expand the index and continue
153
       * the loop on the current index position (now pointing to the
154
       * first entry inside the expanded sparse directory).
155
       */
156
0
      if (ignore_skip_worktree) {
157
0
        ensure_full_index(the_repository->index);
158
0
        ce = the_repository->index->cache[i];
159
0
      }
160
0
    }
161
162
0
    if (!ignore_skip_worktree && ce_skip_worktree(ce))
163
0
      continue;
164
0
    if (ce_stage(ce) != checkout_stage
165
0
        && (CHECKOUT_ALL != checkout_stage || !ce_stage(ce)))
166
0
      continue;
167
0
    if (prefix && *prefix &&
168
0
        (ce_namelen(ce) <= prefix_length ||
169
0
         memcmp(prefix, ce->name, prefix_length)))
170
0
      continue;
171
0
    if (last_ce && to_tempfile) {
172
0
      if (ce_namelen(last_ce) != ce_namelen(ce)
173
0
          || memcmp(last_ce->name, ce->name, ce_namelen(ce)))
174
0
        write_tempfile_record(last_ce->name, prefix);
175
0
    }
176
0
    if (checkout_entry(ce, &state,
177
0
           to_tempfile ? topath[ce_stage(ce)] : NULL,
178
0
           NULL) < 0)
179
0
      errs++;
180
0
    last_ce = ce;
181
0
  }
182
0
  if (last_ce && to_tempfile)
183
0
    write_tempfile_record(last_ce->name, prefix);
184
0
  return !!errs;
185
0
}
186
187
static const char * const builtin_checkout_index_usage[] = {
188
  N_("git checkout-index [<options>] [--] [<file>...]"),
189
  NULL
190
};
191
192
static int option_parse_stage(const struct option *opt,
193
            const char *arg, int unset)
194
0
{
195
0
  int *stage = opt->value;
196
197
0
  BUG_ON_OPT_NEG(unset);
198
199
0
  if (!strcmp(arg, "all")) {
200
0
    *stage = CHECKOUT_ALL;
201
0
  } else {
202
0
    int ch = arg[0];
203
0
    if ('1' <= ch && ch <= '3')
204
0
      *stage = arg[0] - '0';
205
0
    else
206
0
      die(_("stage should be between 1 and 3 or all"));
207
0
  }
208
0
  return 0;
209
0
}
210
211
int cmd_checkout_index(int argc, const char **argv, const char *prefix)
212
0
{
213
0
  int i;
214
0
  struct lock_file lock_file = LOCK_INIT;
215
0
  int all = 0;
216
0
  int read_from_stdin = 0;
217
0
  int prefix_length;
218
0
  int force = 0, quiet = 0, not_new = 0;
219
0
  int index_opt = 0;
220
0
  int err = 0;
221
0
  int pc_workers, pc_threshold;
222
0
  struct option builtin_checkout_index_options[] = {
223
0
    OPT_BOOL('a', "all", &all,
224
0
      N_("check out all files in the index")),
225
0
    OPT_BOOL(0, "ignore-skip-worktree-bits", &ignore_skip_worktree,
226
0
      N_("do not skip files with skip-worktree set")),
227
0
    OPT__FORCE(&force, N_("force overwrite of existing files"), 0),
228
0
    OPT__QUIET(&quiet,
229
0
      N_("no warning for existing files and files not in index")),
230
0
    OPT_BOOL('n', "no-create", &not_new,
231
0
      N_("don't checkout new files")),
232
0
    OPT_BOOL('u', "index", &index_opt,
233
0
       N_("update stat information in the index file")),
234
0
    OPT_BOOL('z', NULL, &nul_term_line,
235
0
      N_("paths are separated with NUL character")),
236
0
    OPT_BOOL(0, "stdin", &read_from_stdin,
237
0
      N_("read list of paths from the standard input")),
238
0
    OPT_BOOL(0, "temp", &to_tempfile,
239
0
      N_("write the content to temporary files")),
240
0
    OPT_STRING(0, "prefix", &state.base_dir, N_("string"),
241
0
      N_("when creating files, prepend <string>")),
242
0
    OPT_CALLBACK_F(0, "stage", &checkout_stage, "(1|2|3|all)",
243
0
      N_("copy out the files from named stage"),
244
0
      PARSE_OPT_NONEG, option_parse_stage),
245
0
    OPT_END()
246
0
  };
247
248
0
  if (argc == 2 && !strcmp(argv[1], "-h"))
249
0
    usage_with_options(builtin_checkout_index_usage,
250
0
           builtin_checkout_index_options);
251
0
  git_config(git_default_config, NULL);
252
0
  prefix_length = prefix ? strlen(prefix) : 0;
253
254
0
  prepare_repo_settings(the_repository);
255
0
  the_repository->settings.command_requires_full_index = 0;
256
257
0
  if (repo_read_index(the_repository) < 0) {
258
0
    die("invalid cache");
259
0
  }
260
261
0
  argc = parse_options(argc, argv, prefix, builtin_checkout_index_options,
262
0
      builtin_checkout_index_usage, 0);
263
0
  state.istate = the_repository->index;
264
0
  state.force = force;
265
0
  state.quiet = quiet;
266
0
  state.not_new = not_new;
267
268
0
  if (!state.base_dir)
269
0
    state.base_dir = "";
270
0
  state.base_dir_len = strlen(state.base_dir);
271
272
0
  if (to_tempfile < 0)
273
0
    to_tempfile = (checkout_stage == CHECKOUT_ALL);
274
0
  if (!to_tempfile && checkout_stage == CHECKOUT_ALL)
275
0
    die(_("options '%s' and '%s' cannot be used together"),
276
0
        "--stage=all", "--no-temp");
277
278
  /*
279
   * when --prefix is specified we do not want to update cache.
280
   */
281
0
  if (index_opt && !state.base_dir_len && !to_tempfile) {
282
0
    state.refresh_cache = 1;
283
0
    state.istate = the_repository->index;
284
0
    repo_hold_locked_index(the_repository, &lock_file,
285
0
               LOCK_DIE_ON_ERROR);
286
0
  }
287
288
0
  get_parallel_checkout_configs(&pc_workers, &pc_threshold);
289
0
  if (pc_workers > 1)
290
0
    init_parallel_checkout();
291
292
  /* Check out named files first */
293
0
  for (i = 0; i < argc; i++) {
294
0
    const char *arg = argv[i];
295
0
    char *p;
296
297
0
    if (all)
298
0
      die("git checkout-index: don't mix '--all' and explicit filenames");
299
0
    if (read_from_stdin)
300
0
      die("git checkout-index: don't mix '--stdin' and explicit filenames");
301
0
    p = prefix_path(prefix, prefix_length, arg);
302
0
    err |= checkout_file(p, prefix);
303
0
    free(p);
304
0
  }
305
306
0
  if (read_from_stdin) {
307
0
    struct strbuf buf = STRBUF_INIT;
308
0
    struct strbuf unquoted = STRBUF_INIT;
309
0
    strbuf_getline_fn getline_fn;
310
311
0
    if (all)
312
0
      die("git checkout-index: don't mix '--all' and '--stdin'");
313
314
0
    getline_fn = nul_term_line ? strbuf_getline_nul : strbuf_getline_lf;
315
0
    while (getline_fn(&buf, stdin) != EOF) {
316
0
      char *p;
317
0
      if (!nul_term_line && buf.buf[0] == '"') {
318
0
        strbuf_reset(&unquoted);
319
0
        if (unquote_c_style(&unquoted, buf.buf, NULL))
320
0
          die("line is badly quoted");
321
0
        strbuf_swap(&buf, &unquoted);
322
0
      }
323
0
      p = prefix_path(prefix, prefix_length, buf.buf);
324
0
      err |= checkout_file(p, prefix);
325
0
      free(p);
326
0
    }
327
0
    strbuf_release(&unquoted);
328
0
    strbuf_release(&buf);
329
0
  }
330
331
0
  if (all)
332
0
    err |= checkout_all(prefix, prefix_length);
333
334
0
  if (pc_workers > 1)
335
0
    err |= run_parallel_checkout(&state, pc_workers, pc_threshold,
336
0
               NULL, NULL);
337
338
0
  if (err)
339
0
    return 1;
340
341
0
  if (is_lock_file_locked(&lock_file) &&
342
0
      write_locked_index(the_repository->index, &lock_file, COMMIT_LOCK))
343
0
    die("Unable to write new index file");
344
0
  return 0;
345
0
}