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