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.
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"
26 /* The lines that we allocate ourself are allocated out of the pool.
27 * (Lines may have been allocated out of the diff.)
33 static int apply_err(const char *fmt
, ...) GIT_FORMAT_PRINTF(1, 2);
34 static int apply_err(const char *fmt
, ...)
39 git_error_vset(GIT_ERROR_PATCH
, fmt
, ap
);
42 return GIT_EAPPLYFAIL
;
45 static void patch_line_init(
52 out
->content_len
= in_len
;
53 out
->content_offset
= in_offset
;
56 #define PATCH_IMAGE_INIT { GIT_POOL_INIT, GIT_VECTOR_INIT }
58 static int patch_image_init_fromstr(
59 patch_image
*out
, const char *in
, size_t in_len
)
62 const char *start
, *end
;
64 memset(out
, 0x0, sizeof(patch_image
));
66 git_pool_init(&out
->pool
, sizeof(git_diff_line
));
68 for (start
= in
; start
< in
+ in_len
; start
= end
) {
69 end
= memchr(start
, '\n', in_len
- (start
- in
));
74 else if (end
< in
+ in_len
)
77 line
= git_pool_mallocz(&out
->pool
, 1);
78 GIT_ERROR_CHECK_ALLOC(line
);
80 if (git_vector_insert(&out
->lines
, line
) < 0)
83 patch_line_init(line
, start
, (end
- start
), (start
- in
));
89 static void patch_image_free(patch_image
*image
)
94 git_pool_clear(&image
->pool
);
95 git_vector_free(&image
->lines
);
98 static bool match_hunk(
100 patch_image
*preimage
,
106 /* Ensure this hunk is within the image boundaries. */
107 if (git_vector_length(&preimage
->lines
) + linenum
>
108 git_vector_length(&image
->lines
))
113 /* Check exact match. */
114 for (i
= 0; i
< git_vector_length(&preimage
->lines
); i
++) {
115 git_diff_line
*preimage_line
= git_vector_get(&preimage
->lines
, i
);
116 git_diff_line
*image_line
= git_vector_get(&image
->lines
, linenum
+ i
);
118 if (preimage_line
->content_len
!= image_line
->content_len
||
119 memcmp(preimage_line
->content
, image_line
->content
, image_line
->content_len
) != 0) {
128 static bool find_hunk_linenum(
131 patch_image
*preimage
,
134 size_t max
= git_vector_length(&image
->lines
);
140 match
= match_hunk(image
, preimage
, linenum
);
146 static int update_hunk(
149 patch_image
*preimage
,
150 patch_image
*postimage
)
152 size_t postlen
= git_vector_length(&postimage
->lines
);
153 size_t prelen
= git_vector_length(&preimage
->lines
);
157 if (postlen
> prelen
)
158 error
= git_vector_insert_null(
159 &image
->lines
, linenum
, (postlen
- prelen
));
160 else if (prelen
> postlen
)
161 error
= git_vector_remove_range(
162 &image
->lines
, linenum
, (prelen
- postlen
));
169 for (i
= 0; i
< git_vector_length(&postimage
->lines
); i
++) {
170 image
->lines
.contents
[linenum
+ i
] =
171 git_vector_get(&postimage
->lines
, i
);
178 git_apply_options opts
;
179 size_t skipped_new_lines
;
180 size_t skipped_old_lines
;
183 static int apply_hunk(
186 git_patch_hunk
*hunk
,
187 apply_hunks_ctx
*ctx
)
189 patch_image preimage
= PATCH_IMAGE_INIT
, postimage
= PATCH_IMAGE_INIT
;
193 if (ctx
->opts
.hunk_cb
) {
194 error
= ctx
->opts
.hunk_cb(&hunk
->hunk
, ctx
->opts
.payload
);
198 ctx
->skipped_new_lines
+= hunk
->hunk
.new_lines
;
199 ctx
->skipped_old_lines
+= hunk
->hunk
.old_lines
;
207 for (i
= 0; i
< hunk
->line_count
; i
++) {
208 size_t linenum
= hunk
->line_start
+ i
;
209 git_diff_line
*line
= git_array_get(patch
->lines
, linenum
), *prev
;
212 error
= apply_err("preimage does not contain line %"PRIuZ
, linenum
);
216 switch (line
->origin
) {
217 case GIT_DIFF_LINE_CONTEXT_EOFNL
:
218 case GIT_DIFF_LINE_DEL_EOFNL
:
219 case GIT_DIFF_LINE_ADD_EOFNL
:
220 prev
= i
? git_array_get(patch
->lines
, linenum
- 1) : NULL
;
221 if (prev
&& prev
->content
[prev
->content_len
- 1] == '\n')
222 prev
->content_len
-= 1;
224 case GIT_DIFF_LINE_CONTEXT
:
225 if ((error
= git_vector_insert(&preimage
.lines
, line
)) < 0 ||
226 (error
= git_vector_insert(&postimage
.lines
, line
)) < 0)
229 case GIT_DIFF_LINE_DELETION
:
230 if ((error
= git_vector_insert(&preimage
.lines
, line
)) < 0)
233 case GIT_DIFF_LINE_ADDITION
:
234 if ((error
= git_vector_insert(&postimage
.lines
, line
)) < 0)
240 if (hunk
->hunk
.new_start
) {
241 line_num
= hunk
->hunk
.new_start
-
242 ctx
->skipped_new_lines
+
243 ctx
->skipped_old_lines
-
249 if (!find_hunk_linenum(&line_num
, image
, &preimage
, line_num
)) {
250 error
= apply_err("hunk at line %d did not apply",
251 hunk
->hunk
.new_start
);
255 error
= update_hunk(image
, line_num
, &preimage
, &postimage
);
258 patch_image_free(&preimage
);
259 patch_image_free(&postimage
);
264 static int apply_hunks(
269 apply_hunks_ctx
*ctx
)
271 git_patch_hunk
*hunk
;
277 if ((error
= patch_image_init_fromstr(&image
, source
, source_len
)) < 0)
280 git_array_foreach(patch
->hunks
, i
, hunk
) {
281 if ((error
= apply_hunk(&image
, patch
, hunk
, ctx
)) < 0)
285 git_vector_foreach(&image
.lines
, i
, line
)
286 git_buf_put(out
, line
->content
, line
->content_len
);
289 patch_image_free(&image
);
294 static int apply_binary_delta(
298 git_diff_binary_file
*binary_file
)
300 git_buf inflated
= GIT_BUF_INIT
;
303 /* no diff means identical contents */
304 if (binary_file
->datalen
== 0)
305 return git_buf_put(out
, source
, source_len
);
307 error
= git_zstream_inflatebuf(&inflated
,
308 binary_file
->data
, binary_file
->datalen
);
310 if (!error
&& inflated
.size
!= binary_file
->inflatedlen
) {
311 error
= apply_err("inflated delta does not match expected length");
312 git_buf_dispose(out
);
318 if (binary_file
->type
== GIT_DIFF_BINARY_DELTA
) {
322 error
= git_delta_apply(&data
, &data_len
, (void *)source
, source_len
,
323 (void *)inflated
.ptr
, inflated
.size
);
326 out
->size
= data_len
;
327 out
->asize
= data_len
;
329 else if (binary_file
->type
== GIT_DIFF_BINARY_LITERAL
) {
330 git_buf_swap(out
, &inflated
);
333 error
= apply_err("unknown binary delta type");
338 git_buf_dispose(&inflated
);
342 static int apply_binary(
348 git_buf reverse
= GIT_BUF_INIT
;
351 if (!patch
->binary
.contains_data
) {
352 error
= apply_err("patch does not contain binary data");
356 if (!patch
->binary
.old_file
.datalen
&& !patch
->binary
.new_file
.datalen
)
359 /* first, apply the new_file delta to the given source */
360 if ((error
= apply_binary_delta(out
, source
, source_len
,
361 &patch
->binary
.new_file
)) < 0)
364 /* second, apply the old_file delta to sanity check the result */
365 if ((error
= apply_binary_delta(&reverse
, out
->ptr
, out
->size
,
366 &patch
->binary
.old_file
)) < 0)
369 /* Verify that the resulting file with the reverse patch applied matches the source file */
370 if (source_len
!= reverse
.size
||
371 (source_len
&& memcmp(source
, reverse
.ptr
, source_len
) != 0)) {
372 error
= apply_err("binary patch did not apply cleanly");
378 git_buf_dispose(out
);
380 git_buf_dispose(&reverse
);
384 int git_apply__patch(
385 git_buf
*contents_out
,
387 unsigned int *mode_out
,
391 const git_apply_options
*given_opts
)
393 apply_hunks_ctx ctx
= { GIT_APPLY_OPTIONS_INIT
};
394 char *filename
= NULL
;
395 unsigned int mode
= 0;
398 assert(contents_out
&& filename_out
&& mode_out
&& (source
|| !source_len
) && patch
);
401 memcpy(&ctx
.opts
, given_opts
, sizeof(git_apply_options
));
403 *filename_out
= NULL
;
406 if (patch
->delta
->status
!= GIT_DELTA_DELETED
) {
407 const git_diff_file
*newfile
= &patch
->delta
->new_file
;
409 filename
= git__strdup(newfile
->path
);
410 mode
= newfile
->mode
?
411 newfile
->mode
: GIT_FILEMODE_BLOB
;
414 if (patch
->delta
->flags
& GIT_DIFF_FLAG_BINARY
)
415 error
= apply_binary(contents_out
, source
, source_len
, patch
);
416 else if (patch
->hunks
.size
)
417 error
= apply_hunks(contents_out
, source
, source_len
, patch
, &ctx
);
419 error
= git_buf_put(contents_out
, source
, source_len
);
424 if (patch
->delta
->status
== GIT_DELTA_DELETED
&&
425 git_buf_len(contents_out
) > 0) {
426 error
= apply_err("removal patch leaves file contents");
430 *filename_out
= filename
;
440 static int apply_one(
441 git_repository
*repo
,
442 git_reader
*preimage_reader
,
444 git_reader
*postimage_reader
,
445 git_index
*postimage
,
447 git_strmap
*removed_paths
,
449 const git_apply_options
*opts
)
451 git_patch
*patch
= NULL
;
452 git_buf pre_contents
= GIT_BUF_INIT
, post_contents
= GIT_BUF_INIT
;
453 const git_diff_delta
*delta
;
454 char *filename
= NULL
;
456 git_oid pre_id
, post_id
;
457 git_filemode_t pre_filemode
;
458 git_index_entry pre_entry
, post_entry
;
459 bool skip_preimage
= false;
462 if ((error
= git_patch_from_diff(&patch
, diff
, i
)) < 0)
465 delta
= git_patch_get_delta(patch
);
467 if (opts
->delta_cb
) {
468 error
= opts
->delta_cb(delta
, opts
->payload
);
479 * Ensure that the file has not been deleted or renamed if we're
480 * applying a modification delta.
482 if (delta
->status
!= GIT_DELTA_RENAMED
&&
483 delta
->status
!= GIT_DELTA_ADDED
) {
484 if (git_strmap_exists(removed_paths
, delta
->old_file
.path
)) {
485 error
= apply_err("path '%s' has been renamed or deleted", delta
->old_file
.path
);
491 * We may be applying a second delta to an already seen file. If so,
492 * use the already modified data in the postimage instead of the
493 * content from the index or working directory. (Don't do this in
494 * the case of a rename, which must be specified before additional
495 * deltas since we apply deltas to the target filename.)
497 if (delta
->status
!= GIT_DELTA_RENAMED
) {
498 if ((error
= git_reader_read(&pre_contents
, &pre_id
, &pre_filemode
,
499 postimage_reader
, delta
->old_file
.path
)) == 0) {
500 skip_preimage
= true;
501 } else if (error
== GIT_ENOTFOUND
) {
509 if (!skip_preimage
&& delta
->status
!= GIT_DELTA_ADDED
) {
510 error
= git_reader_read(&pre_contents
, &pre_id
, &pre_filemode
,
511 preimage_reader
, delta
->old_file
.path
);
513 /* ENOTFOUND means the preimage was not found; apply failed. */
514 if (error
== GIT_ENOTFOUND
)
515 error
= GIT_EAPPLYFAIL
;
517 /* When applying to BOTH, the index did not match the workdir. */
518 if (error
== GIT_READER_MISMATCH
)
519 error
= apply_err("%s: does not match index", delta
->old_file
.path
);
525 * We need to populate the preimage data structure with the
526 * contents that we are using as the preimage for this file.
527 * This allows us to apply patches to files that have been
528 * modified in the working directory. During checkout,
529 * we will use this expected preimage as the baseline, and
530 * limit checkout to only the paths affected by patch
531 * application. (Without this, we would fail to write the
532 * postimage contents to any file that had been modified
533 * from HEAD on-disk, even if the patch application succeeded.)
534 * Use the contents from the delta where available - some
535 * fields may not be available, like the old file mode (eg in
536 * an exact rename situation) so trust the patch parsing to
537 * validate and use the preimage data in that case.
540 memset(&pre_entry
, 0, sizeof(git_index_entry
));
541 pre_entry
.path
= delta
->old_file
.path
;
542 pre_entry
.mode
= delta
->old_file
.mode
? delta
->old_file
.mode
: pre_filemode
;
543 git_oid_cpy(&pre_entry
.id
, &pre_id
);
545 if ((error
= git_index_add(preimage
, &pre_entry
)) < 0)
550 if (delta
->status
!= GIT_DELTA_DELETED
) {
551 if ((error
= git_apply__patch(&post_contents
, &filename
, &mode
,
552 pre_contents
.ptr
, pre_contents
.size
, patch
, opts
)) < 0 ||
553 (error
= git_blob_create_from_buffer(&post_id
, repo
,
554 post_contents
.ptr
, post_contents
.size
)) < 0)
557 memset(&post_entry
, 0, sizeof(git_index_entry
));
558 post_entry
.path
= filename
;
559 post_entry
.mode
= mode
;
560 git_oid_cpy(&post_entry
.id
, &post_id
);
562 if ((error
= git_index_add(postimage
, &post_entry
)) < 0)
566 if (delta
->status
== GIT_DELTA_RENAMED
||
567 delta
->status
== GIT_DELTA_DELETED
)
568 error
= git_strmap_set(removed_paths
, delta
->old_file
.path
, (char *) delta
->old_file
.path
);
570 if (delta
->status
== GIT_DELTA_RENAMED
||
571 delta
->status
== GIT_DELTA_ADDED
)
572 git_strmap_delete(removed_paths
, delta
->new_file
.path
);
575 git_buf_dispose(&pre_contents
);
576 git_buf_dispose(&post_contents
);
578 git_patch_free(patch
);
583 static int apply_deltas(
584 git_repository
*repo
,
585 git_reader
*pre_reader
,
587 git_reader
*post_reader
,
588 git_index
*postimage
,
590 const git_apply_options
*opts
)
592 git_strmap
*removed_paths
;
596 if (git_strmap_new(&removed_paths
) < 0)
599 for (i
= 0; i
< git_diff_num_deltas(diff
); i
++) {
600 if ((error
= apply_one(repo
, pre_reader
, preimage
, post_reader
, postimage
, diff
, removed_paths
, i
, opts
)) < 0)
605 git_strmap_free(removed_paths
);
609 int git_apply_to_tree(
611 git_repository
*repo
,
614 const git_apply_options
*given_opts
)
616 git_index
*postimage
= NULL
;
617 git_reader
*pre_reader
= NULL
, *post_reader
= NULL
;
618 git_apply_options opts
= GIT_APPLY_OPTIONS_INIT
;
619 const git_diff_delta
*delta
;
623 assert(out
&& repo
&& preimage
&& diff
);
628 memcpy(&opts
, given_opts
, sizeof(git_apply_options
));
630 if ((error
= git_reader_for_tree(&pre_reader
, preimage
)) < 0)
634 * put the current tree into the postimage as-is - the diff will
635 * replace any entries contained therein
637 if ((error
= git_index_new(&postimage
)) < 0 ||
638 (error
= git_index_read_tree(postimage
, preimage
)) < 0 ||
639 (error
= git_reader_for_index(&post_reader
, repo
, postimage
)) < 0)
643 * Remove the old paths from the index before applying diffs -
644 * we need to do a full pass to remove them before adding deltas,
645 * in order to handle rename situations.
647 for (i
= 0; i
< git_diff_num_deltas(diff
); i
++) {
648 delta
= git_diff_get_delta(diff
, i
);
650 if (delta
->status
== GIT_DELTA_DELETED
||
651 delta
->status
== GIT_DELTA_RENAMED
) {
652 if ((error
= git_index_remove(postimage
,
653 delta
->old_file
.path
, 0)) < 0)
658 if ((error
= apply_deltas(repo
, pre_reader
, NULL
, post_reader
, postimage
, diff
, &opts
)) < 0)
665 git_index_free(postimage
);
667 git_reader_free(pre_reader
);
668 git_reader_free(post_reader
);
673 static int git_apply__to_workdir(
674 git_repository
*repo
,
677 git_index
*postimage
,
678 git_apply_location_t location
,
679 git_apply_options
*opts
)
681 git_vector paths
= GIT_VECTOR_INIT
;
682 git_checkout_options checkout_opts
= GIT_CHECKOUT_OPTIONS_INIT
;
683 const git_diff_delta
*delta
;
690 * Limit checkout to the paths affected by the diff; this ensures
691 * that other modifications in the working directory are unaffected.
693 if ((error
= git_vector_init(&paths
, git_diff_num_deltas(diff
), NULL
)) < 0)
696 for (i
= 0; i
< git_diff_num_deltas(diff
); i
++) {
697 delta
= git_diff_get_delta(diff
, i
);
699 if ((error
= git_vector_insert(&paths
, (void *)delta
->old_file
.path
)) < 0)
702 if (strcmp(delta
->old_file
.path
, delta
->new_file
.path
) &&
703 (error
= git_vector_insert(&paths
, (void *)delta
->new_file
.path
)) < 0)
707 checkout_opts
.checkout_strategy
|= GIT_CHECKOUT_SAFE
;
708 checkout_opts
.checkout_strategy
|= GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH
;
709 checkout_opts
.checkout_strategy
|= GIT_CHECKOUT_DONT_WRITE_INDEX
;
711 if (location
== GIT_APPLY_LOCATION_WORKDIR
)
712 checkout_opts
.checkout_strategy
|= GIT_CHECKOUT_DONT_UPDATE_INDEX
;
714 checkout_opts
.paths
.strings
= (char **)paths
.contents
;
715 checkout_opts
.paths
.count
= paths
.length
;
717 checkout_opts
.baseline_index
= preimage
;
719 error
= git_checkout_index(repo
, postimage
, &checkout_opts
);
722 git_vector_free(&paths
);
726 static int git_apply__to_index(
727 git_repository
*repo
,
730 git_index
*postimage
,
731 git_apply_options
*opts
)
733 git_index
*index
= NULL
;
734 const git_diff_delta
*delta
;
735 const git_index_entry
*entry
;
739 GIT_UNUSED(preimage
);
742 if ((error
= git_repository_index(&index
, repo
)) < 0)
745 /* Remove deleted (or renamed) paths from the index. */
746 for (i
= 0; i
< git_diff_num_deltas(diff
); i
++) {
747 delta
= git_diff_get_delta(diff
, i
);
749 if (delta
->status
== GIT_DELTA_DELETED
||
750 delta
->status
== GIT_DELTA_RENAMED
) {
751 if ((error
= git_index_remove(index
, delta
->old_file
.path
, 0)) < 0)
756 /* Then add the changes back to the index. */
757 for (i
= 0; i
< git_index_entrycount(postimage
); i
++) {
758 entry
= git_index_get_byindex(postimage
, i
);
760 if ((error
= git_index_add(index
, entry
)) < 0)
765 git_index_free(index
);
769 int git_apply_options_init(git_apply_options
*opts
, unsigned int version
)
771 GIT_INIT_STRUCTURE_FROM_TEMPLATE(
772 opts
, version
, git_apply_options
, GIT_APPLY_OPTIONS_INIT
);
777 * Handle the three application options ("locations"):
779 * GIT_APPLY_LOCATION_WORKDIR: the default, emulates `git apply`.
780 * Applies the diff only to the workdir items and ignores the index
783 * GIT_APPLY_LOCATION_INDEX: emulates `git apply --cached`.
784 * Applies the diff only to the index items and ignores the workdir
787 * GIT_APPLY_LOCATION_BOTH: emulates `git apply --index`.
788 * Applies the diff to both the index items and the working directory
793 git_repository
*repo
,
795 git_apply_location_t location
,
796 const git_apply_options
*given_opts
)
798 git_indexwriter indexwriter
= GIT_INDEXWRITER_INIT
;
799 git_index
*index
= NULL
, *preimage
= NULL
, *postimage
= NULL
;
800 git_reader
*pre_reader
= NULL
, *post_reader
= NULL
;
801 git_apply_options opts
= GIT_APPLY_OPTIONS_INIT
;
802 int error
= GIT_EINVALID
;
804 assert(repo
&& diff
);
806 GIT_ERROR_CHECK_VERSION(
807 given_opts
, GIT_APPLY_OPTIONS_VERSION
, "git_apply_options");
810 memcpy(&opts
, given_opts
, sizeof(git_apply_options
));
813 * by default, we apply a patch directly to the working directory;
814 * in `--cached` or `--index` mode, we apply to the contents already
818 case GIT_APPLY_LOCATION_BOTH
:
819 error
= git_reader_for_workdir(&pre_reader
, repo
, true);
821 case GIT_APPLY_LOCATION_INDEX
:
822 error
= git_reader_for_index(&pre_reader
, repo
, NULL
);
824 case GIT_APPLY_LOCATION_WORKDIR
:
825 error
= git_reader_for_workdir(&pre_reader
, repo
, false);
835 * Build the preimage and postimage (differences). Note that
836 * this is not the complete preimage or postimage, it only
837 * contains the files affected by the patch. We want to avoid
838 * having the full repo index, so we will limit our checkout
839 * to only write these files that were affected by the diff.
841 if ((error
= git_index_new(&preimage
)) < 0 ||
842 (error
= git_index_new(&postimage
)) < 0 ||
843 (error
= git_reader_for_index(&post_reader
, repo
, postimage
)) < 0)
846 if (!(opts
.flags
& GIT_APPLY_CHECK
))
847 if ((error
= git_repository_index(&index
, repo
)) < 0 ||
848 (error
= git_indexwriter_init(&indexwriter
, index
)) < 0)
851 if ((error
= apply_deltas(repo
, pre_reader
, preimage
, post_reader
, postimage
, diff
, &opts
)) < 0)
854 if ((opts
.flags
& GIT_APPLY_CHECK
))
858 case GIT_APPLY_LOCATION_BOTH
:
859 error
= git_apply__to_workdir(repo
, diff
, preimage
, postimage
, location
, &opts
);
861 case GIT_APPLY_LOCATION_INDEX
:
862 error
= git_apply__to_index(repo
, diff
, preimage
, postimage
, &opts
);
864 case GIT_APPLY_LOCATION_WORKDIR
:
865 error
= git_apply__to_workdir(repo
, diff
, preimage
, postimage
, location
, &opts
);
874 error
= git_indexwriter_commit(&indexwriter
);
877 git_indexwriter_cleanup(&indexwriter
);
878 git_index_free(postimage
);
879 git_index_free(preimage
);
880 git_index_free(index
);
881 git_reader_free(pre_reader
);
882 git_reader_free(post_reader
);