apply.c 21.9 KB
Newer Older
1 2 3 4 5 6 7
/*
 * Copyright (C) the libgit2 contributors. All rights reserved.
 *
 * This file is part of libgit2, distributed under the GNU GPL v2 with
 * a Linking Exception. For full terms see the included COPYING file.
 */

8
#include "git2/apply.h"
9 10
#include "git2/patch.h"
#include "git2/filter.h"
11 12
#include "git2/blob.h"
#include "git2/index.h"
13 14
#include "git2/checkout.h"
#include "git2/repository.h"
15
#include "array.h"
16
#include "patch.h"
17
#include "futils.h"
18 19
#include "delta.h"
#include "zstream.h"
20
#include "reader.h"
21
#include "index.h"
22
#include "repository.h"
Edward Thomson committed
23
#include "apply.h"
24 25 26 27 28 29 30 31 32

typedef struct {
	/* The lines that we allocate ourself are allocated out of the pool.
	 * (Lines may have been allocated out of the diff.)
	 */
	git_pool pool;
	git_vector lines;
} patch_image;

33 34 35 36 37 38 39 40 41 42 43 44
static int apply_err(const char *fmt, ...) GIT_FORMAT_PRINTF(1, 2);
static int apply_err(const char *fmt, ...)
{
	va_list ap;

	va_start(ap, fmt);
	git_error_vset(GIT_ERROR_PATCH, fmt, ap);
	va_end(ap);

	return GIT_EAPPLYFAIL;
}

45 46 47 48 49 50 51 52 53 54 55
static void patch_line_init(
	git_diff_line *out,
	const char *in,
	size_t in_len,
	size_t in_offset)
{
	out->content = in;
	out->content_len = in_len;
	out->content_offset = in_offset;
}

56
#define PATCH_IMAGE_INIT { GIT_POOL_INIT, GIT_VECTOR_INIT }
57 58 59 60 61 62 63 64 65

static int patch_image_init_fromstr(
	patch_image *out, const char *in, size_t in_len)
{
	git_diff_line *line;
	const char *start, *end;

	memset(out, 0x0, sizeof(patch_image));

66 67
	if (git_pool_init(&out->pool, sizeof(git_diff_line)) < 0)
		return -1;
68

69 70 71
	if (!in_len)
		return 0;

72
	for (start = in; start < in + in_len; start = end) {
73
		end = memchr(start, '\n', in_len - (start - in));
74

75 76 77 78
		if (end == NULL)
			end = in + in_len;

		else if (end < in + in_len)
79 80 81
			end++;

		line = git_pool_mallocz(&out->pool, 1);
82
		GIT_ERROR_CHECK_ALLOC(line);
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121

		if (git_vector_insert(&out->lines, line) < 0)
			return -1;

		patch_line_init(line, start, (end - start), (start - in));
	}

	return 0;
}

static void patch_image_free(patch_image *image)
{
	if (image == NULL)
		return;

	git_pool_clear(&image->pool);
	git_vector_free(&image->lines);
}

static bool match_hunk(
	patch_image *image,
	patch_image *preimage,
	size_t linenum)
{
	bool match = 0;
	size_t i;

	/* Ensure this hunk is within the image boundaries. */
	if (git_vector_length(&preimage->lines) + linenum >
		git_vector_length(&image->lines))
		return 0;

	match = 1;

	/* Check exact match. */
	for (i = 0; i < git_vector_length(&preimage->lines); i++) {
		git_diff_line *preimage_line = git_vector_get(&preimage->lines, i);
		git_diff_line *image_line = git_vector_get(&image->lines, linenum + i);

122
		if (preimage_line->content_len != image_line->content_len ||
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
			memcmp(preimage_line->content, image_line->content, image_line->content_len) != 0) {
			match = 0;
			break;
		}
	}

	return match;
}

static bool find_hunk_linenum(
	size_t *out,
	patch_image *image,
	patch_image *preimage,
	size_t linenum)
{
	size_t max = git_vector_length(&image->lines);
	bool match;

	if (linenum > max)
		linenum = max;

	match = match_hunk(image, preimage, linenum);

	*out = linenum;
	return match;
}

