File Coverage

deps/libgit2/src/apply.c
Criterion Covered Total %
statement 0 384 0.0
branch 0 272 0.0
condition n/a
subroutine n/a
pod n/a
total 0 656 0.0


line stmt bran cond sub pod time code
1             /*
2             * Copyright (C) the libgit2 contributors. All rights reserved.
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 "apply.h"
9              
10             #include "git2/apply.h"
11             #include "git2/patch.h"
12             #include "git2/filter.h"
13             #include "git2/blob.h"
14             #include "git2/index.h"
15             #include "git2/checkout.h"
16             #include "git2/repository.h"
17             #include "array.h"
18             #include "patch.h"
19             #include "futils.h"
20             #include "delta.h"
21             #include "zstream.h"
22             #include "reader.h"
23             #include "index.h"
24              
25             typedef struct {
26             /* The lines that we allocate ourself are allocated out of the pool.
27             * (Lines may have been allocated out of the diff.)
28             */
29             git_pool pool;
30             git_vector lines;
31             } patch_image;
32              
33             static int apply_err(const char *fmt, ...) GIT_FORMAT_PRINTF(1, 2);
34 0           static int apply_err(const char *fmt, ...)
35             {
36             va_list ap;
37              
38 0           va_start(ap, fmt);
39 0           git_error_vset(GIT_ERROR_PATCH, fmt, ap);
40 0           va_end(ap);
41              
42 0           return GIT_EAPPLYFAIL;
43             }
44              
45 0           static void patch_line_init(
46             git_diff_line *out,
47             const char *in,
48             size_t in_len,
49             size_t in_offset)
50             {
51 0           out->content = in;
52 0           out->content_len = in_len;
53 0           out->content_offset = in_offset;
54 0           }
55              
56             #define PATCH_IMAGE_INIT { GIT_POOL_INIT, GIT_VECTOR_INIT }
57              
58 0           static int patch_image_init_fromstr(
59             patch_image *out, const char *in, size_t in_len)
60             {
61             git_diff_line *line;
62             const char *start, *end;
63              
64 0           memset(out, 0x0, sizeof(patch_image));
65              
66 0           git_pool_init(&out->pool, sizeof(git_diff_line));
67              
68 0 0         for (start = in; start < in + in_len; start = end) {
69 0           end = memchr(start, '\n', in_len - (start - in));
70              
71 0 0         if (end == NULL)
72 0           end = in + in_len;
73              
74 0 0         else if (end < in + in_len)
75 0           end++;
76              
77 0           line = git_pool_mallocz(&out->pool, 1);
78 0 0         GIT_ERROR_CHECK_ALLOC(line);
79              
80 0 0         if (git_vector_insert(&out->lines, line) < 0)
81 0           return -1;
82              
83 0           patch_line_init(line, start, (end - start), (start - in));
84             }
85              
86 0           return 0;
87             }
88              
89 0           static void patch_image_free(patch_image *image)
90             {
91 0 0         if (image == NULL)
92 0           return;
93              
94 0           git_pool_clear(&image->pool);
95 0           git_vector_free(&image->lines);
96             }
97              
98 0           static bool match_hunk(
99             patch_image *image,
100             patch_image *preimage,
101             size_t linenum)
102             {
103 0           bool match = 0;
104             size_t i;
105              
106             /* Ensure this hunk is within the image boundaries. */
107 0 0         if (git_vector_length(&preimage->lines) + linenum >
108 0           git_vector_length(&image->lines))
109 0           return 0;
110              
111 0           match = 1;
112              
113             /* Check exact match. */
114 0 0         for (i = 0; i < git_vector_length(&preimage->lines); i++) {
115 0           git_diff_line *preimage_line = git_vector_get(&preimage->lines, i);
116 0           git_diff_line *image_line = git_vector_get(&image->lines, linenum + i);
117              
118 0 0         if (preimage_line->content_len != image_line->content_len ||
    0          
119 0           memcmp(preimage_line->content, image_line->content, image_line->content_len) != 0) {
120 0           match = 0;
121 0           break;
122             }
123             }
124              
125 0           return match;
126             }
127              
128 0           static bool find_hunk_linenum(
129             size_t *out,
130             patch_image *image,
131             patch_image *preimage,
132             size_t linenum)
133             {
134 0           size_t max = git_vector_length(&image->lines);
135             bool match;
136              
137 0 0         if (linenum > max)
138 0           linenum = max;
139              
140 0           match = match_hunk(image, preimage, linenum);
141              
142 0           *out = linenum;
143 0           return match;
144             }
145              
146 0           static int update_hunk(
147             patch_image *image,
148             size_t linenum,
149             patch_image *preimage,
150             patch_image *postimage)
151             {
152 0           size_t postlen = git_vector_length(&postimage->lines);
153 0           size_t prelen = git_vector_length(&preimage->lines);
154             size_t i;
155 0           int error = 0;
156              
157 0 0         if (postlen > prelen)
158 0           error = git_vector_insert_null(
159             &image->lines, linenum, (postlen - prelen));
160 0 0         else if (prelen > postlen)
161 0           error = git_vector_remove_range(
162             &image->lines, linenum, (prelen - postlen));
163              
164 0 0         if (error) {
165 0           git_error_set_oom();
166 0           return -1;
167             }
168              
169 0 0         for (i = 0; i < git_vector_length(&postimage->lines); i++) {
170 0           image->lines.contents[linenum + i] =
171 0           git_vector_get(&postimage->lines, i);
172             }
173              
174 0           return 0;
175             }
176              
177             typedef struct {
178             git_apply_options opts;
179             size_t skipped_new_lines;
180             size_t skipped_old_lines;
181             } apply_hunks_ctx;
182              
183 0           static int apply_hunk(
184             patch_image *image,
185             git_patch *patch,
186             git_patch_hunk *hunk,
187             apply_hunks_ctx *ctx)
188             {
189 0           patch_image preimage = PATCH_IMAGE_INIT, postimage = PATCH_IMAGE_INIT;
190             size_t line_num, i;
191 0           int error = 0;
192              
193 0 0         if (ctx->opts.hunk_cb) {
194 0           error = ctx->opts.hunk_cb(&hunk->hunk, ctx->opts.payload);
195              
196 0 0         if (error) {
197 0 0         if (error > 0) {
198 0           ctx->skipped_new_lines += hunk->hunk.new_lines;
199 0           ctx->skipped_old_lines += hunk->hunk.old_lines;
200 0           error = 0;
201             }
202              
203 0           goto done;
204             }
205             }
206              
207 0 0         for (i = 0; i < hunk->line_count; i++) {
208 0           size_t linenum = hunk->line_start + i;
209 0 0         git_diff_line *line = git_array_get(patch->lines, linenum), *prev;
210              
211 0 0         if (!line) {
212 0           error = apply_err("preimage does not contain line %"PRIuZ, linenum);
213 0           goto done;
214             }
215              
216 0           switch (line->origin) {
217             case GIT_DIFF_LINE_CONTEXT_EOFNL:
218             case GIT_DIFF_LINE_DEL_EOFNL:
219             case GIT_DIFF_LINE_ADD_EOFNL:
220 0 0         prev = i ? git_array_get(patch->lines, linenum - 1) : NULL;
    0          
221 0 0         if (prev && prev->content[prev->content_len - 1] == '\n')
    0          
222 0           prev->content_len -= 1;
223 0           break;
224             case GIT_DIFF_LINE_CONTEXT:
225 0 0         if ((error = git_vector_insert(&preimage.lines, line)) < 0 ||
    0          
226             (error = git_vector_insert(&postimage.lines, line)) < 0)
227             goto done;
228 0           break;
229             case GIT_DIFF_LINE_DELETION:
230 0 0         if ((error = git_vector_insert(&preimage.lines, line)) < 0)
231 0           goto done;
232 0           break;
233             case GIT_DIFF_LINE_ADDITION:
234 0 0         if ((error = git_vector_insert(&postimage.lines, line)) < 0)
235 0           goto done;
236 0           break;
237             }
238             }
239              
240 0 0         if (hunk->hunk.new_start) {
241 0           line_num = hunk->hunk.new_start -
242 0           ctx->skipped_new_lines +
243 0           ctx->skipped_old_lines -
244             1;
245             } else {
246 0           line_num = 0;
247             }
248              
249 0 0         if (!find_hunk_linenum(&line_num, image, &preimage, line_num)) {
250 0           error = apply_err("hunk at line %d did not apply",
251             hunk->hunk.new_start);
252 0           goto done;
253             }
254              
255 0           error = update_hunk(image, line_num, &preimage, &postimage);
256              
257             done:
258 0           patch_image_free(&preimage);
259 0           patch_image_free(&postimage);
260              
261 0           return error;
262             }
263              
264 0           static int apply_hunks(
265             git_buf *out,
266             const char *source,
267             size_t source_len,
268             git_patch *patch,
269             apply_hunks_ctx *ctx)
270             {
271             git_patch_hunk *hunk;
272             git_diff_line *line;
273             patch_image image;
274             size_t i;
275 0           int error = 0;
276              
277 0 0         if ((error = patch_image_init_fromstr(&image, source, source_len)) < 0)
278 0           goto done;
279              
280 0 0         git_array_foreach(patch->hunks, i, hunk) {
    0          
281 0 0         if ((error = apply_hunk(&image, patch, hunk, ctx)) < 0)
282 0           goto done;
283             }
284              
285 0 0         git_vector_foreach(&image.lines, i, line)
286 0           git_buf_put(out, line->content, line->content_len);
287              
288             done:
289 0           patch_image_free(&image);
290              
291 0           return error;
292             }
293              
294 0           static int apply_binary_delta(
295             git_buf *out,
296             const char *source,
297             size_t source_len,
298             git_diff_binary_file *binary_file)
299             {
300 0           git_buf inflated = GIT_BUF_INIT;
301 0           int error = 0;
302              
303             /* no diff means identical contents */
304 0 0         if (binary_file->datalen == 0)
305 0           return git_buf_put(out, source, source_len);
306              
307 0           error = git_zstream_inflatebuf(&inflated,
308 0           binary_file->data, binary_file->datalen);
309              
310 0 0         if (!error && inflated.size != binary_file->inflatedlen) {
    0          
311 0           error = apply_err("inflated delta does not match expected length");
312 0           git_buf_dispose(out);
313             }
314              
315 0 0         if (error < 0)
316 0           goto done;
317              
318 0 0         if (binary_file->type == GIT_DIFF_BINARY_DELTA) {
319             void *data;
320             size_t data_len;
321              
322 0           error = git_delta_apply(&data, &data_len, (void *)source, source_len,
323 0           (void *)inflated.ptr, inflated.size);
324              
325 0           out->ptr = data;
326 0           out->size = data_len;
327 0           out->asize = data_len;
328             }
329 0 0         else if (binary_file->type == GIT_DIFF_BINARY_LITERAL) {
330 0           git_buf_swap(out, &inflated);
331             }
332             else {
333 0           error = apply_err("unknown binary delta type");
334 0           goto done;
335             }
336              
337             done:
338 0           git_buf_dispose(&inflated);
339 0           return error;
340             }
341              
342 0           static int apply_binary(
343             git_buf *out,
344             const char *source,
345             size_t source_len,
346             git_patch *patch)
347             {
348 0           git_buf reverse = GIT_BUF_INIT;
349 0           int error = 0;
350              
351 0 0         if (!patch->binary.contains_data) {
352 0           error = apply_err("patch does not contain binary data");
353 0           goto done;
354             }
355              
356 0 0         if (!patch->binary.old_file.datalen && !patch->binary.new_file.datalen)
    0          
357 0           goto done;
358              
359             /* first, apply the new_file delta to the given source */
360 0 0         if ((error = apply_binary_delta(out, source, source_len,
361             &patch->binary.new_file)) < 0)
362 0           goto done;
363              
364             /* second, apply the old_file delta to sanity check the result */
365 0 0         if ((error = apply_binary_delta(&reverse, out->ptr, out->size,
366             &patch->binary.old_file)) < 0)
367 0           goto done;
368              
369             /* Verify that the resulting file with the reverse patch applied matches the source file */
370 0 0         if (source_len != reverse.size ||
    0          
371 0 0         (source_len && memcmp(source, reverse.ptr, source_len) != 0)) {
372 0           error = apply_err("binary patch did not apply cleanly");
373 0           goto done;
374             }
375              
376             done:
377 0 0         if (error < 0)
378 0           git_buf_dispose(out);
379              
380 0           git_buf_dispose(&reverse);
381 0           return error;
382             }
383              
384 0           int git_apply__patch(
385             git_buf *contents_out,
386             char **filename_out,
387             unsigned int *mode_out,
388             const char *source,
389             size_t source_len,
390             git_patch *patch,
391             const git_apply_options *given_opts)
392             {
393 0           apply_hunks_ctx ctx = { GIT_APPLY_OPTIONS_INIT };
394 0           char *filename = NULL;
395 0           unsigned int mode = 0;
396 0           int error = 0;
397              
398 0 0         assert(contents_out && filename_out && mode_out && (source || !source_len) && patch);
    0          
    0          
    0          
    0          
    0          
399              
400 0 0         if (given_opts)
401 0           memcpy(&ctx.opts, given_opts, sizeof(git_apply_options));
402              
403 0           *filename_out = NULL;
404 0           *mode_out = 0;
405              
406 0 0         if (patch->delta->status != GIT_DELTA_DELETED) {
407 0           const git_diff_file *newfile = &patch->delta->new_file;
408              
409 0           filename = git__strdup(newfile->path);
410 0 0         mode = newfile->mode ?
411 0           newfile->mode : GIT_FILEMODE_BLOB;
412             }
413              
414 0 0         if (patch->delta->flags & GIT_DIFF_FLAG_BINARY)
415 0           error = apply_binary(contents_out, source, source_len, patch);
416 0 0         else if (patch->hunks.size)
417 0           error = apply_hunks(contents_out, source, source_len, patch, &ctx);
418             else
419 0           error = git_buf_put(contents_out, source, source_len);
420              
421 0 0         if (error)
422 0           goto done;
423              
424 0           if (patch->delta->status == GIT_DELTA_DELETED &&
425 0           git_buf_len(contents_out) > 0) {
426 0           error = apply_err("removal patch leaves file contents");
427 0           goto done;
428             }
429              
430 0           *filename_out = filename;
431 0           *mode_out = mode;
432              
433             done:
434 0 0         if (error < 0)
435 0           git__free(filename);
436              
437 0           return error;
438             }
439              
440 0           static int apply_one(
441             git_repository *repo,
442             git_reader *preimage_reader,
443             git_index *preimage,
444             git_reader *postimage_reader,
445             git_index *postimage,
446             git_diff *diff,
447             git_strmap *removed_paths,
448             size_t i,
449             const git_apply_options *opts)
450             {
451 0           git_patch *patch = NULL;
452 0           git_buf pre_contents = GIT_BUF_INIT, post_contents = GIT_BUF_INIT;
453             const git_diff_delta *delta;
454 0           char *filename = NULL;
455             unsigned int mode;
456             git_oid pre_id, post_id;
457             git_filemode_t pre_filemode;
458             git_index_entry pre_entry, post_entry;
459 0           bool skip_preimage = false;
460             int error;
461              
462 0 0         if ((error = git_patch_from_diff(&patch, diff, i)) < 0)
463 0           goto done;
464              
465 0           delta = git_patch_get_delta(patch);
466              
467 0 0         if (opts->delta_cb) {
468 0           error = opts->delta_cb(delta, opts->payload);
469              
470 0 0         if (error) {
471 0 0         if (error > 0)
472 0           error = 0;
473              
474 0           goto done;
475             }
476             }
477              
478             /*
479             * Ensure that the file has not been deleted or renamed if we're
480             * applying a modification delta.
481             */
482 0 0         if (delta->status != GIT_DELTA_RENAMED &&
    0          
483 0           delta->status != GIT_DELTA_ADDED) {
484 0 0         if (git_strmap_exists(removed_paths, delta->old_file.path)) {
485 0           error = apply_err("path '%s' has been renamed or deleted", delta->old_file.path);
486 0           goto done;
487             }
488             }
489              
490             /*
491             * We may be applying a second delta to an already seen file. If so,
492             * use the already modified data in the postimage instead of the
493             * content from the index or working directory. (Don't do this in
494             * the case of a rename, which must be specified before additional
495             * deltas since we apply deltas to the target filename.)
496             */
497 0 0         if (delta->status != GIT_DELTA_RENAMED) {
498 0 0         if ((error = git_reader_read(&pre_contents, &pre_id, &pre_filemode,
499             postimage_reader, delta->old_file.path)) == 0) {
500 0           skip_preimage = true;
501 0 0         } else if (error == GIT_ENOTFOUND) {
502 0           git_error_clear();
503 0           error = 0;
504             } else {
505 0           goto done;
506             }
507             }
508              
509 0 0         if (!skip_preimage && delta->status != GIT_DELTA_ADDED) {
    0          
510 0           error = git_reader_read(&pre_contents, &pre_id, &pre_filemode,
511             preimage_reader, delta->old_file.path);
512              
513             /* ENOTFOUND means the preimage was not found; apply failed. */
514 0 0         if (error == GIT_ENOTFOUND)
515 0           error = GIT_EAPPLYFAIL;
516              
517             /* When applying to BOTH, the index did not match the workdir. */
518 0 0         if (error == GIT_READER_MISMATCH)
519 0           error = apply_err("%s: does not match index", delta->old_file.path);
520              
521 0 0         if (error < 0)
522 0           goto done;
523              
524             /*
525             * We need to populate the preimage data structure with the
526             * contents that we are using as the preimage for this file.
527             * This allows us to apply patches to files that have been
528             * modified in the working directory. During checkout,
529             * we will use this expected preimage as the baseline, and
530             * limit checkout to only the paths affected by patch
531             * application. (Without this, we would fail to write the
532             * postimage contents to any file that had been modified
533             * from HEAD on-disk, even if the patch application succeeded.)
534             * Use the contents from the delta where available - some
535             * fields may not be available, like the old file mode (eg in
536             * an exact rename situation) so trust the patch parsing to
537             * validate and use the preimage data in that case.
538             */
539 0 0         if (preimage) {
540 0           memset(&pre_entry, 0, sizeof(git_index_entry));
541 0           pre_entry.path = delta->old_file.path;
542 0 0         pre_entry.mode = delta->old_file.mode ? delta->old_file.mode : pre_filemode;
543 0           git_oid_cpy(&pre_entry.id, &pre_id);
544              
545 0 0         if ((error = git_index_add(preimage, &pre_entry)) < 0)
546 0           goto done;
547             }
548             }
549              
550 0 0         if (delta->status != GIT_DELTA_DELETED) {
551 0 0         if ((error = git_apply__patch(&post_contents, &filename, &mode,
552 0 0         pre_contents.ptr, pre_contents.size, patch, opts)) < 0 ||
553 0           (error = git_blob_create_from_buffer(&post_id, repo,
554 0           post_contents.ptr, post_contents.size)) < 0)
555             goto done;
556              
557 0           memset(&post_entry, 0, sizeof(git_index_entry));
558 0           post_entry.path = filename;
559 0           post_entry.mode = mode;
560 0           git_oid_cpy(&post_entry.id, &post_id);
561              
562 0 0         if ((error = git_index_add(postimage, &post_entry)) < 0)
563 0           goto done;
564             }
565              
566 0 0         if (delta->status == GIT_DELTA_RENAMED ||
    0          
567 0           delta->status == GIT_DELTA_DELETED)
568 0           error = git_strmap_set(removed_paths, delta->old_file.path, (char *) delta->old_file.path);
569              
570 0 0         if (delta->status == GIT_DELTA_RENAMED ||
    0          
571 0           delta->status == GIT_DELTA_ADDED)
572 0           git_strmap_delete(removed_paths, delta->new_file.path);
573              
574             done:
575 0           git_buf_dispose(&pre_contents);
576 0           git_buf_dispose(&post_contents);
577 0           git__free(filename);
578 0           git_patch_free(patch);
579              
580 0           return error;
581             }
582              
583 0           static int apply_deltas(
584             git_repository *repo,
585             git_reader *pre_reader,
586             git_index *preimage,
587             git_reader *post_reader,
588             git_index *postimage,
589             git_diff *diff,
590             const git_apply_options *opts)
591             {
592             git_strmap *removed_paths;
593             size_t i;
594 0           int error = 0;
595              
596 0 0         if (git_strmap_new(&removed_paths) < 0)
597 0           return -1;
598              
599 0 0         for (i = 0; i < git_diff_num_deltas(diff); i++) {
600 0 0         if ((error = apply_one(repo, pre_reader, preimage, post_reader, postimage, diff, removed_paths, i, opts)) < 0)
601 0           goto done;
602             }
603              
604             done:
605 0           git_strmap_free(removed_paths);
606 0           return error;
607             }
608              
609 0           int git_apply_to_tree(
610             git_index **out,
611             git_repository *repo,
612             git_tree *preimage,
613             git_diff *diff,
614             const git_apply_options *given_opts)
615             {
616 0           git_index *postimage = NULL;
617 0           git_reader *pre_reader = NULL, *post_reader = NULL;
618 0           git_apply_options opts = GIT_APPLY_OPTIONS_INIT;
619             const git_diff_delta *delta;
620             size_t i;
621 0           int error = 0;
622              
623 0 0         assert(out && repo && preimage && diff);
    0          
    0          
    0          
624              
625 0           *out = NULL;
626              
627 0 0         if (given_opts)
628 0           memcpy(&opts, given_opts, sizeof(git_apply_options));
629              
630 0 0         if ((error = git_reader_for_tree(&pre_reader, preimage)) < 0)
631 0           goto done;
632              
633             /*
634             * put the current tree into the postimage as-is - the diff will
635             * replace any entries contained therein
636             */
637 0 0         if ((error = git_index_new(&postimage)) < 0 ||
    0          
638 0 0         (error = git_index_read_tree(postimage, preimage)) < 0 ||
639 0           (error = git_reader_for_index(&post_reader, repo, postimage)) < 0)
640             goto done;
641              
642             /*
643             * Remove the old paths from the index before applying diffs -
644             * we need to do a full pass to remove them before adding deltas,
645             * in order to handle rename situations.
646             */
647 0 0         for (i = 0; i < git_diff_num_deltas(diff); i++) {
648 0           delta = git_diff_get_delta(diff, i);
649              
650 0 0         if (delta->status == GIT_DELTA_DELETED ||
    0          
651 0           delta->status == GIT_DELTA_RENAMED) {
652 0 0         if ((error = git_index_remove(postimage,
653             delta->old_file.path, 0)) < 0)
654 0           goto done;
655             }
656             }
657              
658 0 0         if ((error = apply_deltas(repo, pre_reader, NULL, post_reader, postimage, diff, &opts)) < 0)
659 0           goto done;
660              
661 0           *out = postimage;
662              
663             done:
664 0 0         if (error < 0)
665 0           git_index_free(postimage);
666              
667 0           git_reader_free(pre_reader);
668 0           git_reader_free(post_reader);
669              
670 0           return error;
671             }
672              
673 0           static int git_apply__to_workdir(
674             git_repository *repo,
675             git_diff *diff,
676             git_index *preimage,
677             git_index *postimage,
678             git_apply_location_t location,
679             git_apply_options *opts)
680             {
681 0           git_vector paths = GIT_VECTOR_INIT;
682 0           git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
683             const git_diff_delta *delta;
684             size_t i;
685             int error;
686              
687             GIT_UNUSED(opts);
688              
689             /*
690             * Limit checkout to the paths affected by the diff; this ensures
691             * that other modifications in the working directory are unaffected.
692             */
693 0 0         if ((error = git_vector_init(&paths, git_diff_num_deltas(diff), NULL)) < 0)
694 0           goto done;
695              
696 0 0         for (i = 0; i < git_diff_num_deltas(diff); i++) {
697 0           delta = git_diff_get_delta(diff, i);
698              
699 0 0         if ((error = git_vector_insert(&paths, (void *)delta->old_file.path)) < 0)
700 0           goto done;
701              
702 0 0         if (strcmp(delta->old_file.path, delta->new_file.path) &&
    0          
703 0           (error = git_vector_insert(&paths, (void *)delta->new_file.path)) < 0)
704 0           goto done;
705             }
706              
707 0           checkout_opts.checkout_strategy |= GIT_CHECKOUT_SAFE;
708 0           checkout_opts.checkout_strategy |= GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH;
709 0           checkout_opts.checkout_strategy |= GIT_CHECKOUT_DONT_WRITE_INDEX;
710              
711 0 0         if (location == GIT_APPLY_LOCATION_WORKDIR)
712 0           checkout_opts.checkout_strategy |= GIT_CHECKOUT_DONT_UPDATE_INDEX;
713              
714 0           checkout_opts.paths.strings = (char **)paths.contents;
715 0           checkout_opts.paths.count = paths.length;
716              
717 0           checkout_opts.baseline_index = preimage;
718              
719 0           error = git_checkout_index(repo, postimage, &checkout_opts);
720              
721             done:
722 0           git_vector_free(&paths);
723 0           return error;
724             }
725              
726 0           static int git_apply__to_index(
727             git_repository *repo,
728             git_diff *diff,
729             git_index *preimage,
730             git_index *postimage,
731             git_apply_options *opts)
732             {
733 0           git_index *index = NULL;
734             const git_diff_delta *delta;
735             const git_index_entry *entry;
736             size_t i;
737             int error;
738              
739             GIT_UNUSED(preimage);
740             GIT_UNUSED(opts);
741              
742 0 0         if ((error = git_repository_index(&index, repo)) < 0)
743 0           goto done;
744              
745             /* Remove deleted (or renamed) paths from the index. */
746 0 0         for (i = 0; i < git_diff_num_deltas(diff); i++) {
747 0           delta = git_diff_get_delta(diff, i);
748              
749 0 0         if (delta->status == GIT_DELTA_DELETED ||
    0          
750 0           delta->status == GIT_DELTA_RENAMED) {
751 0 0         if ((error = git_index_remove(index, delta->old_file.path, 0)) < 0)
752 0           goto done;
753             }
754             }
755              
756             /* Then add the changes back to the index. */
757 0 0         for (i = 0; i < git_index_entrycount(postimage); i++) {
758 0           entry = git_index_get_byindex(postimage, i);
759              
760 0 0         if ((error = git_index_add(index, entry)) < 0)
761 0           goto done;
762             }
763              
764             done:
765 0           git_index_free(index);
766 0           return error;
767             }
768              
769 0           int git_apply_options_init(git_apply_options *opts, unsigned int version)
770             {
771 0 0         GIT_INIT_STRUCTURE_FROM_TEMPLATE(
772             opts, version, git_apply_options, GIT_APPLY_OPTIONS_INIT);
773 0           return 0;
774             }
775              
776             /*
777             * Handle the three application options ("locations"):
778             *
779             * GIT_APPLY_LOCATION_WORKDIR: the default, emulates `git apply`.
780             * Applies the diff only to the workdir items and ignores the index
781             * entirely.
782             *
783             * GIT_APPLY_LOCATION_INDEX: emulates `git apply --cached`.
784             * Applies the diff only to the index items and ignores the workdir
785             * completely.
786             *
787             * GIT_APPLY_LOCATION_BOTH: emulates `git apply --index`.
788             * Applies the diff to both the index items and the working directory
789             * items.
790             */
791              
792 0           int git_apply(
793             git_repository *repo,
794             git_diff *diff,
795             git_apply_location_t location,
796             const git_apply_options *given_opts)
797             {
798 0           git_indexwriter indexwriter = GIT_INDEXWRITER_INIT;
799 0           git_index *index = NULL, *preimage = NULL, *postimage = NULL;
800 0           git_reader *pre_reader = NULL, *post_reader = NULL;
801 0           git_apply_options opts = GIT_APPLY_OPTIONS_INIT;
802 0           int error = GIT_EINVALID;
803              
804 0 0         assert(repo && diff);
    0          
805              
806 0 0         GIT_ERROR_CHECK_VERSION(
807             given_opts, GIT_APPLY_OPTIONS_VERSION, "git_apply_options");
808              
809 0 0         if (given_opts)
810 0           memcpy(&opts, given_opts, sizeof(git_apply_options));
811              
812             /*
813             * by default, we apply a patch directly to the working directory;
814             * in `--cached` or `--index` mode, we apply to the contents already
815             * in the index.
816             */
817 0           switch (location) {
818             case GIT_APPLY_LOCATION_BOTH:
819 0           error = git_reader_for_workdir(&pre_reader, repo, true);
820 0           break;
821             case GIT_APPLY_LOCATION_INDEX:
822 0           error = git_reader_for_index(&pre_reader, repo, NULL);
823 0           break;
824             case GIT_APPLY_LOCATION_WORKDIR:
825 0           error = git_reader_for_workdir(&pre_reader, repo, false);
826 0           break;
827             default:
828 0           assert(false);
829             }
830              
831 0 0         if (error < 0)
832 0           goto done;
833              
834             /*
835             * Build the preimage and postimage (differences). Note that
836             * this is not the complete preimage or postimage, it only
837             * contains the files affected by the patch. We want to avoid
838             * having the full repo index, so we will limit our checkout
839             * to only write these files that were affected by the diff.
840             */
841 0 0         if ((error = git_index_new(&preimage)) < 0 ||
    0          
842 0 0         (error = git_index_new(&postimage)) < 0 ||
843 0           (error = git_reader_for_index(&post_reader, repo, postimage)) < 0)
844             goto done;
845              
846 0 0         if (!(opts.flags & GIT_APPLY_CHECK))
847 0 0         if ((error = git_repository_index(&index, repo)) < 0 ||
    0          
848 0           (error = git_indexwriter_init(&indexwriter, index)) < 0)
849             goto done;
850              
851 0 0         if ((error = apply_deltas(repo, pre_reader, preimage, post_reader, postimage, diff, &opts)) < 0)
852 0           goto done;
853              
854 0 0         if ((opts.flags & GIT_APPLY_CHECK))
855 0           goto done;
856              
857 0           switch (location) {
858             case GIT_APPLY_LOCATION_BOTH:
859 0           error = git_apply__to_workdir(repo, diff, preimage, postimage, location, &opts);
860 0           break;
861             case GIT_APPLY_LOCATION_INDEX:
862 0           error = git_apply__to_index(repo, diff, preimage, postimage, &opts);
863 0           break;
864             case GIT_APPLY_LOCATION_WORKDIR:
865 0           error = git_apply__to_workdir(repo, diff, preimage, postimage, location, &opts);
866 0           break;
867             default:
868 0           assert(false);
869             }
870              
871 0 0         if (error < 0)
872 0           goto done;
873              
874 0           error = git_indexwriter_commit(&indexwriter);
875              
876             done:
877 0           git_indexwriter_cleanup(&indexwriter);
878 0           git_index_free(postimage);
879 0           git_index_free(preimage);
880 0           git_index_free(index);
881 0           git_reader_free(pre_reader);
882 0           git_reader_free(post_reader);
883              
884 0           return error;
885             }