Coverage Report

Created: 2023-11-19 07:08

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