static int update_hunk(
	patch_image *image,
152
	size_t linenum,
153 154 155 156 157 158 159 160 161
	patch_image *preimage,
	patch_image *postimage)
{
	size_t postlen = git_vector_length(&postimage->lines);
	size_t prelen = git_vector_length(&preimage->lines);
	size_t i;
	int error = 0;

	if (postlen > prelen)
162
		error = git_vector_insert_null(
163 164
			&image->lines, linenum, (postlen - prelen));
	else if (prelen > postlen)
165
		error = git_vector_remove_range(
166 167 168
			&image->lines, linenum, (prelen - postlen));

	if (error) {
169
		git_error_set_oom();
170 171 172 173 174 175 176 177 178 179 180
		return -1;
	}

	for (i = 0; i < git_vector_length(&postimage->lines); i++) {
		image->lines.contents[linenum + i] =
			git_vector_get(&postimage->lines, i);
	}

	return 0;
}

181 182 183 184 185 186
typedef struct {
	git_apply_options opts;
	size_t skipped_new_lines;
	size_t skipped_old_lines;
} apply_hunks_ctx;

187 188 189
static int apply_hunk(
	patch_image *image,
	git_patch *patch,
190 191
	git_patch_hunk *hunk,
	apply_hunks_ctx *ctx)
192
{
193
	patch_image preimage = PATCH_IMAGE_INIT, postimage = PATCH_IMAGE_INIT;
194 195 196
	size_t line_num, i;
	int error = 0;

197 198 199 200 201 202 203 204 205 206 207 208 209 210
	if (ctx->opts.hunk_cb) {
		error = ctx->opts.hunk_cb(&hunk->hunk, ctx->opts.payload);

		if (error) {
			if (error > 0) {
				ctx->skipped_new_lines += hunk->hunk.new_lines;
				ctx->skipped_old_lines += hunk->hunk.old_lines;
				error = 0;
			}

			goto done;
		}
	}

211 212
	for (i = 0; i < hunk->line_count; i++) {
		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 218 219
			goto done;
		}

220
		switch (line->origin) {
221 222 223
			case GIT_DIFF_LINE_CONTEXT_EOFNL:
			case GIT_DIFF_LINE_DEL_EOFNL:
			case GIT_DIFF_LINE_ADD_EOFNL:
224
				prev = i ? git_array_get(patch->lines, linenum - 1) : NULL;
225 226 227
				if (prev && prev->content[prev->content_len - 1] == '\n')
					prev->content_len -= 1;
				break;
228 229 230 231 232 233 234 235 236 237 238 239 240
			case GIT_DIFF_LINE_CONTEXT:
				if ((error = git_vector_insert(&preimage.lines, line)) < 0 ||
				    (error = git_vector_insert(&postimage.lines, line)) < 0)
					goto done;
				break;
			case GIT_DIFF_LINE_DELETION:
				if ((error = git_vector_insert(&preimage.lines, line)) < 0)
					goto done;
				break;
			case GIT_DIFF_LINE_ADDITION:
				if ((error = git_vector_insert(&postimage.lines, line)) < 0)
					goto done;
				break;
241 242 243
		}
	}

244 245 246 247 248 249 250 251
	if (hunk->hunk.new_start) {
		line_num = hunk->hunk.new_start -
			ctx->skipped_new_lines +
			ctx->skipped_old_lines -
			1;
	} else {
		line_num = 0;
	}
252 253

	if (!find_hunk_linenum(&line_num, image, &preimage, line_num)) {
254
		error = apply_err("hunk at line %d did not apply",
255 256 257 258 259 260 261 262 263 264 265 266 267 268
			hunk->hunk.new_start);
		goto done;
	}

	error = update_hunk(image, line_num, &preimage, &postimage);

done:
	patch_image_free(&preimage);
	patch_image_free(&postimage);

	return error;
}

static int apply_hunks(
269
	git_str *out,
270 271
	const char *source,
	size_t source_len,
272 273
	git_patch *patch,
	apply_hunks_ctx *ctx)
274
{
275
	git_patch_hunk *hunk;
276 277 278 279 280 281 282 283 284
	git_diff_line *line;
	patch_image image;
	size_t i;
	int error = 0;

	if ((error = patch_image_init_fromstr(&image, source, source_len)) < 0)
		goto done;

	git_array_foreach(patch->hunks, i, hunk) {
285
		if ((error = apply_hunk(&image, patch, hunk, ctx)) < 0)
286 287 288 289
			goto done;
	}

	git_vector_foreach(&image.lines, i, line)
290
		git_str_put(out, line->content, line->content_len);
291 292 293 294 295 296 297

done:
	patch_image_free(&image);

	return error;
}

