]> git.proxmox.com Git - libgit2.git/blame - src/apply.c
Refresh patches
[libgit2.git] / src / apply.c
CommitLineData
7cb904ba
ET
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
ac3d33df 8#include "git2/apply.h"
7cb904ba
ET
9#include "git2/patch.h"
10#include "git2/filter.h"
ac3d33df
JK
11#include "git2/blob.h"
12#include "git2/index.h"
13#include "git2/checkout.h"
14#include "git2/repository.h"
7cb904ba 15#include "array.h"
804d5fe9 16#include "patch.h"
22a2d3d5 17#include "futils.h"
3149ff6f
ET
18#include "delta.h"
19#include "zstream.h"
ac3d33df
JK
20#include "reader.h"
21#include "index.h"
c25aa7cd 22#include "apply.h"
7cb904ba 23
7cb904ba
ET
24typedef 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
22a2d3d5
UG
32static int apply_err(const char *fmt, ...) GIT_FORMAT_PRINTF(1, 2);
33static int apply_err(const char *fmt, ...)
34{
35 va_list ap;
36
37 va_start(ap, fmt);
38 git_error_vset(GIT_ERROR_PATCH, fmt, ap);
39 va_end(ap);
40
41 return GIT_EAPPLYFAIL;
42}
43
7cb904ba
ET
44static 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 out->content = in;
51 out->content_len = in_len;
52 out->content_offset = in_offset;
53}
54
274a727e 55#define PATCH_IMAGE_INIT { GIT_POOL_INIT, GIT_VECTOR_INIT }
7cb904ba
ET
56
57static 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 memset(out, 0x0, sizeof(patch_image));
64
22a2d3d5
UG
65 if (git_pool_init(&out->pool, sizeof(git_diff_line)) < 0)
66 return -1;
67
68 if (!in_len)
69 return 0;
7cb904ba
ET
70
71 for (start = in; start < in + in_len; start = end) {
22a2d3d5 72 end = memchr(start, '\n', in_len - (start - in));
7cb904ba 73
581a4d39
ET
74 if (end == NULL)
75 end = in + in_len;
76
77 else if (end < in + in_len)
7cb904ba
ET
78 end++;
79
80 line = git_pool_mallocz(&out->pool, 1);
ac3d33df 81 GIT_ERROR_CHECK_ALLOC(line);
7cb904ba
ET
82
83 if (git_vector_insert(&out->lines, line) < 0)
84 return -1;
85
86 patch_line_init(line, start, (end - start), (start - in));
87 }
88
89 return 0;
90}
91
92static void patch_image_free(patch_image *image)
93{
94 if (image == NULL)
95 return;
96
97 git_pool_clear(&image->pool);
98 git_vector_free(&image->lines);
99}
100
101static bool match_hunk(
102 patch_image *image,
103 patch_image *preimage,
104 size_t linenum)
105{
106 bool match = 0;
107 size_t i;
108
109 /* Ensure this hunk is within the image boundaries. */
110 if (git_vector_length(&preimage->lines) + linenum >
111 git_vector_length(&image->lines))
112 return 0;
113
114 match = 1;
115
116 /* Check exact match. */
117 for (i = 0; i < git_vector_length(&preimage->lines); i++) {
118 git_diff_line *preimage_line = git_vector_get(&preimage->lines, i);
119 git_diff_line *image_line = git_vector_get(&image->lines, linenum + i);
120
531be3e8 121 if (preimage_line->content_len != image_line->content_len ||
7cb904ba
ET
122 memcmp(preimage_line->content, image_line->content, image_line->content_len) != 0) {
123 match = 0;
124 break;
125 }
126 }
127
128 return match;
129}
130
131static bool find_hunk_linenum(
132 size_t *out,
133 patch_image *image,
134 patch_image *preimage,
135 size_t linenum)
136{
137 size_t max = git_vector_length(&image->lines);
138 bool match;
139
140 if (linenum > max)
141 linenum = max;
142
143 match = match_hunk(image, preimage, linenum);
144
145 *out = linenum;
146 return match;
147}
148
149static int update_hunk(
150 patch_image *image,
ac3d33df 151 size_t linenum,
7cb904ba
ET
152 patch_image *preimage,
153 patch_image *postimage)
154{
155 size_t postlen = git_vector_length(&postimage->lines);
156 size_t prelen = git_vector_length(&preimage->lines);
157 size_t i;
158 int error = 0;
159
160 if (postlen > prelen)
53571f2f 161 error = git_vector_insert_null(
7cb904ba
ET
162 &image->lines, linenum, (postlen - prelen));
163 else if (prelen > postlen)
53571f2f 164 error = git_vector_remove_range(
7cb904ba
ET
165 &image->lines, linenum, (prelen - postlen));
166
167 if (error) {
ac3d33df 168 git_error_set_oom();
7cb904ba
ET
169 return -1;
170 }
171
172 for (i = 0; i < git_vector_length(&postimage->lines); i++) {
173 image->lines.contents[linenum + i] =
174 git_vector_get(&postimage->lines, i);
175 }
176
177 return 0;
178}
179
ac3d33df
JK
180typedef struct {
181 git_apply_options opts;
182 size_t skipped_new_lines;
183 size_t skipped_old_lines;
184} apply_hunks_ctx;
185
7cb904ba
ET
186static int apply_hunk(
187 patch_image *image,
188 git_patch *patch,
ac3d33df
JK
189 git_patch_hunk *hunk,
190 apply_hunks_ctx *ctx)
7cb904ba 191{
0267c34c 192 patch_image preimage = PATCH_IMAGE_INIT, postimage = PATCH_IMAGE_INIT;
7cb904ba
ET
193 size_t line_num, i;
194 int error = 0;
195
ac3d33df
JK
196 if (ctx->opts.hunk_cb) {
197 error = ctx->opts.hunk_cb(&hunk->hunk, ctx->opts.payload);
198
199 if (error) {
200 if (error > 0) {
201 ctx->skipped_new_lines += hunk->hunk.new_lines;
202 ctx->skipped_old_lines += hunk->hunk.old_lines;
203 error = 0;
204 }
205
206 goto done;
207 }
208 }
209
7cb904ba
ET
210 for (i = 0; i < hunk->line_count; i++) {
211 size_t linenum = hunk->line_start + i;
22a2d3d5 212 git_diff_line *line = git_array_get(patch->lines, linenum), *prev;
7cb904ba
ET
213
214 if (!line) {
909d5494 215 error = apply_err("preimage does not contain line %"PRIuZ, linenum);
7cb904ba
ET
216 goto done;
217 }
218
22a2d3d5
UG
219 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 prev = i ? git_array_get(patch->lines, linenum - 1) : NULL;
224 if (prev && prev->content[prev->content_len - 1] == '\n')
225 prev->content_len -= 1;
226 break;
227 case GIT_DIFF_LINE_CONTEXT:
228 if ((error = git_vector_insert(&preimage.lines, line)) < 0 ||
229 (error = git_vector_insert(&postimage.lines, line)) < 0)
230 goto done;
231 break;
232 case GIT_DIFF_LINE_DELETION:
233 if ((error = git_vector_insert(&preimage.lines, line)) < 0)
234 goto done;
235 break;
236 case GIT_DIFF_LINE_ADDITION:
237 if ((error = git_vector_insert(&postimage.lines, line)) < 0)
238 goto done;
239 break;
7cb904ba
ET
240 }
241 }
242
ac3d33df
JK
243 if (hunk->hunk.new_start) {
244 line_num = hunk->hunk.new_start -
245 ctx->skipped_new_lines +
246 ctx->skipped_old_lines -
247 1;
248 } else {
249 line_num = 0;
250 }
7cb904ba
ET
251
252 if (!find_hunk_linenum(&line_num, image, &preimage, line_num)) {
909d5494 253 error = apply_err("hunk at line %d did not apply",
7cb904ba
ET
254 hunk->hunk.new_start);
255 goto done;
256 }
257
258 error = update_hunk(image, line_num, &preimage, &postimage);
259
260done:
261 patch_image_free(&preimage);
262 patch_image_free(&postimage);
263
264 return error;
265}
266
267static int apply_hunks(
268 git_buf *out,
269 const char *source,
270 size_t source_len,
ac3d33df
JK
271 git_patch *patch,
272 apply_hunks_ctx *ctx)
7cb904ba 273{
804d5fe9 274 git_patch_hunk *hunk;
7cb904ba
ET
275 git_diff_line *line;
276 patch_image image;
277 size_t i;
278 int error = 0;
279
280 if ((error = patch_image_init_fromstr(&image, source, source_len)) < 0)
281 goto done;
282
283 git_array_foreach(patch->hunks, i, hunk) {
ac3d33df 284 if ((error = apply_hunk(&image, patch, hunk, ctx)) < 0)
7cb904ba
ET
285 goto done;
286 }
287
288 git_vector_foreach(&image.lines, i, line)
289 git_buf_put(out, line->content, line->content_len);
290
291done:
292 patch_image_free(&image);
293
294 return error;
295}
296
3149ff6f
ET
297static int apply_binary_delta(
298 git_buf *out,
299 const char *source,
300 size_t source_len,
301 git_diff_binary_file *binary_file)
302{
303 git_buf inflated = GIT_BUF_INIT;
304 int error = 0;
305
306 /* no diff means identical contents */
307 if (binary_file->datalen == 0)
308 return git_buf_put(out, source, source_len);
309
310 error = git_zstream_inflatebuf(&inflated,
311 binary_file->data, binary_file->datalen);
312
313 if (!error && inflated.size != binary_file->inflatedlen) {
314 error = apply_err("inflated delta does not match expected length");
ac3d33df 315 git_buf_dispose(out);
3149ff6f
ET
316 }
317
318 if (error < 0)
319 goto done;
320
321 if (binary_file->type == GIT_DIFF_BINARY_DELTA) {
322 void *data;
323 size_t data_len;
324
325 error = git_delta_apply(&data, &data_len, (void *)source, source_len,
326 (void *)inflated.ptr, inflated.size);
327
328 out->ptr = data;
329 out->size = data_len;
330 out->asize = data_len;
331 }
332 else if (binary_file->type == GIT_DIFF_BINARY_LITERAL) {
333 git_buf_swap(out, &inflated);
334 }
335 else {
336 error = apply_err("unknown binary delta type");
337 goto done;
338 }
339
340done:
ac3d33df 341 git_buf_dispose(&inflated);
3149ff6f
ET
342 return error;
343}
344
345static int apply_binary(
346 git_buf *out,
347 const char *source,
348 size_t source_len,
349 git_patch *patch)
350{
351 git_buf reverse = GIT_BUF_INIT;
adedac5a
ET
352 int error = 0;
353
354 if (!patch->binary.contains_data) {
355 error = apply_err("patch does not contain binary data");
356 goto done;
357 }
358
359 if (!patch->binary.old_file.datalen && !patch->binary.new_file.datalen)
360 goto done;
3149ff6f
ET
361
362 /* first, apply the new_file delta to the given source */
363 if ((error = apply_binary_delta(out, source, source_len,
364 &patch->binary.new_file)) < 0)
365 goto done;
366
367 /* second, apply the old_file delta to sanity check the result */
368 if ((error = apply_binary_delta(&reverse, out->ptr, out->size,
369 &patch->binary.old_file)) < 0)
370 goto done;
371
ac3d33df 372 /* Verify that the resulting file with the reverse patch applied matches the source file */
3149ff6f 373 if (source_len != reverse.size ||
ac3d33df 374 (source_len && memcmp(source, reverse.ptr, source_len) != 0)) {
3149ff6f
ET
375 error = apply_err("binary patch did not apply cleanly");
376 goto done;
377 }
378
379done:
380 if (error < 0)
ac3d33df 381 git_buf_dispose(out);
3149ff6f 382
ac3d33df 383 git_buf_dispose(&reverse);
3149ff6f
ET
384 return error;
385}
386
7cb904ba
ET
387int git_apply__patch(
388 git_buf *contents_out,
389 char **filename_out,
390 unsigned int *mode_out,
391 const char *source,
392 size_t source_len,
ac3d33df
JK
393 git_patch *patch,
394 const git_apply_options *given_opts)
7cb904ba 395{
ac3d33df 396 apply_hunks_ctx ctx = { GIT_APPLY_OPTIONS_INIT };
7cb904ba
ET
397 char *filename = NULL;
398 unsigned int mode = 0;
399 int error = 0;
400
c25aa7cd
PP
401 GIT_ASSERT_ARG(contents_out);
402 GIT_ASSERT_ARG(filename_out);
403 GIT_ASSERT_ARG(mode_out);
404 GIT_ASSERT_ARG(source || !source_len);
405 GIT_ASSERT_ARG(patch);
7cb904ba 406
ac3d33df
JK
407 if (given_opts)
408 memcpy(&ctx.opts, given_opts, sizeof(git_apply_options));
409
7cb904ba
ET
410 *filename_out = NULL;
411 *mode_out = 0;
412
413 if (patch->delta->status != GIT_DELTA_DELETED) {
b85bd8ce 414 const git_diff_file *newfile = &patch->delta->new_file;
804d5fe9
ET
415
416 filename = git__strdup(newfile->path);
417 mode = newfile->mode ?
418 newfile->mode : GIT_FILEMODE_BLOB;
7cb904ba
ET
419 }
420
3149ff6f
ET
421 if (patch->delta->flags & GIT_DIFF_FLAG_BINARY)
422 error = apply_binary(contents_out, source, source_len, patch);
423 else if (patch->hunks.size)
ac3d33df 424 error = apply_hunks(contents_out, source, source_len, patch, &ctx);
3149ff6f
ET
425 else
426 error = git_buf_put(contents_out, source, source_len);
427
428 if (error)
7cb904ba
ET
429 goto done;
430
431 if (patch->delta->status == GIT_DELTA_DELETED &&
432 git_buf_len(contents_out) > 0) {
433 error = apply_err("removal patch leaves file contents");
434 goto done;
435 }
436
437 *filename_out = filename;
438 *mode_out = mode;
439
440done:
441 if (error < 0)
442 git__free(filename);
443
444 return error;
445}
ac3d33df
JK
446
447static 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 git_patch *patch = NULL;
459 git_buf pre_contents = GIT_BUF_INIT, post_contents = GIT_BUF_INIT;
460 const git_diff_delta *delta;
461 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 bool skip_preimage = false;
ac3d33df
JK
467 int error;
468
469 if ((error = git_patch_from_diff(&patch, diff, i)) < 0)
470 goto done;
471
472 delta = git_patch_get_delta(patch);
473
474 if (opts->delta_cb) {
475 error = opts->delta_cb(delta, opts->payload);
476
477 if (error) {
478 if (error > 0)
479 error = 0;
480
481 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 if (delta->status != GIT_DELTA_RENAMED &&
490 delta->status != GIT_DELTA_ADDED) {
22a2d3d5 491 if (git_strmap_exists(removed_paths, delta->old_file.path)) {
ac3d33df
JK
492 error = apply_err("path '%s' has been renamed or deleted", delta->old_file.path);
493 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 if (delta->status != GIT_DELTA_RENAMED) {
505 if ((error = git_reader_read(&pre_contents, &pre_id, &pre_filemode,
506 postimage_reader, delta->old_file.path)) == 0) {
507 skip_preimage = true;
508 } else if (error == GIT_ENOTFOUND) {
509 git_error_clear();
510 error = 0;
511 } else {
512 goto done;
513 }
514 }
515
516 if (!skip_preimage && delta->status != GIT_DELTA_ADDED) {
517 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 if (error == GIT_ENOTFOUND)
522 error = GIT_EAPPLYFAIL;
523
524 /* When applying to BOTH, the index did not match the workdir. */
525 if (error == GIT_READER_MISMATCH)
526 error = apply_err("%s: does not match index", delta->old_file.path);
527
528 if (error < 0)
529 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 if (preimage) {
547 memset(&pre_entry, 0, sizeof(git_index_entry));
548 pre_entry.path = delta->old_file.path;
549 pre_entry.mode = delta->old_file.mode ? delta->old_file.mode : pre_filemode;
550 git_oid_cpy(&pre_entry.id, &pre_id);
551
552 if ((error = git_index_add(preimage, &pre_entry)) < 0)
553 goto done;
554 }
555 }
556
557 if (delta->status != GIT_DELTA_DELETED) {
558 if ((error = git_apply__patch(&post_contents, &filename, &mode,
559 pre_contents.ptr, pre_contents.size, patch, opts)) < 0 ||
22a2d3d5 560 (error = git_blob_create_from_buffer(&post_id, repo,
ac3d33df
JK
561 post_contents.ptr, post_contents.size)) < 0)
562 goto done;
563
564 memset(&post_entry, 0, sizeof(git_index_entry));
565 post_entry.path = filename;
566 post_entry.mode = mode;
567 git_oid_cpy(&post_entry.id, &post_id);
568
569 if ((error = git_index_add(postimage, &post_entry)) < 0)
570 goto done;
571 }
572
573 if (delta->status == GIT_DELTA_RENAMED ||
574 delta->status == GIT_DELTA_DELETED)
22a2d3d5 575 error = git_strmap_set(removed_paths, delta->old_file.path, (char *) delta->old_file.path);
ac3d33df
JK
576
577 if (delta->status == GIT_DELTA_RENAMED ||
578 delta->status == GIT_DELTA_ADDED)
579 git_strmap_delete(removed_paths, delta->new_file.path);
580
581done:
582 git_buf_dispose(&pre_contents);
583 git_buf_dispose(&post_contents);
584 git__free(filename);
585 git_patch_free(patch);
586
587 return error;
588}
589
590static 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 int error = 0;
602
22a2d3d5 603 if (git_strmap_new(&removed_paths) < 0)
ac3d33df
JK
604 return -1;
605
606 for (i = 0; i < git_diff_num_deltas(diff); i++) {
607 if ((error = apply_one(repo, pre_reader, preimage, post_reader, postimage, diff, removed_paths, i, opts)) < 0)
608 goto done;
609 }
610
611done:
612 git_strmap_free(removed_paths);
613 return error;
614}
615
616int 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 git_index *postimage = NULL;
624 git_reader *pre_reader = NULL, *post_reader = NULL;
625 git_apply_options opts = GIT_APPLY_OPTIONS_INIT;
626 const git_diff_delta *delta;
627 size_t i;
628 int error = 0;
629
c25aa7cd
PP
630 GIT_ASSERT_ARG(out);
631 GIT_ASSERT_ARG(repo);
632 GIT_ASSERT_ARG(preimage);
633 GIT_ASSERT_ARG(diff);
ac3d33df
JK
634
635 *out = NULL;
636
637 if (given_opts)
638 memcpy(&opts, given_opts, sizeof(git_apply_options));
639
640 if ((error = git_reader_for_tree(&pre_reader, preimage)) < 0)
641 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 if ((error = git_index_new(&postimage)) < 0 ||
648 (error = git_index_read_tree(postimage, preimage)) < 0 ||
649 (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 for (i = 0; i < git_diff_num_deltas(diff); i++) {
658 delta = git_diff_get_delta(diff, i);
659
22a2d3d5
UG
660 if (delta->status == GIT_DELTA_DELETED ||
661 delta->status == GIT_DELTA_RENAMED) {
662 if ((error = git_index_remove(postimage,
663 delta->old_file.path, 0)) < 0)
664 goto done;
665 }
ac3d33df
JK
666 }
667
668 if ((error = apply_deltas(repo, pre_reader, NULL, post_reader, postimage, diff, &opts)) < 0)
669 goto done;
670
671 *out = postimage;
672
673done:
674 if (error < 0)
675 git_index_free(postimage);
676
677 git_reader_free(pre_reader);
678 git_reader_free(post_reader);
679
680 return error;
681}
682
683static 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 git_vector paths = GIT_VECTOR_INIT;
692 git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
693 const git_diff_delta *delta;
694 size_t i;
695 int error;
696
697 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 if ((error = git_vector_init(&paths, git_diff_num_deltas(diff), NULL)) < 0)
704 goto done;
705
706 for (i = 0; i < git_diff_num_deltas(diff); i++) {
707 delta = git_diff_get_delta(diff, i);
708
709 if ((error = git_vector_insert(&paths, (void *)delta->old_file.path)) < 0)
710 goto done;
711
712 if (strcmp(delta->old_file.path, delta->new_file.path) &&
713 (error = git_vector_insert(&paths, (void *)delta->new_file.path)) < 0)
714 goto done;
715 }
716
717 checkout_opts.checkout_strategy |= GIT_CHECKOUT_SAFE;
718 checkout_opts.checkout_strategy |= GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH;
719 checkout_opts.checkout_strategy |= GIT_CHECKOUT_DONT_WRITE_INDEX;
720
721 if (location == GIT_APPLY_LOCATION_WORKDIR)
722 checkout_opts.checkout_strategy |= GIT_CHECKOUT_DONT_UPDATE_INDEX;
723
724 checkout_opts.paths.strings = (char **)paths.contents;
725 checkout_opts.paths.count = paths.length;
726
727 checkout_opts.baseline_index = preimage;
728
729 error = git_checkout_index(repo, postimage, &checkout_opts);
730
731done:
732 git_vector_free(&paths);
733 return error;
734}
735
736static 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 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 GIT_UNUSED(preimage);
750 GIT_UNUSED(opts);
751
752 if ((error = git_repository_index(&index, repo)) < 0)
753 goto done;
754
755 /* Remove deleted (or renamed) paths from the index. */
756 for (i = 0; i < git_diff_num_deltas(diff); i++) {
757 delta = git_diff_get_delta(diff, i);
758
759 if (delta->status == GIT_DELTA_DELETED ||
760 delta->status == GIT_DELTA_RENAMED) {
761 if ((error = git_index_remove(index, delta->old_file.path, 0)) < 0)
762 goto done;
763 }
764 }
765
766 /* Then add the changes back to the index. */
767 for (i = 0; i < git_index_entrycount(postimage); i++) {
768 entry = git_index_get_byindex(postimage, i);
769
770 if ((error = git_index_add(index, entry)) < 0)
771 goto done;
772 }
773
774done:
775 git_index_free(index);
776 return error;
777}
778
22a2d3d5
UG
779int git_apply_options_init(git_apply_options *opts, unsigned int version)
780{
c25aa7cd
PP
781 GIT_ASSERT_ARG(opts);
782
22a2d3d5
UG
783 GIT_INIT_STRUCTURE_FROM_TEMPLATE(
784 opts, version, git_apply_options, GIT_APPLY_OPTIONS_INIT);
785 return 0;
786}
787
ac3d33df
JK
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
804int 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 git_indexwriter indexwriter = GIT_INDEXWRITER_INIT;
811 git_index *index = NULL, *preimage = NULL, *postimage = NULL;
812 git_reader *pre_reader = NULL, *post_reader = NULL;
813 git_apply_options opts = GIT_APPLY_OPTIONS_INIT;
814 int error = GIT_EINVALID;
815
c25aa7cd
PP
816 GIT_ASSERT_ARG(repo);
817 GIT_ASSERT_ARG(diff);
ac3d33df
JK
818
819 GIT_ERROR_CHECK_VERSION(
820 given_opts, GIT_APPLY_OPTIONS_VERSION, "git_apply_options");
821
822 if (given_opts)
823 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 switch (location) {
831 case GIT_APPLY_LOCATION_BOTH:
832 error = git_reader_for_workdir(&pre_reader, repo, true);
833 break;
834 case GIT_APPLY_LOCATION_INDEX:
835 error = git_reader_for_index(&pre_reader, repo, NULL);
836 break;
837 case GIT_APPLY_LOCATION_WORKDIR:
838 error = git_reader_for_workdir(&pre_reader, repo, false);
839 break;
840 default:
c25aa7cd 841 GIT_ASSERT(false);
ac3d33df
JK
842 }
843
844 if (error < 0)
845 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 if ((error = git_index_new(&preimage)) < 0 ||
855 (error = git_index_new(&postimage)) < 0 ||
856 (error = git_reader_for_index(&post_reader, repo, postimage)) < 0)
857 goto done;
858
22a2d3d5
UG
859 if (!(opts.flags & GIT_APPLY_CHECK))
860 if ((error = git_repository_index(&index, repo)) < 0 ||
861 (error = git_indexwriter_init(&indexwriter, index)) < 0)
862 goto done;
ac3d33df
JK
863
864 if ((error = apply_deltas(repo, pre_reader, preimage, post_reader, postimage, diff, &opts)) < 0)
865 goto done;
866
22a2d3d5
UG
867 if ((opts.flags & GIT_APPLY_CHECK))
868 goto done;
869
ac3d33df
JK
870 switch (location) {
871 case GIT_APPLY_LOCATION_BOTH:
872 error = git_apply__to_workdir(repo, diff, preimage, postimage, location, &opts);
873 break;
874 case GIT_APPLY_LOCATION_INDEX:
875 error = git_apply__to_index(repo, diff, preimage, postimage, &opts);
876 break;
877 case GIT_APPLY_LOCATION_WORKDIR:
878 error = git_apply__to_workdir(repo, diff, preimage, postimage, location, &opts);
879 break;
880 default:
c25aa7cd 881 GIT_ASSERT(false);
ac3d33df
JK
882 }
883
884 if (error < 0)
885 goto done;
886
887 error = git_indexwriter_commit(&indexwriter);
888
889done:
890 git_indexwriter_cleanup(&indexwriter);
891 git_index_free(postimage);
892 git_index_free(preimage);
893 git_index_free(index);
894 git_reader_free(pre_reader);
895 git_reader_free(post_reader);
896
897 return error;
898}