1 /*
2  * Copyright (C) the libgit2 contributors. All rights reserved.
3  *
4  * This file is part of libgit2, distributed under the GNU GPL v2 with
5  * a Linking Exception. For full terms see the included COPYING file.
6  */
7 
8 #include "git2/apply.h"
9 #include "git2/patch.h"
10 #include "git2/filter.h"
11 #include "git2/blob.h"
12 #include "git2/index.h"
13 #include "git2/checkout.h"
14 #include "git2/repository.h"
15 #include "array.h"
16 #include "patch.h"
17 #include "futils.h"
18 #include "delta.h"
19 #include "zstream.h"
20 #include "reader.h"
21 #include "index.h"
22 #include "apply.h"
23 
24 typedef struct {
25 	/* The lines that we allocate ourself are allocated out of the pool.
26 	 * (Lines may have been allocated out of the diff.)
27 	 */
28 	git_pool pool;
29 	git_vector lines;
30 } patch_image;
31 
32 static int apply_err(const char *fmt, ...) GIT_FORMAT_PRINTF(1, 2);
apply_err(const char * fmt,...)33 static 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 
patch_line_init(git_diff_line * out,const char * in,size_t in_len,size_t in_offset)44 static void patch_line_init(
45 	git_diff_line *out,
46 	const char *in,
47 	size_t in_len,
48 	size_t in_offset)
49 {
50 	out->content = in;
51 	out->content_len = in_len;
52 	out->content_offset = in_offset;
53 }
54 
55 #define PATCH_IMAGE_INIT { GIT_POOL_INIT, GIT_VECTOR_INIT }
56 
patch_image_init_fromstr(patch_image * out,const char * in,size_t in_len)57 static int patch_image_init_fromstr(
58 	patch_image *out, const char *in, size_t in_len)
59 {
60 	git_diff_line *line;
61 	const char *start, *end;
62 
63 	memset(out, 0x0, sizeof(patch_image));
64 
65 	if (git_pool_init(&out->pool, sizeof(git_diff_line)) < 0)
66 		return -1;
67 
68 	if (!in_len)
69 		return 0;
70 
71 	for (start = in; start < in + in_len; start = end) {
72 		end = memchr(start, '\n', in_len - (start - in));
73 
74 		if (end == NULL)
75 			end = in + in_len;
76 
77 		else if (end < in + in_len)
78 			end++;
79 
80 		line = git_pool_mallocz(&out->pool, 1);
81 		GIT_ERROR_CHECK_ALLOC(line);
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 
patch_image_free(patch_image * image)92 static 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 
match_hunk(patch_image * image,patch_image * preimage,size_t linenum)101 static 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 
121 		if (preimage_line->content_len != image_line->content_len ||
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 
find_hunk_linenum(size_t * out,patch_image * image,patch_image * preimage,size_t linenum)131 static 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 
update_hunk(patch_image * image,size_t linenum,patch_image * preimage,patch_image * postimage)149 static int update_hunk(
150 	patch_image *image,
151 	size_t linenum,
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)
161 		error = git_vector_insert_null(
162 			&image->lines, linenum, (postlen - prelen));
163 	else if (prelen > postlen)
164 		error = git_vector_remove_range(
165 			&image->lines, linenum, (prelen - postlen));
166 
167 	if (error) {
168 		git_error_set_oom();
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 
180 typedef struct {
181 	git_apply_options opts;
182 	size_t skipped_new_lines;
183 	size_t skipped_old_lines;
184 } apply_hunks_ctx;
185 
apply_hunk(patch_image * image,git_patch * patch,git_patch_hunk * hunk,apply_hunks_ctx * ctx)186 static int apply_hunk(
187 	patch_image *image,
188 	git_patch *patch,
189 	git_patch_hunk *hunk,
190 	apply_hunks_ctx *ctx)
191 {
192 	patch_image preimage = PATCH_IMAGE_INIT, postimage = PATCH_IMAGE_INIT;
193 	size_t line_num, i;
194 	int error = 0;
195 
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 
210 	for (i = 0; i < hunk->line_count; i++) {
211 		size_t linenum = hunk->line_start + i;
212 		git_diff_line *line = git_array_get(patch->lines, linenum), *prev;
213 
214 		if (!line) {
215 			error = apply_err("preimage does not contain line %"PRIuZ, linenum);
216 			goto done;
217 		}
218 
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;
240 		}
241 	}
242 
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 	}
251 
252 	if (!find_hunk_linenum(&line_num, image, &preimage, line_num)) {
253 		error = apply_err("hunk at line %d did not apply",
254 			hunk->hunk.new_start);
255 		goto done;
256 	}
257 
258 	error = update_hunk(image, line_num, &preimage, &postimage);
259 
260 done:
261 	patch_image_free(&preimage);
262 	patch_image_free(&postimage);
263 
264 	return error;
265 }
266 
apply_hunks(git_buf * out,const char * source,size_t source_len,git_patch * patch,apply_hunks_ctx * ctx)267 static int apply_hunks(
268 	git_buf *out,
269 	const char *source,
270 	size_t source_len,
271 	git_patch *patch,
272 	apply_hunks_ctx *ctx)
273 {
274 	git_patch_hunk *hunk;
275 	git_diff_line *line;
276 	patch_image image;
277 	size_t i;
278 	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) {
284 		if ((error = apply_hunk(&image, patch, hunk, ctx)) < 0)
285 			goto done;
286 	}
287 
288 	git_vector_foreach(&image.lines, i, line)
289 		git_buf_put(out, line->content, line->content_len);
290 
291 done:
292 	patch_image_free(&image);
293 
294 	return error;
295 }
296 
apply_binary_delta(git_buf * out,const char * source,size_t source_len,git_diff_binary_file * binary_file)297 static 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");
315 		git_buf_dispose(out);
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 
340 done:
341 	git_buf_dispose(&inflated);
342 	return error;
343 }
344 
apply_binary(git_buf * out,const char * source,size_t source_len,git_patch * patch)345 static 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;
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;
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 
372 	/* Verify that the resulting file with the reverse patch applied matches the source file */
373 	if (source_len != reverse.size ||
374 		(source_len && memcmp(source, reverse.ptr, source_len) != 0)) {
375 		error = apply_err("binary patch did not apply cleanly");
376 		goto done;
377 	}
378 
379 done:
380 	if (error < 0)
381 		git_buf_dispose(out);
382 
383 	git_buf_dispose(&reverse);
384 	return error;
385 }
386 
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)387 int 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,
393 	git_patch *patch,
394 	const git_apply_options *given_opts)
395 {
396 	apply_hunks_ctx ctx = { GIT_APPLY_OPTIONS_INIT };
397 	char *filename = NULL;
398 	unsigned int mode = 0;
399 	int error = 0;
400 
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);
406 
407 	if (given_opts)
408 		memcpy(&ctx.opts, given_opts, sizeof(git_apply_options));
409 
410 	*filename_out = NULL;
411 	*mode_out = 0;
412 
413 	if (patch->delta->status != GIT_DELTA_DELETED) {
414 		const git_diff_file *newfile = &patch->delta->new_file;
415 
416 		filename = git__strdup(newfile->path);
417 		mode = newfile->mode ?
418 			newfile->mode : GIT_FILEMODE_BLOB;
419 	}
420 
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)
424 		error = apply_hunks(contents_out, source, source_len, patch, &ctx);
425 	else
426 		error = git_buf_put(contents_out, source, source_len);
427 
428 	if (error)
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 
440 done:
441 	if (error < 0)
442 		git__free(filename);
443 
444 	return error;
445 }
446 
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)447 static int apply_one(
448 	git_repository *repo,
449 	git_reader *preimage_reader,
450 	git_index *preimage,
451 	git_reader *postimage_reader,
452 	git_index *postimage,
453 	git_diff *diff,
454 	git_strmap *removed_paths,
455 	size_t i,
456 	const git_apply_options *opts)
457 {
458 	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;
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) {
491 		if (git_strmap_exists(removed_paths, delta->old_file.path)) {
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 ||
560 			(error = git_blob_create_from_buffer(&post_id, repo,
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)
575 		error = git_strmap_set(removed_paths, delta->old_file.path, (char *) delta->old_file.path);
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 
581 done:
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 
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)590 static int apply_deltas(
591 	git_repository *repo,
592 	git_reader *pre_reader,
593 	git_index *preimage,
594 	git_reader *post_reader,
595 	git_index *postimage,
596 	git_diff *diff,
597 	const git_apply_options *opts)
598 {
599 	git_strmap *removed_paths;
600 	size_t i;
601 	int error = 0;
602 
603 	if (git_strmap_new(&removed_paths) < 0)
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 
611 done:
612 	git_strmap_free(removed_paths);
613 	return error;
614 }
615 
git_apply_to_tree(git_index ** out,git_repository * repo,git_tree * preimage,git_diff * diff,const git_apply_options * given_opts)616 int git_apply_to_tree(
617 	git_index **out,
618 	git_repository *repo,
619 	git_tree *preimage,
620 	git_diff *diff,
621 	const git_apply_options *given_opts)
622 {
623 	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 
630 	GIT_ASSERT_ARG(out);
631 	GIT_ASSERT_ARG(repo);
632 	GIT_ASSERT_ARG(preimage);
633 	GIT_ASSERT_ARG(diff);
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 
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 		}
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 
673 done:
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 
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)683 static int git_apply__to_workdir(
684 	git_repository *repo,
685 	git_diff *diff,
686 	git_index *preimage,
687 	git_index *postimage,
688 	git_apply_location_t location,
689 	git_apply_options *opts)
690 {
691 	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 
731 done:
732 	git_vector_free(&paths);
733 	return error;
734 }
735 
git_apply__to_index(git_repository * repo,git_diff * diff,git_index * preimage,git_index * postimage,git_apply_options * opts)736 static int git_apply__to_index(
737 	git_repository *repo,
738 	git_diff *diff,
739 	git_index *preimage,
740 	git_index *postimage,
741 	git_apply_options *opts)
742 {
743 	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 
774 done:
775 	git_index_free(index);
776 	return error;
777 }
778 
git_apply_options_init(git_apply_options * opts,unsigned int version)779 int git_apply_options_init(git_apply_options *opts, unsigned int version)
780 {
781 	GIT_ASSERT_ARG(opts);
782 
783 	GIT_INIT_STRUCTURE_FROM_TEMPLATE(
784 		opts, version, git_apply_options, GIT_APPLY_OPTIONS_INIT);
785 	return 0;
786 }
787 
788 /*
789  * Handle the three application options ("locations"):
790  *
791  * GIT_APPLY_LOCATION_WORKDIR: the default, emulates `git apply`.
792  * Applies the diff only to the workdir items and ignores the index
793  * entirely.
794  *
795  * GIT_APPLY_LOCATION_INDEX: emulates `git apply --cached`.
796  * Applies the diff only to the index items and ignores the workdir
797  * completely.
798  *
799  * GIT_APPLY_LOCATION_BOTH: emulates `git apply --index`.
800  * Applies the diff to both the index items and the working directory
801  * items.
802  */
803 
git_apply(git_repository * repo,git_diff * diff,git_apply_location_t location,const git_apply_options * given_opts)804 int git_apply(
805 	git_repository *repo,
806 	git_diff *diff,
807 	git_apply_location_t location,
808 	const git_apply_options *given_opts)
809 {
810 	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 
816 	GIT_ASSERT_ARG(repo);
817 	GIT_ASSERT_ARG(diff);
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:
841 		GIT_ASSERT(false);
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 
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;
863 
864 	if ((error = apply_deltas(repo, pre_reader, preimage, post_reader, postimage, diff, &opts)) < 0)
865 		goto done;
866 
867 	if ((opts.flags & GIT_APPLY_CHECK))
868 		goto done;
869 
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:
881 		GIT_ASSERT(false);
882 	}
883 
884 	if (error < 0)
885 		goto done;
886 
887 	error = git_indexwriter_commit(&indexwriter);
888 
889 done:
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 }
899