298
static int apply_binary_delta(
299
	git_str *out,
300 301 302 303
	const char *source,
	size_t source_len,
	git_diff_binary_file *binary_file)
{
304
	git_str inflated = GIT_STR_INIT;
305 306 307 308
	int error = 0;

	/* no diff means identical contents */
	if (binary_file->datalen == 0)
309
		return git_str_put(out, source, source_len);
310 311 312 313 314 315

	error = git_zstream_inflatebuf(&inflated,
		binary_file->data, binary_file->datalen);

	if (!error && inflated.size != binary_file->inflatedlen) {
		error = apply_err("inflated delta does not match expected length");
316
		git_str_dispose(out);
317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333
	}

	if (error < 0)
		goto done;

	if (binary_file->type == GIT_DIFF_BINARY_DELTA) {
		void *data;
		size_t data_len;

		error = git_delta_apply(&data, &data_len, (void *)source, source_len,
			(void *)inflated.ptr, inflated.size);

		out->ptr = data;
		out->size = data_len;
		out->asize = data_len;
	}
	else if (binary_file->type == GIT_DIFF_BINARY_LITERAL) {
334
		git_str_swap(out, &inflated);
335 336 337 338 339 340 341
	}
	else {
		error = apply_err("unknown binary delta type");
		goto done;
	}

done:
342
	git_str_dispose(&inflated);
343 344 345 346
	return error;
}

static int apply_binary(
347
	git_str *out,
348 349 350 351
	const char *source,
	size_t source_len,
	git_patch *patch)
{
352
	git_str reverse = GIT_STR_INIT;
353 354 355 356 357 358 359 360 361
	int error = 0;

	if (!patch->binary.contains_data) {
		error = apply_err("patch does not contain binary data");
		goto done;
	}

	if (!patch->binary.old_file.datalen && !patch->binary.new_file.datalen)
		goto done;
362 363 364 365 366 367 368 369 370 371 372

	/* first, apply the new_file delta to the given source */
	if ((error = apply_binary_delta(out, source, source_len,
			&patch->binary.new_file)) < 0)
		goto done;

	/* second, apply the old_file delta to sanity check the result */
	if ((error = apply_binary_delta(&reverse, out->ptr, out->size,
			&patch->binary.old_file)) < 0)
		goto done;

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 377 378 379 380 381
		error = apply_err("binary patch did not apply cleanly");
		goto done;
	}

done:
	if (error < 0)
382
		git_str_dispose(out);
383

384
	git_str_dispose(&reverse);
385 386 387
	return error;
}

388
int git_apply__patch(
389
	git_str *contents_out,
390 391 392 393
	char **filename_out,
	unsigned int *mode_out,
	const char *source,
	size_t source_len,
394 395
	git_patch *patch,
	const git_apply_options *given_opts)
396
{
397
	apply_hunks_ctx ctx = { GIT_APPLY_OPTIONS_INIT };
398 399 400 401
	char *filename = NULL;
	unsigned int mode = 0;
	int error = 0;

Edward Thomson committed
402 403 404 405 406
	GIT_ASSERT_ARG(contents_out);
	GIT_ASSERT_ARG(filename_out);
	GIT_ASSERT_ARG(mode_out);
	GIT_ASSERT_ARG(source || !source_len);
	GIT_ASSERT_ARG(patch);
407

408 409 410
	if (given_opts)
		memcpy(&ctx.opts, given_opts, sizeof(git_apply_options));

411 412 413 414
	*filename_out = NULL;
	*mode_out = 0;

	if (patch->delta->status != GIT_DELTA_DELETED) {
415
		const git_diff_file *newfile = &patch->delta->new_file;
416 417 418 419

		filename = git__strdup(newfile->path);
		mode = newfile->mode ?
			newfile->mode : GIT_FILEMODE_BLOB;
420 421
	}

422 423 424
	if (patch->delta->flags & GIT_DIFF_FLAG_BINARY)
		error = apply_binary(contents_out, source, source_len, patch);
	else if (patch->hunks.size)
425
		error = apply_hunks(contents_out, source, source_len, patch, &ctx);
426
	else
427
		error = git_str_put(contents_out, source, source_len);
428 429

	if (error)
430 431 432
		goto done;

	if (patch->delta->status == GIT_DELTA_DELETED &&
433
		git_str_len(contents_out) > 0) {
434 435 436 437 438 439 440 441 442 443 444 445 446
		error = apply_err("removal patch leaves file contents");
		goto done;
	}

	*filename_out = filename;
	*mode_out = mode;

done:
	if (error < 0)
		git__free(filename);

	return error;
}
447 448 449

