File Coverage

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