]> git.proxmox.com Git - libgit2.git/blob - src/apply.c
Update branching information in d/gbp.conf
[libgit2.git] / src / apply.c
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 static int apply_err(const char *fmt, ...)
35 {
36 va_list ap;
37
38 va_start(ap, fmt);
39 git_error_vset(GIT_ERROR_PATCH, fmt, ap);
40 va_end(ap);
41
42 return GIT_EAPPLYFAIL;
43 }
44
45 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 out->content = in;
52 out->content_len = in_len;
53 out->content_offset = in_offset;
54 }
55
56 #define PATCH_IMAGE_INIT { GIT_POOL_INIT, GIT_VECTOR_INIT }
57
58 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 memset(out, 0x0, sizeof(patch_image));
65
66 git_pool_init(&out->pool, sizeof(git_diff_line));
67
68 for (start = in; start < in + in_len; start = end) {
69 end = memchr(start, '\n', in_len - (start - in));
70
71 if (end == NULL)
72 end = in + in_len;
73
74 else if (end < in + in_len)
75 end++;
76
77 line = git_pool_mallocz(&out->pool, 1);
78 GIT_ERROR_CHECK_ALLOC(line);
79
80 if (git_vector_insert(&out->lines, line) < 0)
81 return -1;
82
83 patch_line_init(line, start, (end - start), (start - in));
84 }
85
86 return 0;
87 }
88
89 static void patch_image_free(patch_image *image)
90 {
91 if (image == NULL)
92 return;
93
94 git_pool_clear(&image->pool);
95 git_vector_free(&image->lines);
96 }
97
98 static bool match_hunk(
99 patch_image *image,
100 patch_image *preimage,
101 size_t linenum)
102 {
103 bool match = 0;
104 size_t i;
105
106 /* Ensure this hunk is within the image boundaries. */
107 if (git_vector_length(&preimage->lines) + linenum >
108 git_vector_length(&image->lines))
109 return 0;
110
111 match = 1;
112
113 /* Check exact match. */
114 for (i = 0; i < git_vector_length(&preimage->lines); i++) {
115 git_diff_line *preimage_line = git_vector_get(&preimage->lines, i);
116 git_diff_line *image_line = git_vector_get(&image->lines, linenum + i);
117
118 if (preimage_line->content_len != image_line->content_len ||
119 memcmp(preimage_line->content, image_line->content, image_line->content_len) != 0) {
120 match = 0;
121 break;
122 }
123 }
124
125 return match;
126 }
127
128 static bool find_hunk_linenum(
129 size_t *out,
130 patch_image *image,
131 patch_image *preimage,
132 size_t linenum)
133 {
134 size_t max = git_vector_length(&image->lines);
135 bool match;
136
137 if (linenum > max)
138 linenum = max;
139
140 match = match_hunk(image, preimage, linenum);
141
142 *out = linenum;
143 return match;
144 }
145
146 static int update_hunk(
147 patch_image *image,
148 size_t linenum,
149 patch_image *preimage,
150 patch_image *postimage)
151 {
152 size_t postlen = git_vector_length(&postimage->lines);
153 size_t prelen = git_vector_length(&preimage->lines);
154 size_t i;
155 int error = 0;
156
157 if (postlen > prelen)
158 error = git_vector_insert_null(
159 &image->lines, linenum, (postlen - prelen));
160 else if (prelen > postlen)
161 error = git_vector_remove_range(
162 &image->lines, linenum, (prelen - postlen));
163
164 if (error) {
165 git_error_set_oom();
166 return -1;
167 }
168
169 for (i = 0; i < git_vector_length(&postimage->lines); i++) {
170 image->lines.contents[linenum + i] =
171 git_vector_get(&postimage->lines, i);
172 }
173
174 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 static int apply_hunk(
184 patch_image *image,
185 git_patch *patch,
186 git_patch_hunk *hunk,
187 apply_hunks_ctx *ctx)
188 {
189 patch_image preimage = PATCH_IMAGE_INIT, postimage = PATCH_IMAGE_INIT;
190 size_t line_num, i;
191 int error = 0;
192
193 if (ctx->opts.hunk_cb) {
194 error = ctx->opts.hunk_cb(&hunk->hunk, ctx->opts.payload);
195
196 if (error) {
197 if (error > 0) {
198 ctx->skipped_new_lines += hunk->hunk.new_lines;
199 ctx->skipped_old_lines += hunk->hunk.old_lines;
200 error = 0;
201 }
202
203 goto done;
204 }
205 }
206
207 for (i = 0; i < hunk->line_count; i++) {
208 size_t linenum = hunk->line_start + i;
209 git_diff_line *line = git_array_get(patch->lines, linenum), *prev;
210
211 if (!line) {
212 error = apply_err("preimage does not contain line %"PRIuZ, linenum);
213 goto done;
214 }
215
216 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 prev = i ? git_array_get(patch->lines, linenum - 1) : NULL;
221 if (prev && prev->content[prev->content_len - 1] == '\n')
222 prev->content_len -= 1;
223 break;
224 case GIT_DIFF_LINE_CONTEXT:
225 if ((error = git_vector_insert(&preimage.lines, line)) < 0 ||
226 (error = git_vector_insert(&postimage.lines, line)) < 0)
227 goto done;
228 break;
229 case GIT_DIFF_LINE_DELETION:
230 if ((error = git_vector_insert(&preimage.lines, line)) < 0)
231 goto done;
232 break;
233 case GIT_DIFF_LINE_ADDITION:
234 if ((error = git_vector_insert(&postimage.lines, line)) < 0)
235 goto done;
236 break;
237 }
238 }
239
240 if (hunk->hunk.new_start) {
241 line_num = hunk->hunk.new_start -
242 ctx->skipped_new_lines +
243 ctx->skipped_old_lines -
244 1;
245 } else {
246 line_num = 0;
247 }
248
249 if (!find_hunk_linenum(&line_num, image, &preimage, line_num)) {
250 error = apply_err("hunk at line %d did not apply",
251 hunk->hunk.new_start);
252 goto done;
253 }
254
255 error = update_hunk(image, line_num, &preimage, &postimage);
256
257 done:
258 patch_image_free(&preimage);
259 patch_image_free(&postimage);
260
261 return error;
262 }
263
264 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 int error = 0;
276
277 if ((error = patch_image_init_fromstr(&image, source, source_len)) < 0)
278 goto done;
279
280 git_array_foreach(patch->hunks, i, hunk) {
281 if ((error = apply_hunk(&image, patch, hunk, ctx)) < 0)
282 goto done;
283 }
284
285 git_vector_foreach(&image.lines, i, line)
286 git_buf_put(out, line->content, line->content_len);
287
288 done:
289 patch_image_free(&image);
290
291 return error;
292 }
293
294 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 git_buf inflated = GIT_BUF_INIT;
301 int error = 0;
302
303 /* no diff means identical contents */
304 if (binary_file->datalen == 0)
305 return git_buf_put(out, source, source_len);
306
307 error = git_zstream_inflatebuf(&inflated,
308 binary_file->data, binary_file->datalen);
309
310 if (!error && inflated.size != binary_file->inflatedlen) {
311 error = apply_err("inflated delta does not match expected length");
312 git_buf_dispose(out);
313 }
314
315 if (error < 0)
316 goto done;
317
318 if (binary_file->type == GIT_DIFF_BINARY_DELTA) {
319 void *data;
320 size_t data_len;
321
322 error = git_delta_apply(&data, &data_len, (void *)source, source_len,
323 (void *)inflated.ptr, inflated.size);
324
325 out->ptr = data;
326 out->size = data_len;
327 out->asize = data_len;
328 }
329 else if (binary_file->type == GIT_DIFF_BINARY_LITERAL) {
330 git_buf_swap(out, &inflated);
331 }
332 else {
333 error = apply_err("unknown binary delta type");
334 goto done;
335 }
336
337 done:
338 git_buf_dispose(&inflated);
339 return error;
340 }
341
342 static int apply_binary(
343 git_buf *out,
344 const char *source,
345 size_t source_len,
346 git_patch *patch)
347 {
348 git_buf reverse = GIT_BUF_INIT;
349 int error = 0;
350
351 if (!patch->binary.contains_data) {
352 error = apply_err("patch does not contain binary data");
353 goto done;
354 }
355
356 if (!patch->binary.old_file.datalen && !patch->binary.new_file.datalen)
357 goto done;
358
359 /* first, apply the new_file delta to the given source */
360 if ((error = apply_binary_delta(out, source, source_len,
361 &patch->binary.new_file)) < 0)
362 goto done;
363
364 /* second, apply the old_file delta to sanity check the result */
365 if ((error = apply_binary_delta(&reverse, out->ptr, out->size,
366 &patch->binary.old_file)) < 0)
367 goto done;
368
369 /* Verify that the resulting file with the reverse patch applied matches the source file */
370 if (source_len != reverse.size ||
371 (source_len && memcmp(source, reverse.ptr, source_len) != 0)) {
372 error = apply_err("binary patch did not apply cleanly");
373 goto done;
374 }
375
376 done:
377 if (error < 0)
378 git_buf_dispose(out);
379
380 git_buf_dispose(&reverse);
381 return error;
382 }
383
384 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 apply_hunks_ctx ctx = { GIT_APPLY_OPTIONS_INIT };
394 char *filename = NULL;
395 unsigned int mode = 0;
396 int error = 0;
397
398 assert(contents_out && filename_out && mode_out && (source || !source_len) && patch);
399
400 if (given_opts)
401 memcpy(&ctx.opts, given_opts, sizeof(git_apply_options));
402
403 *filename_out = NULL;
404 *mode_out = 0;
405
406 if (patch->delta->status != GIT_DELTA_DELETED) {
407 const git_diff_file *newfile = &patch->delta->new_file;
408
409 filename = git__strdup(newfile->path);
410 mode = newfile->mode ?
411 newfile->mode : GIT_FILEMODE_BLOB;
412 }
413
414 if (patch->delta->flags & GIT_DIFF_FLAG_BINARY)
415 error = apply_binary(contents_out, source, source_len, patch);
416 else if (patch->hunks.size)
417 error = apply_hunks(contents_out, source, source_len, patch, &ctx);
418 else
419 error = git_buf_put(contents_out, source, source_len);
420
421 if (error)
422 goto done;
423
424 if (patch->delta->status == GIT_DELTA_DELETED &&
425 git_buf_len(contents_out) > 0) {
426 error = apply_err("removal patch leaves file contents");
427 goto done;
428 }
429
430 *filename_out = filename;
431 *mode_out = mode;
432
433 done:
434 if (error < 0)
435 git__free(filename);
436
437 return error;
438 }
439
440 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 git_patch *patch = NULL;
452 git_buf pre_contents = GIT_BUF_INIT, post_contents = GIT_BUF_INIT;
453 const git_diff_delta *delta;
454 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 bool skip_preimage = false;
460 int error;
461
462 if ((error = git_patch_from_diff(&patch, diff, i)) < 0)
463 goto done;
464
465 delta = git_patch_get_delta(patch);
466
467 if (opts->delta_cb) {
468 error = opts->delta_cb(delta, opts->payload);
469
470 if (error) {
471 if (error > 0)
472 error = 0;
473
474 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 if (delta->status != GIT_DELTA_RENAMED &&
483 delta->status != GIT_DELTA_ADDED) {
484 if (git_strmap_exists(removed_paths, delta->old_file.path)) {
485 error = apply_err("path '%s' has been renamed or deleted", delta->old_file.path);
486 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 if (delta->status != GIT_DELTA_RENAMED) {
498 if ((error = git_reader_read(&pre_contents, &pre_id, &pre_filemode,
499 postimage_reader, delta->old_file.path)) == 0) {
500 skip_preimage = true;
501 } else if (error == GIT_ENOTFOUND) {
502 git_error_clear();
503 error = 0;
504 } else {
505 goto done;
506 }
507 }
508
509 if (!skip_preimage && delta->status != GIT_DELTA_ADDED) {
510 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 if (error == GIT_ENOTFOUND)
515 error = GIT_EAPPLYFAIL;
516
517 /* When applying to BOTH, the index did not match the workdir. */
518 if (error == GIT_READER_MISMATCH)
519 error = apply_err("%s: does not match index", delta->old_file.path);
520
521 if (error < 0)
522 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 if (preimage) {
540 memset(&pre_entry, 0, sizeof(git_index_entry));
541 pre_entry.path = delta->old_file.path;
542 pre_entry.mode = delta->old_file.mode ? delta->old_file.mode : pre_filemode;
543 git_oid_cpy(&pre_entry.id, &pre_id);
544
545 if ((error = git_index_add(preimage, &pre_entry)) < 0)
546 goto done;
547 }
548 }
549
550 if (delta->status != GIT_DELTA_DELETED) {
551 if ((error = git_apply__patch(&post_contents, &filename, &mode,
552 pre_contents.ptr, pre_contents.size, patch, opts)) < 0 ||
553 (error = git_blob_create_from_buffer(&post_id, repo,
554 post_contents.ptr, post_contents.size)) < 0)
555 goto done;
556
557 memset(&post_entry, 0, sizeof(git_index_entry));
558 post_entry.path = filename;
559 post_entry.mode = mode;
560 git_oid_cpy(&post_entry.id, &post_id);
561
562 if ((error = git_index_add(postimage, &post_entry)) < 0)
563 goto done;
564 }
565
566 if (delta->status == GIT_DELTA_RENAMED ||
567 delta->status == GIT_DELTA_DELETED)
568 error = git_strmap_set(removed_paths, delta->old_file.path, (char *) delta->old_file.path);
569
570 if (delta->status == GIT_DELTA_RENAMED ||
571 delta->status == GIT_DELTA_ADDED)
572 git_strmap_delete(removed_paths, delta->new_file.path);
573
574 done:
575 git_buf_dispose(&pre_contents);
576 git_buf_dispose(&post_contents);
577 git__free(filename);
578 git_patch_free(patch);
579
580 return error;
581 }
582
583 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 int error = 0;
595
596 if (git_strmap_new(&removed_paths) < 0)
597 return -1;
598
599 for (i = 0; i < git_diff_num_deltas(diff); i++) {
600 if ((error = apply_one(repo, pre_reader, preimage, post_reader, postimage, diff, removed_paths, i, opts)) < 0)
601 goto done;
602 }
603
604 done:
605 git_strmap_free(removed_paths);
606 return error;
607 }
608
609 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 git_index *postimage = NULL;
617 git_reader *pre_reader = NULL, *post_reader = NULL;
618 git_apply_options opts = GIT_APPLY_OPTIONS_INIT;
619 const git_diff_delta *delta;
620 size_t i;
621 int error = 0;
622
623 assert(out && repo && preimage && diff);
624
625 *out = NULL;
626
627 if (given_opts)
628 memcpy(&opts, given_opts, sizeof(git_apply_options));
629
630 if ((error = git_reader_for_tree(&pre_reader, preimage)) < 0)
631 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 if ((error = git_index_new(&postimage)) < 0 ||
638 (error = git_index_read_tree(postimage, preimage)) < 0 ||
639 (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 for (i = 0; i < git_diff_num_deltas(diff); i++) {
648 delta = git_diff_get_delta(diff, i);
649
650 if (delta->status == GIT_DELTA_DELETED ||
651 delta->status == GIT_DELTA_RENAMED) {
652 if ((error = git_index_remove(postimage,
653 delta->old_file.path, 0)) < 0)
654 goto done;
655 }
656 }
657
658 if ((error = apply_deltas(repo, pre_reader, NULL, post_reader, postimage, diff, &opts)) < 0)
659 goto done;
660
661 *out = postimage;
662
663 done:
664 if (error < 0)
665 git_index_free(postimage);
666
667 git_reader_free(pre_reader);
668 git_reader_free(post_reader);
669
670 return error;
671 }
672
673 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 git_vector paths = GIT_VECTOR_INIT;
682 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 if ((error = git_vector_init(&paths, git_diff_num_deltas(diff), NULL)) < 0)
694 goto done;
695
696 for (i = 0; i < git_diff_num_deltas(diff); i++) {
697 delta = git_diff_get_delta(diff, i);
698
699 if ((error = git_vector_insert(&paths, (void *)delta->old_file.path)) < 0)
700 goto done;
701
702 if (strcmp(delta->old_file.path, delta->new_file.path) &&
703 (error = git_vector_insert(&paths, (void *)delta->new_file.path)) < 0)
704 goto done;
705 }
706
707 checkout_opts.checkout_strategy |= GIT_CHECKOUT_SAFE;
708 checkout_opts.checkout_strategy |= GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH;
709 checkout_opts.checkout_strategy |= GIT_CHECKOUT_DONT_WRITE_INDEX;
710
711 if (location == GIT_APPLY_LOCATION_WORKDIR)
712 checkout_opts.checkout_strategy |= GIT_CHECKOUT_DONT_UPDATE_INDEX;
713
714 checkout_opts.paths.strings = (char **)paths.contents;
715 checkout_opts.paths.count = paths.length;
716
717 checkout_opts.baseline_index = preimage;
718
719 error = git_checkout_index(repo, postimage, &checkout_opts);
720
721 done:
722 git_vector_free(&paths);
723 return error;
724 }
725
726 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 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 if ((error = git_repository_index(&index, repo)) < 0)
743 goto done;
744
745 /* Remove deleted (or renamed) paths from the index. */
746 for (i = 0; i < git_diff_num_deltas(diff); i++) {
747 delta = git_diff_get_delta(diff, i);
748
749 if (delta->status == GIT_DELTA_DELETED ||
750 delta->status == GIT_DELTA_RENAMED) {
751 if ((error = git_index_remove(index, delta->old_file.path, 0)) < 0)
752 goto done;
753 }
754 }
755
756 /* Then add the changes back to the index. */
757 for (i = 0; i < git_index_entrycount(postimage); i++) {
758 entry = git_index_get_byindex(postimage, i);
759
760 if ((error = git_index_add(index, entry)) < 0)
761 goto done;
762 }
763
764 done:
765 git_index_free(index);
766 return error;
767 }
768
769 int git_apply_options_init(git_apply_options *opts, unsigned int version)
770 {
771 GIT_INIT_STRUCTURE_FROM_TEMPLATE(
772 opts, version, git_apply_options, GIT_APPLY_OPTIONS_INIT);
773 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 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 git_indexwriter indexwriter = GIT_INDEXWRITER_INIT;
799 git_index *index = NULL, *preimage = NULL, *postimage = NULL;
800 git_reader *pre_reader = NULL, *post_reader = NULL;
801 git_apply_options opts = GIT_APPLY_OPTIONS_INIT;
802 int error = GIT_EINVALID;
803
804 assert(repo && diff);
805
806 GIT_ERROR_CHECK_VERSION(
807 given_opts, GIT_APPLY_OPTIONS_VERSION, "git_apply_options");
808
809 if (given_opts)
810 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 switch (location) {
818 case GIT_APPLY_LOCATION_BOTH:
819 error = git_reader_for_workdir(&pre_reader, repo, true);
820 break;
821 case GIT_APPLY_LOCATION_INDEX:
822 error = git_reader_for_index(&pre_reader, repo, NULL);
823 break;
824 case GIT_APPLY_LOCATION_WORKDIR:
825 error = git_reader_for_workdir(&pre_reader, repo, false);
826 break;
827 default:
828 assert(false);
829 }
830
831 if (error < 0)
832 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 if ((error = git_index_new(&preimage)) < 0 ||
842 (error = git_index_new(&postimage)) < 0 ||
843 (error = git_reader_for_index(&post_reader, repo, postimage)) < 0)
844 goto done;
845
846 if (!(opts.flags & GIT_APPLY_CHECK))
847 if ((error = git_repository_index(&index, repo)) < 0 ||
848 (error = git_indexwriter_init(&indexwriter, index)) < 0)
849 goto done;
850
851 if ((error = apply_deltas(repo, pre_reader, preimage, post_reader, postimage, diff, &opts)) < 0)
852 goto done;
853
854 if ((opts.flags & GIT_APPLY_CHECK))
855 goto done;
856
857 switch (location) {
858 case GIT_APPLY_LOCATION_BOTH:
859 error = git_apply__to_workdir(repo, diff, preimage, postimage, location, &opts);
860 break;
861 case GIT_APPLY_LOCATION_INDEX:
862 error = git_apply__to_index(repo, diff, preimage, postimage, &opts);
863 break;
864 case GIT_APPLY_LOCATION_WORKDIR:
865 error = git_apply__to_workdir(repo, diff, preimage, postimage, location, &opts);
866 break;
867 default:
868 assert(false);
869 }
870
871 if (error < 0)
872 goto done;
873
874 error = git_indexwriter_commit(&indexwriter);
875
876 done:
877 git_indexwriter_cleanup(&indexwriter);
878 git_index_free(postimage);
879 git_index_free(preimage);
880 git_index_free(index);
881 git_reader_free(pre_reader);
882 git_reader_free(post_reader);
883
884 return error;
885 }