static int apply_one(
	git_repository *repo,
450
	git_reader *preimage_reader,
451
	git_index *preimage,
452
	git_reader *postimage_reader,
453 454
	git_index *postimage,
	git_diff *diff,
455
	git_strmap *removed_paths,
456 457
	size_t i,
	const git_apply_options *opts)
458 459
{
	git_patch *patch = NULL;
460
	git_str pre_contents = GIT_STR_INIT, post_contents = GIT_STR_INIT;
461 462 463
	const git_diff_delta *delta;
	char *filename = NULL;
	unsigned int mode;
464
	git_oid pre_id, post_id;
465
	git_filemode_t pre_filemode;
466
	git_index_entry pre_entry, post_entry;
467
	bool skip_preimage = false;
468 469 470 471 472 473 474
	int error;

	if ((error = git_patch_from_diff(&patch, diff, i)) < 0)
		goto done;

	delta = git_patch_get_delta(patch);

475 476 477 478 479 480 481 482 483 484 485
	if (opts->delta_cb) {
		error = opts->delta_cb(delta, opts->payload);

		if (error) {
			if (error > 0)
				error = 0;

			goto done;
		}
	}

486
	/*
487 488
	 * Ensure that the file has not been deleted or renamed if we're
	 * applying a modification delta.
489
	 */
490 491
	if (delta->status != GIT_DELTA_RENAMED &&
	    delta->status != GIT_DELTA_ADDED) {
492
		if (git_strmap_exists(removed_paths, delta->old_file.path)) {
493 494 495
			error = apply_err("path '%s' has been renamed or deleted", delta->old_file.path);
			goto done;
		}
496
	}
497

498 499 500 501 502 503 504 505
	/*
	 * We may be applying a second delta to an already seen file.  If so,
	 * use the already modified data in the postimage instead of the
	 * content from the index or working directory.  (Don't do this in
	 * the case of a rename, which must be specified before additional
	 * deltas since we apply deltas to the target filename.)
	 */
	if (delta->status != GIT_DELTA_RENAMED) {
506 507 508 509
		if ((error = git_reader_read(&pre_contents, &pre_id, &pre_filemode,
		    postimage_reader, delta->old_file.path)) == 0) {
			skip_preimage = true;
		} else if (error == GIT_ENOTFOUND) {
510
			git_error_clear();
511 512 513 514 515 516 517
			error = 0;
		} else {
			goto done;
		}
	}

	if (!skip_preimage && delta->status != GIT_DELTA_ADDED) {
518
		error = git_reader_read(&pre_contents, &pre_id, &pre_filemode,
519
			preimage_reader, delta->old_file.path);
520

521 522 523
		/* ENOTFOUND means the preimage was not found; apply failed. */
		if (error == GIT_ENOTFOUND)
			error = GIT_EAPPLYFAIL;
524

525 526 527 528 529
		/* When applying to BOTH, the index did not match the workdir. */
		if (error == GIT_READER_MISMATCH)
			error = apply_err("%s: does not match index", delta->old_file.path);

		if (error < 0)
530
			goto done;
531 532 533 534 535 536 537 538 539 540 541

		/*
		 * We need to populate the preimage data structure with the
		 * contents that we are using as the preimage for this file.
		 * This allows us to apply patches to files that have been
		 * modified in the working directory.  During checkout,
		 * we will use this expected preimage as the baseline, and
		 * limit checkout to only the paths affected by patch
		 * application.  (Without this, we would fail to write the
		 * postimage contents to any file that had been modified
		 * from HEAD on-disk, even if the patch application succeeded.)
542 543 544 545
		 * Use the contents from the delta where available - some
		 * fields may not be available, like the old file mode (eg in
		 * an exact rename situation) so trust the patch parsing to
		 * validate and use the preimage data in that case.
546 547 548 549
		 */
		if (preimage) {
			memset(&pre_entry, 0, sizeof(git_index_entry));
			pre_entry.path = delta->old_file.path;
550
			pre_entry.mode = delta->old_file.mode ? delta->old_file.mode : pre_filemode;
551 552 553 554 555
			git_oid_cpy(&pre_entry.id, &pre_id);

			if ((error = git_index_add(preimage, &pre_entry)) < 0)
				goto done;
		}
556 557
	}

558 559
	if (delta->status != GIT_DELTA_DELETED) {
		if ((error = git_apply__patch(&post_contents, &filename, &mode,
560
				pre_contents.ptr, pre_contents.size, patch, opts)) < 0 ||
561
			(error = git_blob_create_from_buffer(&post_id, repo,
562 563
				post_contents.ptr, post_contents.size)) < 0)
			goto done;
564

565 566 567 568
		memset(&post_entry, 0, sizeof(git_index_entry));
		post_entry.path = filename;
		post_entry.mode = mode;
		git_oid_cpy(&post_entry.id, &post_id);
569

570 571 572
		if ((error = git_index_add(postimage, &post_entry)) < 0)
			goto done;
	}
573

574 575
	if (delta->status == GIT_DELTA_RENAMED ||
	    delta->status == GIT_DELTA_DELETED)
576
		error = git_strmap_set(removed_paths, delta->old_file.path, (char *) delta->old_file.path);
577 578 579 580 581

	if (delta->status == GIT_DELTA_RENAMED ||
	    delta->status == GIT_DELTA_ADDED)
		git_strmap_delete(removed_paths, delta->new_file.path);

582
done:
583 584
	git_str_dispose(&pre_contents);
	git_str_dispose(&post_contents);
585 586 587 588 589 590
	git__free(filename);
	git_patch_free(patch);

	return error;
}

