2 * Copyright (C) the libgit2 contributors. All rights reserved.
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.
12 #include "git2/apply.h"
13 #include "git2/patch.h"
14 #include "git2/filter.h"
15 #include "git2/blob.h"
16 #include "git2/index.h"
17 #include "git2/checkout.h"
18 #include "git2/repository.h"
27 #define apply_err(...) \
28 ( git_error_set(GIT_ERROR_PATCH, __VA_ARGS__), GIT_EAPPLYFAIL )
31 /* The lines that we allocate ourself are allocated out of the pool.
32 * (Lines may have been allocated out of the diff.)
38 static void patch_line_init(
45 out
->content_len
= in_len
;
46 out
->content_offset
= in_offset
;
49 #define PATCH_IMAGE_INIT { GIT_POOL_INIT, GIT_VECTOR_INIT }
51 static int patch_image_init_fromstr(
52 patch_image
*out
, const char *in
, size_t in_len
)
55 const char *start
, *end
;
57 memset(out
, 0x0, sizeof(patch_image
));
59 git_pool_init(&out
->pool
, sizeof(git_diff_line
));
61 for (start
= in
; start
< in
+ in_len
; start
= end
) {
62 end
= memchr(start
, '\n', in_len
);
67 else if (end
< in
+ in_len
)
70 line
= git_pool_mallocz(&out
->pool
, 1);
71 GIT_ERROR_CHECK_ALLOC(line
);
73 if (git_vector_insert(&out
->lines
, line
) < 0)
76 patch_line_init(line
, start
, (end
- start
), (start
- in
));
82 static void patch_image_free(patch_image
*image
)
87 git_pool_clear(&image
->pool
);
88 git_vector_free(&image
->lines
);
91 static bool match_hunk(
93 patch_image
*preimage
,
99 /* Ensure this hunk is within the image boundaries. */
100 if (git_vector_length(&preimage
->lines
) + linenum
>
101 git_vector_length(&image
->lines
))
106 /* Check exact match. */
107 for (i
= 0; i
< git_vector_length(&preimage
->lines
); i
++) {
108 git_diff_line
*preimage_line
= git_vector_get(&preimage
->lines
, i
);
109 git_diff_line
*image_line
= git_vector_get(&image
->lines
, linenum
+ i
);
111 if (preimage_line
->content_len
!= image_line
->content_len
||
112 memcmp(preimage_line
->content
, image_line
->content
, image_line
->content_len
) != 0) {
121 static bool find_hunk_linenum(
124 patch_image
*preimage
,
127 size_t max
= git_vector_length(&image
->lines
);
133 match
= match_hunk(image
, preimage
, linenum
);
139 static int update_hunk(
142 patch_image
*preimage
,
143 patch_image
*postimage
)
145 size_t postlen
= git_vector_length(&postimage
->lines
);
146 size_t prelen
= git_vector_length(&preimage
->lines
);
150 if (postlen
> prelen
)
151 error
= git_vector_insert_null(
152 &image
->lines
, linenum
, (postlen
- prelen
));
153 else if (prelen
> postlen
)
154 error
= git_vector_remove_range(
155 &image
->lines
, linenum
, (prelen
- postlen
));
162 for (i
= 0; i
< git_vector_length(&postimage
->lines
); i
++) {
163 image
->lines
.contents
[linenum
+ i
] =
164 git_vector_get(&postimage
->lines
, i
);
171 git_apply_options opts
;
172 size_t skipped_new_lines
;
173 size_t skipped_old_lines
;
176 static int apply_hunk(
179 git_patch_hunk
*hunk
,
180 apply_hunks_ctx
*ctx
)
182 patch_image preimage
= PATCH_IMAGE_INIT
, postimage
= PATCH_IMAGE_INIT
;
186 if (ctx
->opts
.hunk_cb
) {
187 error
= ctx
->opts
.hunk_cb(&hunk
->hunk
, ctx
->opts
.payload
);
191 ctx
->skipped_new_lines
+= hunk
->hunk
.new_lines
;
192 ctx
->skipped_old_lines
+= hunk
->hunk
.old_lines
;
200 for (i
= 0; i
< hunk
->line_count
; i
++) {
201 size_t linenum
= hunk
->line_start
+ i
;
202 git_diff_line
*line
= git_array_get(patch
->lines
, linenum
);
205 error
= apply_err("preimage does not contain line %"PRIuZ
, linenum
);
209 if (line
->origin
== GIT_DIFF_LINE_CONTEXT
||
210 line
->origin
== GIT_DIFF_LINE_DELETION
) {
211 if ((error
= git_vector_insert(&preimage
.lines
, line
)) < 0)
215 if (line
->origin
== GIT_DIFF_LINE_CONTEXT
||
216 line
->origin
== GIT_DIFF_LINE_ADDITION
) {
217 if ((error
= git_vector_insert(&postimage
.lines
, line
)) < 0)
222 if (hunk
->hunk
.new_start
) {
223 line_num
= hunk
->hunk
.new_start
-
224 ctx
->skipped_new_lines
+
225 ctx
->skipped_old_lines
-
231 if (!find_hunk_linenum(&line_num
, image
, &preimage
, line_num
)) {
232 error
= apply_err("hunk at line %d did not apply",
233 hunk
->hunk
.new_start
);
237 error
= update_hunk(image
, line_num
, &preimage
, &postimage
);
240 patch_image_free(&preimage
);
241 patch_image_free(&postimage
);
246 static int apply_hunks(
251 apply_hunks_ctx
*ctx
)
253 git_patch_hunk
*hunk
;
259 if ((error
= patch_image_init_fromstr(&image
, source
, source_len
)) < 0)
262 git_array_foreach(patch
->hunks
, i
, hunk
) {
263 if ((error
= apply_hunk(&image
, patch
, hunk
, ctx
)) < 0)
267 git_vector_foreach(&image
.lines
, i
, line
)
268 git_buf_put(out
, line
->content
, line
->content_len
);
271 patch_image_free(&image
);
276 static int apply_binary_delta(
280 git_diff_binary_file
*binary_file
)
282 git_buf inflated
= GIT_BUF_INIT
;
285 /* no diff means identical contents */
286 if (binary_file
->datalen
== 0)
287 return git_buf_put(out
, source
, source_len
);
289 error
= git_zstream_inflatebuf(&inflated
,
290 binary_file
->data
, binary_file
->datalen
);
292 if (!error
&& inflated
.size
!= binary_file
->inflatedlen
) {
293 error
= apply_err("inflated delta does not match expected length");
294 git_buf_dispose(out
);
300 if (binary_file
->type
== GIT_DIFF_BINARY_DELTA
) {
304 error
= git_delta_apply(&data
, &data_len
, (void *)source
, source_len
,
305 (void *)inflated
.ptr
, inflated
.size
);
308 out
->size
= data_len
;
309 out
->asize
= data_len
;
311 else if (binary_file
->type
== GIT_DIFF_BINARY_LITERAL
) {
312 git_buf_swap(out
, &inflated
);
315 error
= apply_err("unknown binary delta type");
320 git_buf_dispose(&inflated
);
324 static int apply_binary(
330 git_buf reverse
= GIT_BUF_INIT
;
333 if (!patch
->binary
.contains_data
) {
334 error
= apply_err("patch does not contain binary data");
338 if (!patch
->binary
.old_file
.datalen
&& !patch
->binary
.new_file
.datalen
)
341 /* first, apply the new_file delta to the given source */
342 if ((error
= apply_binary_delta(out
, source
, source_len
,
343 &patch
->binary
.new_file
)) < 0)
346 /* second, apply the old_file delta to sanity check the result */
347 if ((error
= apply_binary_delta(&reverse
, out
->ptr
, out
->size
,
348 &patch
->binary
.old_file
)) < 0)
351 /* Verify that the resulting file with the reverse patch applied matches the source file */
352 if (source_len
!= reverse
.size
||
353 (source_len
&& memcmp(source
, reverse
.ptr
, source_len
) != 0)) {
354 error
= apply_err("binary patch did not apply cleanly");
360 git_buf_dispose(out
);
362 git_buf_dispose(&reverse
);
366 int git_apply__patch(
367 git_buf
*contents_out
,
369 unsigned int *mode_out
,
373 const git_apply_options
*given_opts
)
375 apply_hunks_ctx ctx
= { GIT_APPLY_OPTIONS_INIT
};
376 char *filename
= NULL
;
377 unsigned int mode
= 0;
380 assert(contents_out
&& filename_out
&& mode_out
&& (source
|| !source_len
) && patch
);
383 memcpy(&ctx
.opts
, given_opts
, sizeof(git_apply_options
));
385 *filename_out
= NULL
;
388 if (patch
->delta
->status
!= GIT_DELTA_DELETED
) {
389 const git_diff_file
*newfile
= &patch
->delta
->new_file
;
391 filename
= git__strdup(newfile
->path
);
392 mode
= newfile
->mode
?
393 newfile
->mode
: GIT_FILEMODE_BLOB
;
396 if (patch
->delta
->flags
& GIT_DIFF_FLAG_BINARY
)
397 error
= apply_binary(contents_out
, source
, source_len
, patch
);
398 else if (patch
->hunks
.size
)
399 error
= apply_hunks(contents_out
, source
, source_len
, patch
, &ctx
);
401 error
= git_buf_put(contents_out
, source
, source_len
);
406 if (patch
->delta
->status
== GIT_DELTA_DELETED
&&
407 git_buf_len(contents_out
) > 0) {
408 error
= apply_err("removal patch leaves file contents");
412 *filename_out
= filename
;
422 static int apply_one(
423 git_repository
*repo
,
424 git_reader
*preimage_reader
,
426 git_reader
*postimage_reader
,
427 git_index
*postimage
,
429 git_strmap
*removed_paths
,
431 const git_apply_options
*opts
)
433 git_patch
*patch
= NULL
;
434 git_buf pre_contents
= GIT_BUF_INIT
, post_contents
= GIT_BUF_INIT
;
435 const git_diff_delta
*delta
;
436 char *filename
= NULL
;
438 git_oid pre_id
, post_id
;
439 git_filemode_t pre_filemode
;
440 git_index_entry pre_entry
, post_entry
;
441 bool skip_preimage
= false;
445 if ((error
= git_patch_from_diff(&patch
, diff
, i
)) < 0)
448 delta
= git_patch_get_delta(patch
);
450 if (opts
->delta_cb
) {
451 error
= opts
->delta_cb(delta
, opts
->payload
);
462 * Ensure that the file has not been deleted or renamed if we're
463 * applying a modification delta.
465 if (delta
->status
!= GIT_DELTA_RENAMED
&&
466 delta
->status
!= GIT_DELTA_ADDED
) {
467 pos
= git_strmap_lookup_index(removed_paths
, delta
->old_file
.path
);
468 if (git_strmap_valid_index(removed_paths
, pos
)) {
469 error
= apply_err("path '%s' has been renamed or deleted", delta
->old_file
.path
);
475 * We may be applying a second delta to an already seen file. If so,
476 * use the already modified data in the postimage instead of the
477 * content from the index or working directory. (Don't do this in
478 * the case of a rename, which must be specified before additional
479 * deltas since we apply deltas to the target filename.)
481 if (delta
->status
!= GIT_DELTA_RENAMED
) {
482 if ((error
= git_reader_read(&pre_contents
, &pre_id
, &pre_filemode
,
483 postimage_reader
, delta
->old_file
.path
)) == 0) {
484 skip_preimage
= true;
485 } else if (error
== GIT_ENOTFOUND
) {
493 if (!skip_preimage
&& delta
->status
!= GIT_DELTA_ADDED
) {
494 error
= git_reader_read(&pre_contents
, &pre_id
, &pre_filemode
,
495 preimage_reader
, delta
->old_file
.path
);
497 /* ENOTFOUND means the preimage was not found; apply failed. */
498 if (error
== GIT_ENOTFOUND
)
499 error
= GIT_EAPPLYFAIL
;
501 /* When applying to BOTH, the index did not match the workdir. */
502 if (error
== GIT_READER_MISMATCH
)
503 error
= apply_err("%s: does not match index", delta
->old_file
.path
);
509 * We need to populate the preimage data structure with the
510 * contents that we are using as the preimage for this file.
511 * This allows us to apply patches to files that have been
512 * modified in the working directory. During checkout,
513 * we will use this expected preimage as the baseline, and
514 * limit checkout to only the paths affected by patch
515 * application. (Without this, we would fail to write the
516 * postimage contents to any file that had been modified
517 * from HEAD on-disk, even if the patch application succeeded.)
518 * Use the contents from the delta where available - some
519 * fields may not be available, like the old file mode (eg in
520 * an exact rename situation) so trust the patch parsing to
521 * validate and use the preimage data in that case.
524 memset(&pre_entry
, 0, sizeof(git_index_entry
));
525 pre_entry
.path
= delta
->old_file
.path
;
526 pre_entry
.mode
= delta
->old_file
.mode
? delta
->old_file
.mode
: pre_filemode
;
527 git_oid_cpy(&pre_entry
.id
, &pre_id
);
529 if ((error
= git_index_add(preimage
, &pre_entry
)) < 0)
534 if (delta
->status
!= GIT_DELTA_DELETED
) {
535 if ((error
= git_apply__patch(&post_contents
, &filename
, &mode
,
536 pre_contents
.ptr
, pre_contents
.size
, patch
, opts
)) < 0 ||
537 (error
= git_blob_create_frombuffer(&post_id
, repo
,
538 post_contents
.ptr
, post_contents
.size
)) < 0)
541 memset(&post_entry
, 0, sizeof(git_index_entry
));
542 post_entry
.path
= filename
;
543 post_entry
.mode
= mode
;
544 git_oid_cpy(&post_entry
.id
, &post_id
);
546 if ((error
= git_index_add(postimage
, &post_entry
)) < 0)
550 if (delta
->status
== GIT_DELTA_RENAMED
||
551 delta
->status
== GIT_DELTA_DELETED
)
552 git_strmap_insert(removed_paths
, delta
->old_file
.path
, (char *)delta
->old_file
.path
, &error
);
554 if (delta
->status
== GIT_DELTA_RENAMED
||
555 delta
->status
== GIT_DELTA_ADDED
)
556 git_strmap_delete(removed_paths
, delta
->new_file
.path
);
559 git_buf_dispose(&pre_contents
);
560 git_buf_dispose(&post_contents
);
562 git_patch_free(patch
);
567 static int apply_deltas(
568 git_repository
*repo
,
569 git_reader
*pre_reader
,
571 git_reader
*post_reader
,
572 git_index
*postimage
,
574 const git_apply_options
*opts
)
576 git_strmap
*removed_paths
;
580 if (git_strmap_alloc(&removed_paths
) < 0)
583 for (i
= 0; i
< git_diff_num_deltas(diff
); i
++) {
584 if ((error
= apply_one(repo
, pre_reader
, preimage
, post_reader
, postimage
, diff
, removed_paths
, i
, opts
)) < 0)
589 git_strmap_free(removed_paths
);
593 int git_apply_to_tree(
595 git_repository
*repo
,
598 const git_apply_options
*given_opts
)
600 git_index
*postimage
= NULL
;
601 git_reader
*pre_reader
= NULL
, *post_reader
= NULL
;
602 git_apply_options opts
= GIT_APPLY_OPTIONS_INIT
;
603 const git_diff_delta
*delta
;
607 assert(out
&& repo
&& preimage
&& diff
);
612 memcpy(&opts
, given_opts
, sizeof(git_apply_options
));
614 if ((error
= git_reader_for_tree(&pre_reader
, preimage
)) < 0)
618 * put the current tree into the postimage as-is - the diff will
619 * replace any entries contained therein
621 if ((error
= git_index_new(&postimage
)) < 0 ||
622 (error
= git_index_read_tree(postimage
, preimage
)) < 0 ||
623 (error
= git_reader_for_index(&post_reader
, repo
, postimage
)) < 0)
627 * Remove the old paths from the index before applying diffs -
628 * we need to do a full pass to remove them before adding deltas,
629 * in order to handle rename situations.
631 for (i
= 0; i
< git_diff_num_deltas(diff
); i
++) {
632 delta
= git_diff_get_delta(diff
, i
);
634 if ((error
= git_index_remove(postimage
,
635 delta
->old_file
.path
, 0)) < 0)
639 if ((error
= apply_deltas(repo
, pre_reader
, NULL
, post_reader
, postimage
, diff
, &opts
)) < 0)
646 git_index_free(postimage
);
648 git_reader_free(pre_reader
);
649 git_reader_free(post_reader
);
654 static int git_apply__to_workdir(
655 git_repository
*repo
,
658 git_index
*postimage
,
659 git_apply_location_t location
,
660 git_apply_options
*opts
)
662 git_vector paths
= GIT_VECTOR_INIT
;
663 git_checkout_options checkout_opts
= GIT_CHECKOUT_OPTIONS_INIT
;
664 const git_diff_delta
*delta
;
671 * Limit checkout to the paths affected by the diff; this ensures
672 * that other modifications in the working directory are unaffected.
674 if ((error
= git_vector_init(&paths
, git_diff_num_deltas(diff
), NULL
)) < 0)
677 for (i
= 0; i
< git_diff_num_deltas(diff
); i
++) {
678 delta
= git_diff_get_delta(diff
, i
);
680 if ((error
= git_vector_insert(&paths
, (void *)delta
->old_file
.path
)) < 0)
683 if (strcmp(delta
->old_file
.path
, delta
->new_file
.path
) &&
684 (error
= git_vector_insert(&paths
, (void *)delta
->new_file
.path
)) < 0)
688 checkout_opts
.checkout_strategy
|= GIT_CHECKOUT_SAFE
;
689 checkout_opts
.checkout_strategy
|= GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH
;
690 checkout_opts
.checkout_strategy
|= GIT_CHECKOUT_DONT_WRITE_INDEX
;
692 if (location
== GIT_APPLY_LOCATION_WORKDIR
)
693 checkout_opts
.checkout_strategy
|= GIT_CHECKOUT_DONT_UPDATE_INDEX
;
695 checkout_opts
.paths
.strings
= (char **)paths
.contents
;
696 checkout_opts
.paths
.count
= paths
.length
;
698 checkout_opts
.baseline_index
= preimage
;
700 error
= git_checkout_index(repo
, postimage
, &checkout_opts
);
703 git_vector_free(&paths
);
707 static int git_apply__to_index(
708 git_repository
*repo
,
711 git_index
*postimage
,
712 git_apply_options
*opts
)
714 git_index
*index
= NULL
;
715 const git_diff_delta
*delta
;
716 const git_index_entry
*entry
;
720 GIT_UNUSED(preimage
);
723 if ((error
= git_repository_index(&index
, repo
)) < 0)
726 /* Remove deleted (or renamed) paths from the index. */
727 for (i
= 0; i
< git_diff_num_deltas(diff
); i
++) {
728 delta
= git_diff_get_delta(diff
, i
);
730 if (delta
->status
== GIT_DELTA_DELETED
||
731 delta
->status
== GIT_DELTA_RENAMED
) {
732 if ((error
= git_index_remove(index
, delta
->old_file
.path
, 0)) < 0)
737 /* Then add the changes back to the index. */
738 for (i
= 0; i
< git_index_entrycount(postimage
); i
++) {
739 entry
= git_index_get_byindex(postimage
, i
);
741 if ((error
= git_index_add(index
, entry
)) < 0)
746 git_index_free(index
);
751 * Handle the three application options ("locations"):
753 * GIT_APPLY_LOCATION_WORKDIR: the default, emulates `git apply`.
754 * Applies the diff only to the workdir items and ignores the index
757 * GIT_APPLY_LOCATION_INDEX: emulates `git apply --cached`.
758 * Applies the diff only to the index items and ignores the workdir
761 * GIT_APPLY_LOCATION_BOTH: emulates `git apply --index`.
762 * Applies the diff to both the index items and the working directory
767 git_repository
*repo
,
769 git_apply_location_t location
,
770 const git_apply_options
*given_opts
)
772 git_indexwriter indexwriter
= GIT_INDEXWRITER_INIT
;
773 git_index
*index
= NULL
, *preimage
= NULL
, *postimage
= NULL
;
774 git_reader
*pre_reader
= NULL
, *post_reader
= NULL
;
775 git_apply_options opts
= GIT_APPLY_OPTIONS_INIT
;
776 int error
= GIT_EINVALID
;
778 assert(repo
&& diff
);
780 GIT_ERROR_CHECK_VERSION(
781 given_opts
, GIT_APPLY_OPTIONS_VERSION
, "git_apply_options");
784 memcpy(&opts
, given_opts
, sizeof(git_apply_options
));
787 * by default, we apply a patch directly to the working directory;
788 * in `--cached` or `--index` mode, we apply to the contents already
792 case GIT_APPLY_LOCATION_BOTH
:
793 error
= git_reader_for_workdir(&pre_reader
, repo
, true);
795 case GIT_APPLY_LOCATION_INDEX
:
796 error
= git_reader_for_index(&pre_reader
, repo
, NULL
);
798 case GIT_APPLY_LOCATION_WORKDIR
:
799 error
= git_reader_for_workdir(&pre_reader
, repo
, false);
809 * Build the preimage and postimage (differences). Note that
810 * this is not the complete preimage or postimage, it only
811 * contains the files affected by the patch. We want to avoid
812 * having the full repo index, so we will limit our checkout
813 * to only write these files that were affected by the diff.
815 if ((error
= git_index_new(&preimage
)) < 0 ||
816 (error
= git_index_new(&postimage
)) < 0 ||
817 (error
= git_reader_for_index(&post_reader
, repo
, postimage
)) < 0)
820 if ((error
= git_repository_index(&index
, repo
)) < 0 ||
821 (error
= git_indexwriter_init(&indexwriter
, index
)) < 0)
824 if ((error
= apply_deltas(repo
, pre_reader
, preimage
, post_reader
, postimage
, diff
, &opts
)) < 0)
828 case GIT_APPLY_LOCATION_BOTH
:
829 error
= git_apply__to_workdir(repo
, diff
, preimage
, postimage
, location
, &opts
);
831 case GIT_APPLY_LOCATION_INDEX
:
832 error
= git_apply__to_index(repo
, diff
, preimage
, postimage
, &opts
);
834 case GIT_APPLY_LOCATION_WORKDIR
:
835 error
= git_apply__to_workdir(repo
, diff
, preimage
, postimage
, location
, &opts
);
844 error
= git_indexwriter_commit(&indexwriter
);
847 git_indexwriter_cleanup(&indexwriter
);
848 git_index_free(postimage
);
849 git_index_free(preimage
);
850 git_index_free(index
);
851 git_reader_free(pre_reader
);
852 git_reader_free(post_reader
);