/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", ¬_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 | } |