591 592 593 594 595 596 597 598 599 600 601
static int 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)
{
	git_strmap *removed_paths;
	size_t i;
lhchavez committed
602
	int error = 0;
603

604
	if (git_strmap_new(&removed_paths) < 0)
605 606 607 608 609 610 611 612 613 614 615 616
		return -1;

	for (i = 0; i < git_diff_num_deltas(diff); i++) {
		if ((error = apply_one(repo, pre_reader, preimage, post_reader, postimage, diff, removed_paths, i, opts)) < 0)
			goto done;
	}

done:
	git_strmap_free(removed_paths);
	return error;
}

617 618 619 620
int git_apply_to_tree(
	git_index **out,
	git_repository *repo,
	git_tree *preimage,
621 622
	git_diff *diff,
	const git_apply_options *given_opts)
623 624
{
	git_index *postimage = NULL;
625
	git_reader *pre_reader = NULL, *post_reader = NULL;
626
	git_apply_options opts = GIT_APPLY_OPTIONS_INIT;
627 628 629 630
	const git_diff_delta *delta;
	size_t i;
	int error = 0;

Edward Thomson committed
631 632 633 634
	GIT_ASSERT_ARG(out);
	GIT_ASSERT_ARG(repo);
	GIT_ASSERT_ARG(preimage);
	GIT_ASSERT_ARG(diff);
635 636 637

	*out = NULL;

638 639 640
	if (given_opts)
		memcpy(&opts, given_opts, sizeof(git_apply_options));

641 642 643 644 645 646 647
	if ((error = git_reader_for_tree(&pre_reader, preimage)) < 0)
		goto done;

	/*
	 * put the current tree into the postimage as-is - the diff will
	 * replace any entries contained therein
	 */
648
	if ((error = git_index__new(&postimage, repo->oid_type)) < 0 ||
649 650
		(error = git_index_read_tree(postimage, preimage)) < 0 ||
		(error = git_reader_for_index(&post_reader, repo, postimage)) < 0)
651 652 653 654 655 656 657 658 659 660
		goto done;

	/*
	 * Remove the old paths from the index before applying diffs -
	 * we need to do a full pass to remove them before adding deltas,
	 * in order to handle rename situations.
	 */
	for (i = 0; i < git_diff_num_deltas(diff); i++) {
		delta = git_diff_get_delta(diff, i);

661 662 663 664 665 666
		if (delta->status == GIT_DELTA_DELETED ||
			delta->status == GIT_DELTA_RENAMED) {
			if ((error = git_index_remove(postimage,
					delta->old_file.path, 0)) < 0)
				goto done;
		}
667 668
	}

669 670
	if ((error = apply_deltas(repo, pre_reader, NULL, post_reader, postimage, diff, &opts)) < 0)
		goto done;
671 672 673 674 675 676 677

	*out = postimage;

done:
	if (error < 0)
		git_index_free(postimage);

678
	git_reader_free(pre_reader);
679
	git_reader_free(post_reader);
680

681 682
	return error;
}
683

