]> git.proxmox.com Git - libgit2.git/blame - src/checkout.c
Merge pull request #1204 from arrbee/diff-blob-to-buffer
[libgit2.git] / src / checkout.c
CommitLineData
14741d62
BS
1/*
2 * Copyright (C) 2009-2012 the libgit2 contributors
3 *
4 * This file is part of libgit2, distributed under the GNU GPL v2 with
5 * a Linking Exception. For full terms see the included COPYING file.
6 */
7
8#include <assert.h>
9
cf208031
RB
10#include "checkout.h"
11
14741d62
BS
12#include "git2/repository.h"
13#include "git2/refs.h"
14#include "git2/tree.h"
24b0d3d5 15#include "git2/blob.h"
8651c10f 16#include "git2/config.h"
3aa443a9 17#include "git2/diff.h"
e0548c0e 18#include "git2/submodule.h"
14741d62 19
14741d62 20#include "refs.h"
24b0d3d5 21#include "repository.h"
0e874b12 22#include "filter.h"
41ad70d0 23#include "blob.h"
32def5af 24#include "diff.h"
ad9a921b 25#include "pathspec.h"
14741d62 26
77cffa31 27/* See docs/checkout-internals.md for more information */
cf208031
RB
28
29enum {
30 CHECKOUT_ACTION__NONE = 0,
31 CHECKOUT_ACTION__REMOVE = 1,
32 CHECKOUT_ACTION__UPDATE_BLOB = 2,
33 CHECKOUT_ACTION__UPDATE_SUBMODULE = 4,
34 CHECKOUT_ACTION__CONFLICT = 8,
35 CHECKOUT_ACTION__MAX = 8,
7e5c8a5b
RB
36 CHECKOUT_ACTION__DEFER_REMOVE = 16,
37 CHECKOUT_ACTION__REMOVE_AND_UPDATE =
38 (CHECKOUT_ACTION__UPDATE_BLOB | CHECKOUT_ACTION__REMOVE),
cf208031
RB
39};
40
ad9a921b
RB
41typedef struct {
42 git_repository *repo;
43 git_diff_list *diff;
7e5c8a5b
RB
44 git_checkout_opts opts;
45 bool opts_free_baseline;
46 char *pfx;
5cf9875a 47 git_index *index;
7e5c8a5b
RB
48 git_pool pool;
49 git_vector removes;
50 git_buf path;
9ac8b113 51 size_t workdir_len;
7e5c8a5b
RB
52 unsigned int strategy;
53 int can_symlink;
e0548c0e 54 bool reload_submodules;
9c05c17b
BS
55 size_t total_steps;
56 size_t completed_steps;
7e5c8a5b 57} checkout_data;
3aa443a9 58
cf208031 59static int checkout_notify(
7e5c8a5b 60 checkout_data *data,
cf208031
RB
61 git_checkout_notify_t why,
62 const git_diff_delta *delta,
16a666d3 63 const git_index_entry *wditem)
cf208031 64{
16a666d3 65 git_diff_file wdfile;
7e5c8a5b
RB
66 const git_diff_file *baseline = NULL, *target = NULL, *workdir = NULL;
67
68 if (!data->opts.notify_cb)
69 return 0;
70
71 if ((why & data->opts.notify_flags) == 0)
72 return 0;
73
16a666d3
RB
74 if (wditem) {
75 memset(&wdfile, 0, sizeof(wdfile));
7e5c8a5b 76
16a666d3
RB
77 git_oid_cpy(&wdfile.oid, &wditem->oid);
78 wdfile.path = wditem->path;
79 wdfile.size = wditem->file_size;
80 wdfile.flags = GIT_DIFF_FILE_VALID_OID;
81 wdfile.mode = wditem->mode;
7e5c8a5b 82
16a666d3 83 workdir = &wdfile;
7e5c8a5b
RB
84 }
85
16a666d3 86 if (delta) {
7e5c8a5b
RB
87 switch (delta->status) {
88 case GIT_DELTA_UNMODIFIED:
89 case GIT_DELTA_MODIFIED:
90 case GIT_DELTA_TYPECHANGE:
91 default:
16a666d3
RB
92 baseline = &delta->old_file;
93 target = &delta->new_file;
7e5c8a5b
RB
94 break;
95 case GIT_DELTA_ADDED:
96 case GIT_DELTA_IGNORED:
97 case GIT_DELTA_UNTRACKED:
16a666d3 98 target = &delta->new_file;
7e5c8a5b
RB
99 break;
100 case GIT_DELTA_DELETED:
16a666d3 101 baseline = &delta->old_file;
7e5c8a5b
RB
102 break;
103 }
104 }
105
106 return data->opts.notify_cb(
16a666d3
RB
107 why, delta ? delta->old_file.path : wditem->path,
108 baseline, target, workdir, data->opts.notify_payload);
cf208031
RB
109}
110
111static bool checkout_is_workdir_modified(
7e5c8a5b 112 checkout_data *data,
5cf9875a
RB
113 const git_diff_file *baseitem,
114 const git_index_entry *wditem)
cf208031
RB
115{
116 git_oid oid;
117
e0548c0e
RB
118 /* handle "modified" submodule */
119 if (wditem->mode == GIT_FILEMODE_COMMIT) {
120 git_submodule *sm;
121 unsigned int sm_status = 0;
122 const git_oid *sm_oid = NULL;
123
124 if (git_submodule_lookup(&sm, data->repo, wditem->path) < 0 ||
125 git_submodule_status(&sm_status, sm) < 0)
126 return true;
127
128 if (GIT_SUBMODULE_STATUS_IS_WD_DIRTY(sm_status))
129 return true;
130
131 sm_oid = git_submodule_wd_id(sm);
132 if (!sm_oid)
133 return false;
134
135 return (git_oid_cmp(&baseitem->oid, sm_oid) != 0);
136 }
137
5cf9875a
RB
138 /* depending on where base is coming from, we may or may not know
139 * the actual size of the data, so we can't rely on this shortcut.
140 */
141 if (baseitem->size && wditem->file_size != baseitem->size)
cf208031
RB
142 return true;
143
144 if (git_diff__oid_for_file(
5cf9875a
RB
145 data->repo, wditem->path, wditem->mode,
146 wditem->file_size, &oid) < 0)
cf208031
RB
147 return false;
148
7e5c8a5b
RB
149 return (git_oid_cmp(&baseitem->oid, &oid) != 0);
150}
151
152#define CHECKOUT_ACTION_IF(FLAG,YES,NO) \
153 ((data->strategy & GIT_CHECKOUT_##FLAG) ? CHECKOUT_ACTION__##YES : CHECKOUT_ACTION__##NO)
154
7e5c8a5b
RB
155static int checkout_action_common(
156 checkout_data *data,
157 int action,
cf208031 158 const git_diff_delta *delta,
7e5c8a5b
RB
159 const git_index_entry *wd)
160{
161 git_checkout_notify_t notify = GIT_CHECKOUT_NOTIFY_NONE;
162
163 if (action <= 0)
164 return action;
165
166 if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0)
167 action = (action & ~CHECKOUT_ACTION__REMOVE);
168
169 if ((action & CHECKOUT_ACTION__UPDATE_BLOB) != 0) {
170 if (S_ISGITLINK(delta->new_file.mode))
171 action = (action & ~CHECKOUT_ACTION__UPDATE_BLOB) |
172 CHECKOUT_ACTION__UPDATE_SUBMODULE;
173
174 notify = GIT_CHECKOUT_NOTIFY_UPDATED;
175 }
176
177 if ((action & CHECKOUT_ACTION__CONFLICT) != 0)
178 notify = GIT_CHECKOUT_NOTIFY_CONFLICT;
179
180 if (notify != GIT_CHECKOUT_NOTIFY_NONE &&
181 checkout_notify(data, notify, delta, wd) != 0)
182 return GIT_EUSER;
183
184 return action;
185}
186
187static int checkout_action_no_wd(
188 checkout_data *data,
189 const git_diff_delta *delta)
cf208031
RB
190{
191 int action = CHECKOUT_ACTION__NONE;
cf208031 192
7e5c8a5b
RB
193 switch (delta->status) {
194 case GIT_DELTA_UNMODIFIED: /* case 12 */
195 if (checkout_notify(data, GIT_CHECKOUT_NOTIFY_DIRTY, delta, NULL))
196 return GIT_EUSER;
197 action = CHECKOUT_ACTION_IF(SAFE_CREATE, UPDATE_BLOB, NONE);
198 break;
199 case GIT_DELTA_ADDED: /* case 2 or 28 (and 5 but not really) */
200 case GIT_DELTA_MODIFIED: /* case 13 (and 35 but not really) */
201 action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE);
202 break;
203 case GIT_DELTA_TYPECHANGE: /* case 21 (B->T) and 28 (T->B)*/
204 if (delta->new_file.mode == GIT_FILEMODE_TREE)
205 action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE);
206 break;
207 case GIT_DELTA_DELETED: /* case 8 or 25 */
208 default: /* impossible */
209 break;
cf208031
RB
210 }
211
7e5c8a5b
RB
212 return checkout_action_common(data, action, delta, NULL);
213}
214
215static int checkout_action_wd_only(
216 checkout_data *data,
217 git_iterator *workdir,
218 const git_index_entry *wd,
219 git_vector *pathspec)
220{
5cf9875a 221 bool remove = false;
7e5c8a5b
RB
222 git_checkout_notify_t notify = GIT_CHECKOUT_NOTIFY_NONE;
223
224 if (!git_pathspec_match_path(
225 pathspec, wd->path, false, workdir->ignore_case))
226 return 0;
227
5cf9875a 228 /* check if item is tracked in the index but not in the checkout diff */
817d6251
RB
229 if (data->index != NULL) {
230 if (wd->mode != GIT_FILEMODE_TREE) {
231 if (git_index_get_bypath(data->index, wd->path, 0) != NULL) {
232 notify = GIT_CHECKOUT_NOTIFY_DIRTY;
233 remove = ((data->strategy & GIT_CHECKOUT_FORCE) != 0);
234 }
235 } else {
236 /* for tree entries, we have to see if there are any index
237 * entries that are contained inside that tree
238 */
239 size_t pos = git_index__prefix_position(data->index, wd->path);
240 const git_index_entry *e = git_index_get_byindex(data->index, pos);
241
242 if (e != NULL && data->diff->pfxcomp(e->path, wd->path) == 0) {
243 notify = GIT_CHECKOUT_NOTIFY_DIRTY;
244 remove = ((data->strategy & GIT_CHECKOUT_FORCE) != 0);
245 }
246 }
5cf9875a 247 }
817d6251
RB
248
249 if (notify != GIT_CHECKOUT_NOTIFY_NONE)
250 /* found in index */;
5cf9875a 251 else if (git_iterator_current_is_ignored(workdir)) {
7e5c8a5b
RB
252 notify = GIT_CHECKOUT_NOTIFY_IGNORED;
253 remove = ((data->strategy & GIT_CHECKOUT_REMOVE_IGNORED) != 0);
5cf9875a
RB
254 }
255 else {
7e5c8a5b
RB
256 notify = GIT_CHECKOUT_NOTIFY_UNTRACKED;
257 remove = ((data->strategy & GIT_CHECKOUT_REMOVE_UNTRACKED) != 0);
cf208031
RB
258 }
259
7e5c8a5b
RB
260 if (checkout_notify(data, notify, NULL, wd))
261 return GIT_EUSER;
cf208031 262
7e5c8a5b
RB
263 if (remove) {
264 char *path = git_pool_strdup(&data->pool, wd->path);
265 GITERR_CHECK_ALLOC(path);
266
267 if (git_vector_insert(&data->removes, path) < 0)
268 return -1;
269 }
270
271 return 0;
272}
273
e0548c0e
RB
274static bool submodule_is_config_only(
275 checkout_data *data,
276 const char *path)
277{
278 git_submodule *sm = NULL;
279 unsigned int sm_loc = 0;
280
281 if (git_submodule_lookup(&sm, data->repo, path) < 0 ||
282 git_submodule_location(&sm_loc, sm) < 0 ||
283 sm_loc == GIT_SUBMODULE_STATUS_IN_CONFIG)
284 return true;
285
286 return false;
287}
288
7e5c8a5b
RB
289static int checkout_action_with_wd(
290 checkout_data *data,
291 const git_diff_delta *delta,
292 const git_index_entry *wd)
293{
294 int action = CHECKOUT_ACTION__NONE;
295
296 switch (delta->status) {
297 case GIT_DELTA_UNMODIFIED: /* case 14/15 or 33 */
e0548c0e 298 if (checkout_is_workdir_modified(data, &delta->old_file, wd)) {
7e5c8a5b
RB
299 if (checkout_notify(
300 data, GIT_CHECKOUT_NOTIFY_DIRTY, delta, wd))
301 return GIT_EUSER;
302 action = CHECKOUT_ACTION_IF(FORCE, UPDATE_BLOB, NONE);
cf208031 303 }
7e5c8a5b
RB
304 break;
305 case GIT_DELTA_ADDED: /* case 3, 4 or 6 */
306 action = CHECKOUT_ACTION_IF(FORCE, UPDATE_BLOB, CONFLICT);
307 break;
308 case GIT_DELTA_DELETED: /* case 9 or 10 (or 26 but not really) */
309 if (checkout_is_workdir_modified(data, &delta->old_file, wd))
310 action = CHECKOUT_ACTION_IF(FORCE, REMOVE, CONFLICT);
311 else
312 action = CHECKOUT_ACTION_IF(SAFE, REMOVE, NONE);
313 break;
314 case GIT_DELTA_MODIFIED: /* case 16, 17, 18 (or 36 but not really) */
315 if (checkout_is_workdir_modified(data, &delta->old_file, wd))
316 action = CHECKOUT_ACTION_IF(FORCE, UPDATE_BLOB, CONFLICT);
317 else
318 action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE);
319 break;
320 case GIT_DELTA_TYPECHANGE: /* case 22, 23, 29, 30 */
e0548c0e
RB
321 if (delta->old_file.mode == GIT_FILEMODE_TREE) {
322 if (wd->mode == GIT_FILEMODE_TREE)
323 /* either deleting items in old tree will delete the wd dir,
324 * or we'll get a conflict when we attempt blob update...
325 */
326 action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE);
327 else if (wd->mode == GIT_FILEMODE_COMMIT) {
328 /* workdir is possibly a "phantom" submodule - treat as a
329 * tree if the only submodule info came from the config
330 */
331 if (submodule_is_config_only(data, wd->path))
332 action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE);
333 else
334 action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT);
335 } else
336 action = CHECKOUT_ACTION_IF(FORCE, REMOVE, CONFLICT);
337 }
7e5c8a5b
RB
338 else if (checkout_is_workdir_modified(data, &delta->old_file, wd))
339 action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT);
340 else
341 action = CHECKOUT_ACTION_IF(SAFE, REMOVE_AND_UPDATE, NONE);
e0548c0e
RB
342
343 /* don't update if the typechange is to a tree */
344 if (delta->new_file.mode == GIT_FILEMODE_TREE)
345 action = (action & ~CHECKOUT_ACTION__UPDATE_BLOB);
7e5c8a5b
RB
346 break;
347 default: /* impossible */
348 break;
cf208031
RB
349 }
350
7e5c8a5b
RB
351 return checkout_action_common(data, action, delta, wd);
352}
cf208031 353
7e5c8a5b
RB
354static int checkout_action_with_wd_blocker(
355 checkout_data *data,
356 const git_diff_delta *delta,
357 const git_index_entry *wd)
358{
359 int action = CHECKOUT_ACTION__NONE;
cf208031 360
7e5c8a5b
RB
361 switch (delta->status) {
362 case GIT_DELTA_UNMODIFIED:
363 /* should show delta as dirty / deleted */
364 if (checkout_notify(data, GIT_CHECKOUT_NOTIFY_DIRTY, delta, wd))
cf208031 365 return GIT_EUSER;
7e5c8a5b
RB
366 action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, NONE);
367 break;
368 case GIT_DELTA_ADDED:
369 case GIT_DELTA_MODIFIED:
370 action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT);
371 break;
372 case GIT_DELTA_DELETED:
373 action = CHECKOUT_ACTION_IF(FORCE, REMOVE, CONFLICT);
374 break;
375 case GIT_DELTA_TYPECHANGE:
376 /* not 100% certain about this... */
377 action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT);
378 break;
379 default: /* impossible */
380 break;
cf208031
RB
381 }
382
7e5c8a5b
RB
383 return checkout_action_common(data, action, delta, wd);
384}
385
386static int checkout_action_with_wd_dir(
387 checkout_data *data,
388 const git_diff_delta *delta,
389 const git_index_entry *wd)
390{
391 int action = CHECKOUT_ACTION__NONE;
392
393 switch (delta->status) {
394 case GIT_DELTA_UNMODIFIED: /* case 19 or 24 (or 34 but not really) */
395 if (checkout_notify(data, GIT_CHECKOUT_NOTIFY_DIRTY, delta, NULL) ||
396 checkout_notify(
397 data, GIT_CHECKOUT_NOTIFY_UNTRACKED, NULL, wd))
cf208031 398 return GIT_EUSER;
7e5c8a5b
RB
399 break;
400 case GIT_DELTA_ADDED:/* case 4 (and 7 for dir) */
401 case GIT_DELTA_MODIFIED: /* case 20 (or 37 but not really) */
e0548c0e
RB
402 if (delta->old_file.mode == GIT_FILEMODE_COMMIT)
403 /* expected submodule (and maybe found one) */;
404 else if (delta->new_file.mode != GIT_FILEMODE_TREE)
7e5c8a5b
RB
405 action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT);
406 break;
407 case GIT_DELTA_DELETED: /* case 11 (and 27 for dir) */
408 if (delta->old_file.mode != GIT_FILEMODE_TREE &&
409 checkout_notify(
410 data, GIT_CHECKOUT_NOTIFY_UNTRACKED, NULL, wd))
411 return GIT_EUSER;
412 break;
413 case GIT_DELTA_TYPECHANGE: /* case 24 or 31 */
7e5c8a5b 414 if (delta->old_file.mode == GIT_FILEMODE_TREE) {
e0548c0e
RB
415 /* For typechange from dir, remove dir and add blob, but it is
416 * not safe to remove dir if it contains modified files.
417 * However, safely removing child files will remove the parent
418 * directory if is it left empty, so we can defer removing the
419 * dir and it will succeed if no children are left.
420 */
7e5c8a5b
RB
421 action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE);
422 if (action != CHECKOUT_ACTION__NONE)
423 action |= CHECKOUT_ACTION__DEFER_REMOVE;
424 }
e0548c0e
RB
425 else if (delta->new_file.mode != GIT_FILEMODE_TREE)
426 /* For typechange to dir, dir is already created so no action */
427 action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT);
7e5c8a5b
RB
428 break;
429 default: /* impossible */
430 break;
cf208031
RB
431 }
432
7e5c8a5b 433 return checkout_action_common(data, action, delta, wd);
cf208031
RB
434}
435
7e5c8a5b
RB
436static int checkout_action(
437 checkout_data *data,
cf208031 438 git_diff_delta *delta,
7e5c8a5b
RB
439 git_iterator *workdir,
440 const git_index_entry **wditem_ptr,
cf208031
RB
441 git_vector *pathspec)
442{
7e5c8a5b
RB
443 const git_index_entry *wd = *wditem_ptr;
444 int cmp = -1, act;
445 int (*strcomp)(const char *, const char *) = data->diff->strcomp;
446 int (*pfxcomp)(const char *str, const char *pfx) = data->diff->pfxcomp;
447
448 /* move workdir iterator to follow along with deltas */
449
450 while (1) {
451 if (!wd)
452 return checkout_action_no_wd(data, delta);
453
454 cmp = strcomp(wd->path, delta->old_file.path);
455
456 /* 1. wd before delta ("a/a" before "a/b")
457 * 2. wd prefixes delta & should expand ("a/" before "a/b")
458 * 3. wd prefixes delta & cannot expand ("a/b" before "a/b/c")
459 * 4. wd equals delta ("a/b" and "a/b")
460 * 5. wd after delta & delta prefixes wd ("a/b/c" after "a/b/" or "a/b")
461 * 6. wd after delta ("a/c" after "a/b")
462 */
463
464 if (cmp < 0) {
465 cmp = pfxcomp(delta->old_file.path, wd->path);
466
d8889d2b 467 if (cmp == 0) {
817d6251
RB
468 if (wd->mode == GIT_FILEMODE_TREE) {
469 /* case 2 - entry prefixed by workdir tree */
470 if (git_iterator_advance_into_directory(workdir, &wd) < 0)
471 goto fail;
472 continue;
473 }
474
475 /* case 3 maybe - wd contains non-dir where dir expected */
476 if (delta->old_file.path[strlen(wd->path)] == '/') {
477 act = checkout_action_with_wd_blocker(data, delta, wd);
478 *wditem_ptr =
479 git_iterator_advance(workdir, &wd) ? NULL : wd;
480 return act;
481 }
7e5c8a5b 482 }
cf208031 483
7e5c8a5b
RB
484 /* case 1 - handle wd item (if it matches pathspec) */
485 if (checkout_action_wd_only(data, workdir, wd, pathspec) < 0 ||
486 git_iterator_advance(workdir, &wd) < 0)
487 goto fail;
cf208031 488
7e5c8a5b
RB
489 *wditem_ptr = wd;
490 continue;
491 }
cf208031 492
7e5c8a5b
RB
493 if (cmp == 0) {
494 /* case 4 */
495 act = checkout_action_with_wd(data, delta, wd);
496 *wditem_ptr = git_iterator_advance(workdir, &wd) ? NULL : wd;
497 return act;
498 }
cf208031 499
7e5c8a5b 500 cmp = pfxcomp(wd->path, delta->old_file.path);
cf208031 501
7e5c8a5b 502 if (cmp == 0) { /* case 5 */
817d6251 503 if (wd->path[strlen(delta->old_file.path)] != '/')
e0548c0e
RB
504 return checkout_action_no_wd(data, delta);
505
506 if (delta->status == GIT_DELTA_TYPECHANGE) {
507 if (delta->old_file.mode == GIT_FILEMODE_TREE) {
508 act = checkout_action_with_wd(data, delta, wd);
509 if (git_iterator_advance_into_directory(workdir, &wd) < 0)
510 wd = NULL;
511 *wditem_ptr = wd;
512 return act;
513 }
514
515 if (delta->new_file.mode == GIT_FILEMODE_TREE ||
516 delta->new_file.mode == GIT_FILEMODE_COMMIT ||
517 delta->old_file.mode == GIT_FILEMODE_COMMIT)
518 {
519 act = checkout_action_with_wd(data, delta, wd);
520 if (git_iterator_advance(workdir, &wd) < 0)
521 wd = NULL;
522 *wditem_ptr = wd;
523 return act;
524 }
cf208031 525 }
cf208031 526
7e5c8a5b
RB
527 return checkout_action_with_wd_dir(data, delta, wd);
528 }
cf208031 529
7e5c8a5b
RB
530 /* case 6 - wd is after delta */
531 return checkout_action_no_wd(data, delta);
cf208031
RB
532 }
533
7e5c8a5b
RB
534fail:
535 *wditem_ptr = NULL;
536 return -1;
cf208031
RB
537}
538
d8889d2b
RB
539static int checkout_remaining_wd_items(
540 checkout_data *data,
541 git_iterator *workdir,
542 const git_index_entry *wd,
543 git_vector *spec)
544{
545 int error = 0;
d8889d2b
RB
546
547 while (wd && !error) {
817d6251 548 if (!(error = checkout_action_wd_only(data, workdir, wd, spec)))
d8889d2b
RB
549 error = git_iterator_advance(workdir, &wd);
550 }
551
552 return error;
553}
554
cf208031
RB
555static int checkout_get_actions(
556 uint32_t **actions_ptr,
557 size_t **counts_ptr,
7e5c8a5b
RB
558 checkout_data *data,
559 git_iterator *workdir)
cf208031
RB
560{
561 int error = 0;
cf208031
RB
562 const git_index_entry *wditem;
563 git_vector pathspec = GIT_VECTOR_INIT, *deltas;
564 git_pool pathpool = GIT_POOL_INIT_STRINGPOOL;
565 git_diff_delta *delta;
566 size_t i, *counts = NULL;
567 uint32_t *actions = NULL;
cf208031 568
7e5c8a5b
RB
569 if (data->opts.paths.count > 0 &&
570 git_pathspec_init(&pathspec, &data->opts.paths, &pathpool) < 0)
cf208031
RB
571 return -1;
572
7e5c8a5b 573 if ((error = git_iterator_current(workdir, &wditem)) < 0)
cf208031
RB
574 goto fail;
575
576 deltas = &data->diff->deltas;
577
578 *counts_ptr = counts = git__calloc(CHECKOUT_ACTION__MAX+1, sizeof(size_t));
579 *actions_ptr = actions = git__calloc(
580 deltas->length ? deltas->length : 1, sizeof(uint32_t));
581 if (!counts || !actions) {
582 error = -1;
583 goto fail;
584 }
585
586 git_vector_foreach(deltas, i, delta) {
7e5c8a5b 587 int act = checkout_action(data, delta, workdir, &wditem, &pathspec);
cf208031 588
cf208031
RB
589 if (act < 0) {
590 error = act;
591 goto fail;
592 }
593
cf208031
RB
594 actions[i] = act;
595
596 if (act & CHECKOUT_ACTION__REMOVE)
597 counts[CHECKOUT_ACTION__REMOVE]++;
598 if (act & CHECKOUT_ACTION__UPDATE_BLOB)
599 counts[CHECKOUT_ACTION__UPDATE_BLOB]++;
600 if (act & CHECKOUT_ACTION__UPDATE_SUBMODULE)
601 counts[CHECKOUT_ACTION__UPDATE_SUBMODULE]++;
602 if (act & CHECKOUT_ACTION__CONFLICT)
603 counts[CHECKOUT_ACTION__CONFLICT]++;
604 }
605
d8889d2b
RB
606 error = checkout_remaining_wd_items(data, workdir, wditem, &pathspec);
607 if (error < 0)
608 goto fail;
16a666d3 609
7e5c8a5b
RB
610 counts[CHECKOUT_ACTION__REMOVE] += data->removes.length;
611
612 if (counts[CHECKOUT_ACTION__CONFLICT] > 0 &&
613 (data->strategy & GIT_CHECKOUT_ALLOW_CONFLICTS) == 0)
614 {
cf208031
RB
615 giterr_set(GITERR_CHECKOUT, "%d conflicts prevent checkout",
616 (int)counts[CHECKOUT_ACTION__CONFLICT]);
617 error = -1;
618 goto fail;
619 }
620
cf208031
RB
621 git_pathspec_free(&pathspec);
622 git_pool_clear(&pathpool);
623
624 return 0;
625
626fail:
627 *counts_ptr = NULL;
628 git__free(counts);
629 *actions_ptr = NULL;
630 git__free(actions);
631
cf208031
RB
632 git_pathspec_free(&pathspec);
633 git_pool_clear(&pathpool);
634
635 return error;
636}
637
3aa443a9 638static int buffer_to_file(
5cf9875a 639 struct stat *st,
3aa443a9 640 git_buf *buffer,
641 const char *path,
28abf3db 642 mode_t dir_mode,
3aa443a9 643 int file_open_flags,
644 mode_t file_mode)
645{
32def5af 646 int fd, error;
ec532d5e 647
0d64bef9
RB
648 if ((error = git_futils_mkpath2file(path, dir_mode)) < 0)
649 return error;
ec532d5e 650
32def5af
RB
651 if ((fd = p_open(path, file_open_flags, file_mode)) < 0) {
652 giterr_set(GITERR_OS, "Could not open '%s' for writing", path);
0d64bef9 653 return fd;
32def5af 654 }
0d64bef9 655
32def5af
RB
656 if ((error = p_write(fd, git_buf_cstr(buffer), git_buf_len(buffer))) < 0) {
657 giterr_set(GITERR_OS, "Could not write to '%s'", path);
658 (void)p_close(fd);
659 } else {
5cf9875a
RB
660 if ((error = p_fstat(fd, st)) < 0)
661 giterr_set(GITERR_OS, "Error while statting '%s'", path);
662
32def5af
RB
663 if ((error = p_close(fd)) < 0)
664 giterr_set(GITERR_OS, "Error while closing '%s'", path);
665 }
0d64bef9
RB
666
667 if (!error &&
668 (file_mode & 0100) != 0 &&
669 (error = p_chmod(path, file_mode)) < 0)
670 giterr_set(GITERR_OS, "Failed to set permissions on '%s'", path);
671
672 return error;
1d68fcd0
BS
673}
674
3aa443a9 675static int blob_content_to_file(
5cf9875a 676 struct stat *st,
3aa443a9 677 git_blob *blob,
678 const char *path,
28abf3db 679 mode_t entry_filemode,
3aa443a9 680 git_checkout_opts *opts)
dc1b0909 681{
d75074f4 682 int error = -1, nb_filters = 0;
28abf3db 683 mode_t file_mode = opts->file_mode;
5e4cb4f4 684 bool dont_free_filtered = false;
685 git_buf unfiltered = GIT_BUF_INIT, filtered = GIT_BUF_INIT;
686 git_vector filters = GIT_VECTOR_INIT;
6eb240b0 687
5e4cb4f4 688 if (opts->disable_filters ||
689 (nb_filters = git_filters_load(
690 &filters,
691 git_object_owner((git_object *)blob),
692 path,
693 GIT_FILTER_TO_WORKTREE)) == 0) {
694
695 /* Create a fake git_buf from the blob raw data... */
696 filtered.ptr = blob->odb_object->raw.data;
697 filtered.size = blob->odb_object->raw.len;
698
699 /* ... and make sure it doesn't get unexpectedly freed */
700 dont_free_filtered = true;
701 }
702
703 if (nb_filters < 0)
704 return nb_filters;
3aa443a9 705
5e4cb4f4 706 if (nb_filters > 0) {
fade21db 707 if ((error = git_blob__getbuf(&unfiltered, blob)) < 0)
5e4cb4f4 708 goto cleanup;
709
710 if ((error = git_filters_apply(&filtered, &unfiltered, &filters)) < 0)
711 goto cleanup;
712 }
095ccc01 713
8e4aae1a
BS
714 /* Allow overriding of file mode */
715 if (!file_mode)
3aa443a9 716 file_mode = entry_filemode;
8e4aae1a 717
ad9a921b 718 error = buffer_to_file(
5cf9875a
RB
719 st, &filtered, path, opts->dir_mode, opts->file_open_flags, file_mode);
720
721 if (!error) {
722 st->st_size = blob->odb_object->raw.len;
723 st->st_mode = entry_filemode;
724 }
095ccc01 725
3aa443a9 726cleanup:
5e4cb4f4 727 git_filters_free(&filters);
728 git_buf_free(&unfiltered);
729 if (!dont_free_filtered)
730 git_buf_free(&filtered);
731
732 return error;
3aa443a9 733}
734
ad9a921b 735static int blob_content_to_link(
5cf9875a 736 struct stat *st, git_blob *blob, const char *path, int can_symlink)
3aa443a9 737{
738 git_buf linktarget = GIT_BUF_INIT;
739 int error;
6eb240b0 740
fade21db
RB
741 if ((error = git_blob__getbuf(&linktarget, blob)) < 0)
742 return error;
3aa443a9 743
5cf9875a
RB
744 if (can_symlink) {
745 if ((error = p_symlink(git_buf_cstr(&linktarget), path)) < 0)
746 giterr_set(GITERR_CHECKOUT, "Could not create symlink %s\n", path);
747 } else {
3aa443a9 748 error = git_futils_fake_symlink(git_buf_cstr(&linktarget), path);
5cf9875a
RB
749 }
750
751 if (!error) {
752 if ((error = p_lstat(path, st)) < 0)
753 giterr_set(GITERR_CHECKOUT, "Could not stat symlink %s", path);
754
755 st->st_mode = GIT_FILEMODE_LINK;
756 }
4a26ee4f 757
3aa443a9 758 git_buf_free(&linktarget);
759
760 return error;
24b0d3d5
BS
761}
762
5cf9875a
RB
763static int checkout_update_index(
764 checkout_data *data,
765 const git_diff_file *file,
766 struct stat *st)
767{
768 git_index_entry entry;
769
770 if (!data->index)
771 return 0;
772
773 memset(&entry, 0, sizeof(entry));
774 entry.path = (char *)file->path; /* cast to prevent warning */
775 git_index_entry__init_from_stat(&entry, st);
776 git_oid_cpy(&entry.oid, &file->oid);
777
778 return git_index_add(data->index, &entry);
779}
780
0d64bef9 781static int checkout_submodule(
7e5c8a5b 782 checkout_data *data,
0d64bef9
RB
783 const git_diff_file *file)
784{
5cf9875a 785 int error = 0;
e0548c0e 786 git_submodule *sm;
5cf9875a 787
ad9a921b 788 /* Until submodules are supported, UPDATE_ONLY means do nothing here */
7e5c8a5b 789 if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0)
ad9a921b
RB
790 return 0;
791
5cf9875a 792 if ((error = git_futils_mkdir(
ad9a921b 793 file->path, git_repository_workdir(data->repo),
5cf9875a
RB
794 data->opts.dir_mode, GIT_MKDIR_PATH)) < 0)
795 return error;
0d64bef9 796
e0548c0e
RB
797 if ((error = git_submodule_lookup(&sm, data->repo, file->path)) < 0)
798 return error;
799
ad9a921b 800 /* TODO: Support checkout_strategy options. Two circumstances:
0d64bef9
RB
801 * 1 - submodule already checked out, but we need to move the HEAD
802 * to the new OID, or
803 * 2 - submodule not checked out and we should recursively check it out
804 *
ad9a921b
RB
805 * Checkout will not execute a pull on the submodule, but a clone
806 * command should probably be able to. Do we need a submodule callback?
0d64bef9
RB
807 */
808
5cf9875a
RB
809 /* update the index unless prevented */
810 if ((data->strategy & GIT_CHECKOUT_DONT_UPDATE_INDEX) == 0) {
811 struct stat st;
812
813 git_buf_truncate(&data->path, data->workdir_len);
814 if (git_buf_puts(&data->path, file->path) < 0)
815 return -1;
816
817 if ((error = p_stat(git_buf_cstr(&data->path), &st)) < 0) {
818 giterr_set(
819 GITERR_CHECKOUT, "Could not stat submodule %s\n", file->path);
820 return error;
821 }
822
823 st.st_mode = GIT_FILEMODE_COMMIT;
824
825 error = checkout_update_index(data, file, &st);
826 }
827
828 return error;
0d64bef9
RB
829}
830
45b60d7b 831static void report_progress(
7e5c8a5b 832 checkout_data *data,
32def5af 833 const char *path)
45b60d7b 834{
7e5c8a5b
RB
835 if (data->opts.progress_cb)
836 data->opts.progress_cb(
ad9a921b 837 path, data->completed_steps, data->total_steps,
7e5c8a5b 838 data->opts.progress_payload);
45b60d7b
BS
839}
840
0d70f650
RB
841static int checkout_safe_for_update_only(const char *path, mode_t expected_mode)
842{
843 struct stat st;
844
845 if (p_lstat(path, &st) < 0) {
846 /* if doesn't exist, then no error and no update */
847 if (errno == ENOENT || errno == ENOTDIR)
848 return 0;
849
850 /* otherwise, stat error and no update */
851 giterr_set(GITERR_OS, "Failed to stat file '%s'", path);
852 return -1;
853 }
854
855 /* only safe for update if this is the same type of file */
856 if ((st.st_mode & ~0777) == (expected_mode & ~0777))
857 return 1;
858
859 return 0;
860}
861
3aa443a9 862static int checkout_blob(
7e5c8a5b 863 checkout_data *data,
9c05c17b 864 const git_diff_file *file)
ec532d5e 865{
32def5af 866 int error = 0;
ad9a921b 867 git_blob *blob;
5cf9875a 868 struct stat st;
1d68fcd0 869
7e5c8a5b
RB
870 git_buf_truncate(&data->path, data->workdir_len);
871 if (git_buf_puts(&data->path, file->path) < 0)
0d64bef9 872 return -1;
3aa443a9 873
0d70f650
RB
874 if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0) {
875 int rval = checkout_safe_for_update_only(
876 git_buf_cstr(&data->path), file->mode);
877 if (rval <= 0)
878 return rval;
879 }
880
ad9a921b 881 if ((error = git_blob_lookup(&blob, data->repo, &file->oid)) < 0)
0d64bef9
RB
882 return error;
883
884 if (S_ISLNK(file->mode))
885 error = blob_content_to_link(
5cf9875a 886 &st, blob, git_buf_cstr(&data->path), data->can_symlink);
3aa443a9 887 else
0d64bef9 888 error = blob_content_to_file(
5cf9875a 889 &st, blob, git_buf_cstr(&data->path), file->mode, &data->opts);
3aa443a9 890
891 git_blob_free(blob);
892
7e5c8a5b
RB
893 /* if we try to create the blob and an existing directory blocks it from
894 * being written, then there must have been a typechange conflict in a
895 * parent directory - suppress the error and try to continue.
896 */
897 if ((data->strategy & GIT_CHECKOUT_ALLOW_CONFLICTS) != 0 &&
898 (error == GIT_ENOTFOUND || error == GIT_EEXISTS))
899 {
900 giterr_clear();
901 error = 0;
902 }
903
5cf9875a
RB
904 /* update the index unless prevented */
905 if (!error && (data->strategy & GIT_CHECKOUT_DONT_UPDATE_INDEX) == 0)
906 error = checkout_update_index(data, file, &st);
907
e0548c0e
RB
908 /* update the submodule data if this was a new .gitmodules file */
909 if (!error && strcmp(file->path, ".gitmodules") == 0)
910 data->reload_submodules = true;
911
3aa443a9 912 return error;
913}
914
32def5af 915static int checkout_remove_the_old(
32def5af 916 unsigned int *actions,
7e5c8a5b 917 checkout_data *data)
32def5af 918{
cf208031 919 int error = 0;
32def5af 920 git_diff_delta *delta;
7e5c8a5b 921 const char *str;
32def5af 922 size_t i;
7e5c8a5b
RB
923 const char *workdir = git_buf_cstr(&data->path);
924 uint32_t flg = GIT_RMDIR_EMPTY_PARENTS |
925 GIT_RMDIR_REMOVE_FILES | GIT_RMDIR_REMOVE_BLOCKERS;
32def5af 926
7e5c8a5b 927 git_buf_truncate(&data->path, data->workdir_len);
ad9a921b 928
cf208031 929 git_vector_foreach(&data->diff->deltas, i, delta) {
32def5af 930 if (actions[i] & CHECKOUT_ACTION__REMOVE) {
cf208031 931 error = git_futils_rmdir_r(delta->old_file.path, workdir, flg);
7e5c8a5b 932 if (error < 0)
32def5af
RB
933 return error;
934
935 data->completed_steps++;
cf208031 936 report_progress(data, delta->old_file.path);
5cf9875a
RB
937
938 if ((actions[i] & CHECKOUT_ACTION__UPDATE_BLOB) == 0 &&
939 (data->strategy & GIT_CHECKOUT_DONT_UPDATE_INDEX) == 0 &&
940 data->index != NULL)
941 {
942 (void)git_index_remove(data->index, delta->old_file.path, 0);
943 }
32def5af
RB
944 }
945 }
946
7e5c8a5b
RB
947 git_vector_foreach(&data->removes, i, str) {
948 error = git_futils_rmdir_r(str, workdir, flg);
949 if (error < 0)
950 return error;
951
952 data->completed_steps++;
953 report_progress(data, str);
5cf9875a
RB
954
955 if ((data->strategy & GIT_CHECKOUT_DONT_UPDATE_INDEX) == 0 &&
956 data->index != NULL)
957 {
817d6251
RB
958 if (str[strlen(str) - 1] == '/')
959 (void)git_index_remove_directory(data->index, str, 0);
960 else
961 (void)git_index_remove(data->index, str, 0);
5cf9875a 962 }
7e5c8a5b
RB
963 }
964
965 return 0;
966}
967
968static int checkout_deferred_remove(git_repository *repo, const char *path)
969{
970#if 0
971 int error = git_futils_rmdir_r(
972 path, git_repository_workdir(repo), GIT_RMDIR_EMPTY_PARENTS);
973
974 if (error == GIT_ENOTFOUND) {
975 error = 0;
976 giterr_clear();
977 }
978
979 return error;
980#else
981 GIT_UNUSED(repo);
982 GIT_UNUSED(path);
5cf9875a 983 assert(false);
32def5af 984 return 0;
7e5c8a5b 985#endif
32def5af
RB
986}
987
988static int checkout_create_the_new(
32def5af 989 unsigned int *actions,
7e5c8a5b 990 checkout_data *data)
32def5af 991{
7e5c8a5b 992 int error = 0;
32def5af
RB
993 git_diff_delta *delta;
994 size_t i;
995
cf208031 996 git_vector_foreach(&data->diff->deltas, i, delta) {
7e5c8a5b
RB
997 if (actions[i] & CHECKOUT_ACTION__DEFER_REMOVE) {
998 /* this had a blocker directory that should only be removed iff
999 * all of the contents of the directory were safely removed
1000 */
1001 if ((error = checkout_deferred_remove(
1002 data->repo, delta->old_file.path)) < 0)
1003 return error;
1004 }
1005
ad9a921b 1006 if (actions[i] & CHECKOUT_ACTION__UPDATE_BLOB) {
7e5c8a5b 1007 error = checkout_blob(data, &delta->new_file);
ad9a921b 1008 if (error < 0)
32def5af
RB
1009 return error;
1010
1011 data->completed_steps++;
cf208031 1012 report_progress(data, delta->new_file.path);
32def5af 1013 }
32def5af
RB
1014 }
1015
1016 return 0;
1017}
1018
1019static int checkout_create_submodules(
32def5af 1020 unsigned int *actions,
7e5c8a5b 1021 checkout_data *data)
32def5af 1022{
7e5c8a5b 1023 int error = 0;
32def5af
RB
1024 git_diff_delta *delta;
1025 size_t i;
1026
e0548c0e
RB
1027 /* initial reload of submodules if .gitmodules was changed */
1028 if (data->reload_submodules &&
1029 (error = git_submodule_reload_all(data->repo)) < 0)
1030 return error;
1031
cf208031 1032 git_vector_foreach(&data->diff->deltas, i, delta) {
7e5c8a5b
RB
1033 if (actions[i] & CHECKOUT_ACTION__DEFER_REMOVE) {
1034 /* this has a blocker directory that should only be removed iff
1035 * all of the contents of the directory were safely removed
1036 */
1037 if ((error = checkout_deferred_remove(
1038 data->repo, delta->old_file.path)) < 0)
1039 return error;
1040 }
1041
ad9a921b 1042 if (actions[i] & CHECKOUT_ACTION__UPDATE_SUBMODULE) {
cf208031 1043 int error = checkout_submodule(data, &delta->new_file);
32def5af
RB
1044 if (error < 0)
1045 return error;
1046
1047 data->completed_steps++;
cf208031 1048 report_progress(data, delta->new_file.path);
32def5af
RB
1049 }
1050 }
1051
e0548c0e
RB
1052 /* final reload once submodules have been updated */
1053 return git_submodule_reload_all(data->repo);
32def5af
RB
1054}
1055
7e5c8a5b
RB
1056static int checkout_lookup_head_tree(git_tree **out, git_repository *repo)
1057{
1058 int error = 0;
1059 git_reference *ref = NULL;
1060 git_object *head;
1061
1062 if (!(error = git_repository_head(&ref, repo)) &&
1063 !(error = git_reference_peel(&head, ref, GIT_OBJ_TREE)))
1064 *out = (git_tree *)head;
1065
1066 git_reference_free(ref);
1067
1068 return error;
1069}
1070
1071static void checkout_data_clear(checkout_data *data)
1072{
1073 if (data->opts_free_baseline) {
1074 git_tree_free(data->opts.baseline);
1075 data->opts.baseline = NULL;
1076 }
1077
1078 git_vector_free(&data->removes);
1079 git_pool_clear(&data->pool);
1080
1081 git__free(data->pfx);
1082 data->pfx = NULL;
1083
1084 git_buf_free(&data->path);
5cf9875a
RB
1085
1086 git_index_free(data->index);
1087 data->index = NULL;
7e5c8a5b
RB
1088}
1089
1090static int checkout_data_init(
1091 checkout_data *data,
5cf9875a 1092 git_iterator *target,
7e5c8a5b 1093 git_checkout_opts *proposed)
3aa443a9 1094{
7e5c8a5b 1095 int error = 0;
cf208031 1096 git_config *cfg;
5cf9875a 1097 git_repository *repo = git_iterator_owner(target);
cf208031 1098
7e5c8a5b
RB
1099 memset(data, 0, sizeof(*data));
1100
1101 if (!repo) {
1102 giterr_set(GITERR_CHECKOUT, "Cannot checkout nothing");
cf208031 1103 return -1;
7e5c8a5b 1104 }
cf208031 1105
7e5c8a5b
RB
1106 if ((error = git_repository__ensure_not_bare(repo, "checkout")) < 0)
1107 return error;
cf208031 1108
7e5c8a5b
RB
1109 if ((error = git_repository_config__weakptr(&cfg, repo)) < 0)
1110 return error;
1111
1112 data->repo = repo;
1113
1114 GITERR_CHECK_VERSION(
1115 proposed, GIT_CHECKOUT_OPTS_VERSION, "git_checkout_opts");
1116
1117 if (!proposed)
1118 GIT_INIT_STRUCTURE(&data->opts, GIT_CHECKOUT_OPTS_VERSION);
1119 else
1120 memmove(&data->opts, proposed, sizeof(git_checkout_opts));
1121
5cf9875a
RB
1122 /* refresh config and index content unless NO_REFRESH is given */
1123 if ((data->opts.checkout_strategy & GIT_CHECKOUT_NO_REFRESH) == 0) {
1124 if ((error = git_config_refresh(cfg)) < 0)
1125 goto cleanup;
1126
1127 if (git_iterator_inner_type(target) == GIT_ITERATOR_INDEX) {
1128 /* if we are iterating over the index, don't reload */
1129 data->index = git_iterator_index_get_index(target);
1130 GIT_REFCOUNT_INC(data->index);
1131 } else {
1132 /* otherwise, grab and reload the index */
1133 if ((error = git_repository_index(&data->index, data->repo)) < 0 ||
1134 (error = git_index_read(data->index)) < 0)
1135 goto cleanup;
1136 }
1137 }
7e5c8a5b 1138
5cf9875a 1139 /* if you are forcing, definitely allow safe updates */
7e5c8a5b
RB
1140 if ((data->opts.checkout_strategy & GIT_CHECKOUT_FORCE) != 0)
1141 data->opts.checkout_strategy |= GIT_CHECKOUT_SAFE_CREATE;
1142 if ((data->opts.checkout_strategy & GIT_CHECKOUT_SAFE_CREATE) != 0)
1143 data->opts.checkout_strategy |= GIT_CHECKOUT_SAFE;
1144
1145 data->strategy = data->opts.checkout_strategy;
1146
1147 /* opts->disable_filters is false by default */
1148
1149 if (!data->opts.dir_mode)
1150 data->opts.dir_mode = GIT_DIR_MODE;
1151
1152 if (!data->opts.file_open_flags)
1153 data->opts.file_open_flags = O_CREAT | O_TRUNC | O_WRONLY;
1154
1155 data->pfx = git_pathspec_prefix(&data->opts.paths);
1156
1157 error = git_config_get_bool(&data->can_symlink, cfg, "core.symlinks");
1158 if (error < 0) {
1159 if (error != GIT_ENOTFOUND)
1160 goto cleanup;
1161
1162 /* If "core.symlinks" is not found anywhere, default to true. */
1163 data->can_symlink = true;
1164 giterr_clear();
cf208031
RB
1165 error = 0;
1166 }
1167
7e5c8a5b
RB
1168 if (!data->opts.baseline) {
1169 data->opts_free_baseline = true;
1170 if ((error = checkout_lookup_head_tree(&data->opts.baseline, repo)) < 0)
1171 goto cleanup;
1172 }
1173
1174 if ((error = git_vector_init(&data->removes, 0, git__strcmp_cb)) < 0 ||
1175 (error = git_pool_init(&data->pool, 1, 0)) < 0 ||
1176 (error = git_buf_puts(&data->path, git_repository_workdir(repo))) < 0)
1177 goto cleanup;
1178
1179 data->workdir_len = git_buf_len(&data->path);
1180
1181cleanup:
1182 if (error < 0)
1183 checkout_data_clear(data);
cf208031
RB
1184
1185 return error;
1186}
1187
7e5c8a5b
RB
1188int git_checkout_iterator(
1189 git_iterator *target,
1190 git_checkout_opts *opts)
cf208031
RB
1191{
1192 int error = 0;
7e5c8a5b
RB
1193 git_iterator *baseline = NULL, *workdir = NULL;
1194 checkout_data data = {0};
cf208031 1195 git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
32def5af
RB
1196 uint32_t *actions = NULL;
1197 size_t *counts = NULL;
3aa443a9 1198
7e5c8a5b 1199 /* initialize structures and options */
5cf9875a 1200 error = checkout_data_init(&data, target, opts);
7e5c8a5b
RB
1201 if (error < 0)
1202 return error;
c7231c45 1203
7e5c8a5b
RB
1204 diff_opts.flags =
1205 GIT_DIFF_INCLUDE_UNMODIFIED |
1206 GIT_DIFF_INCLUDE_UNTRACKED |
1207 GIT_DIFF_RECURSE_UNTRACKED_DIRS | /* needed to match baseline */
1208 GIT_DIFF_INCLUDE_IGNORED |
1209 GIT_DIFF_INCLUDE_TYPECHANGE |
1210 GIT_DIFF_INCLUDE_TYPECHANGE_TREES |
1211 GIT_DIFF_SKIP_BINARY_CHECK;
1212 if (data.opts.paths.count > 0)
1213 diff_opts.pathspec = data.opts.paths;
1214
1215 /* set up iterators */
1216 if ((error = git_iterator_reset(target, data.pfx, data.pfx)) < 0 ||
1217 (error = git_iterator_for_workdir_range(
1218 &workdir, data.repo, data.pfx, data.pfx)) < 0 ||
1219 (error = git_iterator_for_tree_range(
1220 &baseline, data.opts.baseline, data.pfx, data.pfx)) < 0)
1221 goto cleanup;
1222
1223 /* Handle case insensitivity for baseline if necessary */
1224 if (workdir->ignore_case && !baseline->ignore_case) {
546d65a8 1225 if ((error = git_iterator_spoolandsort_push(baseline, true)) < 0)
7e5c8a5b 1226 goto cleanup;
cf208031 1227 }
3aa443a9 1228
77cffa31
RB
1229 /* Generate baseline-to-target diff which will include an entry for
1230 * every possible update that might need to be made.
cf208031
RB
1231 */
1232 if ((error = git_diff__from_iterators(
7e5c8a5b 1233 &data.diff, data.repo, baseline, target, &diff_opts)) < 0)
3aa443a9 1234 goto cleanup;
1235
77cffa31
RB
1236 /* Loop through diff (and working directory iterator) building a list of
1237 * actions to be taken, plus look for conflicts and send notifications.
32def5af 1238 */
7e5c8a5b 1239 if ((error = checkout_get_actions(&actions, &counts, &data, workdir)) < 0)
ad9a921b
RB
1240 goto cleanup;
1241
32def5af 1242 data.total_steps = counts[CHECKOUT_ACTION__REMOVE] +
ad9a921b
RB
1243 counts[CHECKOUT_ACTION__UPDATE_BLOB] +
1244 counts[CHECKOUT_ACTION__UPDATE_SUBMODULE];
3aa443a9 1245
ad9a921b 1246 report_progress(&data, NULL); /* establish 0 baseline */
45b60d7b 1247
77cffa31
RB
1248 /* To deal with some order dependencies, perform remaining checkout
1249 * in three passes: removes, then update blobs, then update submodules.
1250 */
32def5af 1251 if (counts[CHECKOUT_ACTION__REMOVE] > 0 &&
cf208031 1252 (error = checkout_remove_the_old(actions, &data)) < 0)
32def5af
RB
1253 goto cleanup;
1254
ad9a921b 1255 if (counts[CHECKOUT_ACTION__UPDATE_BLOB] > 0 &&
cf208031 1256 (error = checkout_create_the_new(actions, &data)) < 0)
32def5af
RB
1257 goto cleanup;
1258
ad9a921b 1259 if (counts[CHECKOUT_ACTION__UPDATE_SUBMODULE] > 0 &&
cf208031 1260 (error = checkout_create_submodules(actions, &data)) < 0)
32def5af
RB
1261 goto cleanup;
1262
1263 assert(data.completed_steps == data.total_steps);
3aa443a9 1264
0d64bef9 1265cleanup:
fade21db 1266 if (error == GIT_EUSER)
32def5af 1267 giterr_clear();
fade21db 1268
5cf9875a
RB
1269 if (!error && data.index != NULL &&
1270 (data.strategy & GIT_CHECKOUT_DONT_UPDATE_INDEX) == 0)
1271 error = git_index_write(data.index);
1272
cf208031 1273 git_diff_list_free(data.diff);
7e5c8a5b 1274 git_iterator_free(workdir);
dde7602a 1275 git_iterator_free(baseline);
32def5af
RB
1276 git__free(actions);
1277 git__free(counts);
7e5c8a5b 1278 checkout_data_clear(&data);
cf208031
RB
1279
1280 return error;
1281}
1282
cf208031
RB
1283int git_checkout_index(
1284 git_repository *repo,
1285 git_index *index,
1286 git_checkout_opts *opts)
1287{
1288 int error;
7e5c8a5b 1289 git_iterator *index_i;
cf208031
RB
1290
1291 if ((error = git_repository__ensure_not_bare(repo, "checkout index")) < 0)
1292 return error;
1293
1294 if (!index && (error = git_repository_index__weakptr(&index, repo)) < 0)
1295 return error;
7e5c8a5b 1296 GIT_REFCOUNT_INC(index);
cf208031 1297
7e5c8a5b
RB
1298 if (!(error = git_iterator_for_index(&index_i, index)))
1299 error = git_checkout_iterator(index_i, opts);
cf208031 1300
cf208031 1301 git_iterator_free(index_i);
7e5c8a5b 1302 git_index_free(index);
0d64bef9 1303
e93af304 1304 return error;
1305}
1306
1307int git_checkout_tree(
1308 git_repository *repo,
cfbe4be3 1309 const git_object *treeish,
80642656 1310 git_checkout_opts *opts)
e93af304 1311{
cf208031 1312 int error;
7e5c8a5b
RB
1313 git_tree *tree = NULL;
1314 git_iterator *tree_i = NULL;
cf208031
RB
1315
1316 if ((error = git_repository__ensure_not_bare(repo, "checkout tree")) < 0)
1317 return error;
e93af304 1318
1319 if (git_object_peel((git_object **)&tree, treeish, GIT_OBJ_TREE) < 0) {
ad9a921b
RB
1320 giterr_set(
1321 GITERR_CHECKOUT, "Provided object cannot be peeled to a tree");
1322 return -1;
e93af304 1323 }
1324
7e5c8a5b
RB
1325 if (!(error = git_iterator_for_tree(&tree_i, tree)))
1326 error = git_checkout_iterator(tree_i, opts);
e93af304 1327
cf208031 1328 git_iterator_free(tree_i);
3aa443a9 1329 git_tree_free(tree);
ad9a921b 1330
3aa443a9 1331 return error;
14741d62
BS
1332}
1333
3aa443a9 1334int git_checkout_head(
1335 git_repository *repo,
80642656 1336 git_checkout_opts *opts)
3aa443a9 1337{
1338 int error;
7e5c8a5b
RB
1339 git_tree *head = NULL;
1340 git_iterator *head_i = NULL;
3aa443a9 1341
cf208031
RB
1342 if ((error = git_repository__ensure_not_bare(repo, "checkout head")) < 0)
1343 return error;
1344
7e5c8a5b
RB
1345 if (!(error = checkout_lookup_head_tree(&head, repo)) &&
1346 !(error = git_iterator_for_tree(&head_i, head)))
1347 error = git_checkout_iterator(head_i, opts);
cf208031 1348
7e5c8a5b 1349 git_iterator_free(head_i);
cf208031 1350 git_tree_free(head);
14741d62 1351
3aa443a9 1352 return error;
1353}