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