684 685 686
static int git_apply__to_workdir(
	git_repository *repo,
	git_diff *diff,
687
	git_index *preimage,
688
	git_index *postimage,
689
	git_apply_location_t location,
690 691 692 693 694 695 696 697
	git_apply_options *opts)
{
	git_vector paths = GIT_VECTOR_INIT;
	git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
	const git_diff_delta *delta;
	size_t i;
	int error;

698 699
	GIT_UNUSED(opts);

700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719
	/*
	 * Limit checkout to the paths affected by the diff; this ensures
	 * that other modifications in the working directory are unaffected.
	 */
	if ((error = git_vector_init(&paths, git_diff_num_deltas(diff), NULL)) < 0)
		goto done;

	for (i = 0; i < git_diff_num_deltas(diff); i++) {
		delta = git_diff_get_delta(diff, i);

		if ((error = git_vector_insert(&paths, (void *)delta->old_file.path)) < 0)
			goto done;

		if (strcmp(delta->old_file.path, delta->new_file.path) &&
		    (error = git_vector_insert(&paths, (void *)delta->new_file.path)) < 0)
			goto done;
	}

	checkout_opts.checkout_strategy |= GIT_CHECKOUT_SAFE;
	checkout_opts.checkout_strategy |= GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH;
720
	checkout_opts.checkout_strategy |= GIT_CHECKOUT_DONT_WRITE_INDEX;
721

722
	if (location == GIT_APPLY_LOCATION_WORKDIR)
723 724 725 726 727
		checkout_opts.checkout_strategy |= GIT_CHECKOUT_DONT_UPDATE_INDEX;

	checkout_opts.paths.strings = (char **)paths.contents;
	checkout_opts.paths.count = paths.length;

728 729
	checkout_opts.baseline_index = preimage;

730 731 732 733 734 735 736 737 738 739
	error = git_checkout_index(repo, postimage, &checkout_opts);

done:
	git_vector_free(&paths);
	return error;
}

static int git_apply__to_index(
	git_repository *repo,
	git_diff *diff,
740
	git_index *preimage,
741 742 743 744 745 746 747 748 749
	git_index *postimage,
	git_apply_options *opts)
{
	git_index *index = NULL;
	const git_diff_delta *delta;
	const git_index_entry *entry;
	size_t i;
	int error;

750
	GIT_UNUSED(preimage);
751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779
	GIT_UNUSED(opts);

	if ((error = git_repository_index(&index, repo)) < 0)
		goto done;

	/* Remove deleted (or renamed) paths from the index. */
	for (i = 0; i < git_diff_num_deltas(diff); i++) {
		delta = git_diff_get_delta(diff, i);

		if (delta->status == GIT_DELTA_DELETED ||
		    delta->status == GIT_DELTA_RENAMED) {
			if ((error = git_index_remove(index, delta->old_file.path, 0)) < 0)
				goto done;
		}
	}

	/* Then add the changes back to the index. */
	for (i = 0; i < git_index_entrycount(postimage); i++) {
		entry = git_index_get_byindex(postimage, i);

		if ((error = git_index_add(index, entry)) < 0)
			goto done;
	}

done:
	git_index_free(index);
	return error;
}

780 781
int git_apply_options_init(git_apply_options *opts, unsigned int version)
{
Edward Thomson committed
782 783
	GIT_ASSERT_ARG(opts);

784 785 786 787 788
	GIT_INIT_STRUCTURE_FROM_TEMPLATE(
		opts, version, git_apply_options, GIT_APPLY_OPTIONS_INIT);
	return 0;
}

