File Coverage

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