789 790 791 792 793 794 795 796 797 798 799 800 801 802
/*
 * Handle the three application options ("locations"):
 *
 * GIT_APPLY_LOCATION_WORKDIR: the default, emulates `git apply`.
 * Applies the diff only to the workdir items and ignores the index
 * entirely.
 *
 * GIT_APPLY_LOCATION_INDEX: emulates `git apply --cached`.
 * Applies the diff only to the index items and ignores the workdir
 * completely.
 *
 * GIT_APPLY_LOCATION_BOTH: emulates `git apply --index`.
 * Applies the diff to both the index items and the working directory
 * items.
803 804 805 806 807
 */

int git_apply(
	git_repository *repo,
	git_diff *diff,
808 809
	git_apply_location_t location,
	const git_apply_options *given_opts)
810
{
811 812
	git_indexwriter indexwriter = GIT_INDEXWRITER_INIT;
	git_index *index = NULL, *preimage = NULL, *postimage = NULL;
813
	git_reader *pre_reader = NULL, *post_reader = NULL;
814
	git_apply_options opts = GIT_APPLY_OPTIONS_INIT;
815
	int error = GIT_EINVALID;
816

Edward Thomson committed
817 818
	GIT_ASSERT_ARG(repo);
	GIT_ASSERT_ARG(diff);
819

820
	GIT_ERROR_CHECK_VERSION(
821 822 823 824 825 826 827 828 829 830
		given_opts, GIT_APPLY_OPTIONS_VERSION, "git_apply_options");

	if (given_opts)
		memcpy(&opts, given_opts, sizeof(git_apply_options));

	/*
	 * by default, we apply a patch directly to the working directory;
	 * in `--cached` or `--index` mode, we apply to the contents already
	 * in the index.
	 */
831
	switch (location) {
832 833 834 835
	case GIT_APPLY_LOCATION_BOTH:
		error = git_reader_for_workdir(&pre_reader, repo, true);
		break;
	case GIT_APPLY_LOCATION_INDEX:
836
		error = git_reader_for_index(&pre_reader, repo, NULL);
837 838 839 840 841
		break;
	case GIT_APPLY_LOCATION_WORKDIR:
		error = git_reader_for_workdir(&pre_reader, repo, false);
		break;
	default:
Edward Thomson committed
842
		GIT_ASSERT(false);
843
	}
844 845 846 847 848

	if (error < 0)
		goto done;

	/*
849 850 851 852 853
	 * Build the preimage and postimage (differences).  Note that
	 * this is not the complete preimage or postimage, it only
	 * contains the files affected by the patch.  We want to avoid
	 * having the full repo index, so we will limit our checkout
	 * to only write these files that were affected by the diff.
854
	 */
855 856
	if ((error = git_index__new(&preimage, repo->oid_type)) < 0 ||
	    (error = git_index__new(&postimage, repo->oid_type)) < 0 ||
857
	    (error = git_reader_for_index(&post_reader, repo, postimage)) < 0)
858 859
		goto done;

860 861 862 863
	if (!(opts.flags & GIT_APPLY_CHECK))
		if ((error = git_repository_index(&index, repo)) < 0 ||
		    (error = git_indexwriter_init(&indexwriter, index)) < 0)
			goto done;
864

865 866
	if ((error = apply_deltas(repo, pre_reader, preimage, post_reader, postimage, diff, &opts)) < 0)
		goto done;
867

868 869 870
	if ((opts.flags & GIT_APPLY_CHECK))
		goto done;

871
	switch (location) {
872
	case GIT_APPLY_LOCATION_BOTH:
873
		error = git_apply__to_workdir(repo, diff, preimage, postimage, location, &opts);
874 875
		break;
	case GIT_APPLY_LOCATION_INDEX:
876
		error = git_apply__to_index(repo, diff, preimage, postimage, &opts);
877 878
		break;
	case GIT_APPLY_LOCATION_WORKDIR:
879
		error = git_apply__to_workdir(repo, diff, preimage, postimage, location, &opts);
880 881
		break;
	default:
Edward Thomson committed
882
		GIT_ASSERT(false);
883 884
	}

885 886 887
	if (error < 0)
		goto done;

888 889
	error = git_indexwriter_commit(&indexwriter);

890
done:
891
	git_indexwriter_cleanup(&indexwriter);
892
	git_index_free(postimage);
893
	git_index_free(preimage);
894
	git_index_free(index);
895
	git_reader_free(pre_reader);
896
	git_reader_free(post_reader);
897 898 899

	return error;
}