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 if (git_pool_init(&out
->pool
, sizeof(git_diff_line
)) < 0)
72 for (start
= in
; start
< in
+ in_len
; start
= end
) {
73 end
= memchr(start
, '\n', in_len
- (start
- in
));
78 else if (end
< in
+ in_len
)
81 line
= git_pool_mallocz(&out
->pool
, 1);
82 GIT_ERROR_CHECK_ALLOC(line
);
84 if (git_vector_insert(&out
->lines
, line
) < 0)
87 patch_line_init(line
, start
, (end
- start
), (start
- in
));
93 static void patch_image_free(patch_image
*image
)
98 git_pool_clear(&image
->pool
);
99 git_vector_free(&image
->lines
);
102 static bool match_hunk(
104 patch_image
*preimage
,
110 /* Ensure this hunk is within the image boundaries. */
111 if (git_vector_length(&preimage
->lines
) + linenum
>
112 git_vector_length(&image
->lines
))
117 /* Check exact match. */
118 for (i
= 0; i
< git_vector_length(&preimage
->lines
); i
++) {
119 git_diff_line
*preimage_line
= git_vector_get(&preimage
->lines
, i
);
120 git_diff_line
*image_line
= git_vector_get(&image
->lines
, linenum
+ i
);
122 if (preimage_line
->content_len
!= image_line
->content_len
||
123 memcmp(preimage_line
->content
, image_line
->content
, image_line
->content_len
) != 0) {
132 static bool find_hunk_linenum(
135 patch_image
*preimage
,
138 size_t max
= git_vector_length(&image
->lines
);
144 match
= match_hunk(image
, preimage
, linenum
);
150 static int update_hunk(
153 patch_image
*preimage
,
154 patch_image
*postimage
)
156 size_t postlen
= git_vector_length(&postimage
->lines
);
157 size_t prelen
= git_vector_length(&preimage
->lines
);
161 if (postlen
> prelen
)
162 error
= git_vector_insert_null(
163 &image
->lines
, linenum
, (postlen
- prelen
));
164 else if (prelen
> postlen
)
165 error
= git_vector_remove_range(
166 &image
->lines
, linenum
, (prelen
- postlen
));
173 for (i
= 0; i
< git_vector_length(&postimage
->lines
); i
++) {
174 image
->lines
.contents
[linenum
+ i
] =
175 git_vector_get(&postimage
->lines
, i
);
182 git_apply_options opts
;
183 size_t skipped_new_lines
;
184 size_t skipped_old_lines
;
187 static int apply_hunk(
190 git_patch_hunk
*hunk
,
191 apply_hunks_ctx
*ctx
)
193 patch_image preimage
= PATCH_IMAGE_INIT
, postimage
= PATCH_IMAGE_INIT
;
197 if (ctx
->opts
.hunk_cb
) {
198 error
= ctx
->opts
.hunk_cb(&hunk
->hunk
, ctx
->opts
.payload
);
202 ctx
->skipped_new_lines
+= hunk
->hunk
.new_lines
;
203 ctx
->skipped_old_lines
+= hunk
->hunk
.old_lines
;
211 for (i
= 0; i
< hunk
->line_count
; i
++) {
212 size_t linenum
= hunk
->line_start
+ i
;
213 git_diff_line
*line
= git_array_get(patch
->lines
, linenum
), *prev
;
216 error
= apply_err("preimage does not contain line %"PRIuZ
, linenum
);
220 switch (line
->origin
) {
221 case GIT_DIFF_LINE_CONTEXT_EOFNL
:
222 case GIT_DIFF_LINE_DEL_EOFNL
:
223 case GIT_DIFF_LINE_ADD_EOFNL
:
224 prev
= i
? git_array_get(patch
->lines
, linenum
- 1) : NULL
;
225 if (prev
&& prev
->content
[prev
->content_len
- 1] == '\n')
226 prev
->content_len
-= 1;
228 case GIT_DIFF_LINE_CONTEXT
:
229 if ((error
= git_vector_insert(&preimage
.lines
, line
)) < 0 ||
230 (error
= git_vector_insert(&postimage
.lines
, line
)) < 0)
233 case GIT_DIFF_LINE_DELETION
:
234 if ((error
= git_vector_insert(&preimage
.lines
, line
)) < 0)
237 case GIT_DIFF_LINE_ADDITION
:
238 if ((error
= git_vector_insert(&postimage
.lines
, line
)) < 0)
244 if (hunk
->hunk
.new_start
) {
245 line_num
= hunk
->hunk
.new_start
-
246 ctx
->skipped_new_lines
+
247 ctx
->skipped_old_lines
-
253 if (!find_hunk_linenum(&line_num
, image
, &preimage
, line_num
)) {
254 error
= apply_err("hunk at line %d did not apply",
255 hunk
->hunk
.new_start
);
259 error
= update_hunk(image
, line_num
, &preimage
, &postimage
);
262 patch_image_free(&preimage
);
263 patch_image_free(&postimage
);
268 static int apply_hunks(
273 apply_hunks_ctx
*ctx
)
275 git_patch_hunk
*hunk
;
281 if ((error
= patch_image_init_fromstr(&image
, source
, source_len
)) < 0)
284 git_array_foreach(patch
->hunks
, i
, hunk
) {
285 if ((error
= apply_hunk(&image
, patch
, hunk
, ctx
)) < 0)
289 git_vector_foreach(&image
.lines
, i
, line
)
290 git_buf_put(out
, line
->content
, line
->content_len
);
293 patch_image_free(&image
);
298 static int apply_binary_delta(
302 git_diff_binary_file
*binary_file
)
304 git_buf inflated
= GIT_BUF_INIT
;
307 /* no diff means identical contents */
308 if (binary_file
->datalen
== 0)
309 return git_buf_put(out
, source
, source_len
);
311 error
= git_zstream_inflatebuf(&inflated
,
312 binary_file
->data
, binary_file
->datalen
);
314 if (!error
&& inflated
.size
!= binary_file
->inflatedlen
) {
315 error
= apply_err("inflated delta does not match expected length");
316 git_buf_dispose(out
);
322 if (binary_file
->type
== GIT_DIFF_BINARY_DELTA
) {
326 error
= git_delta_apply(&data
, &data_len
, (void *)source
, source_len
,
327 (void *)inflated
.ptr
, inflated
.size
);
330 out
->size
= data_len
;
331 out
->asize
= data_len
;
333 else if (binary_file
->type
== GIT_DIFF_BINARY_LITERAL
) {
334 git_buf_swap(out
, &inflated
);
337 error
= apply_err("unknown binary delta type");
342 git_buf_dispose(&inflated
);
346 static int apply_binary(
352 git_buf reverse
= GIT_BUF_INIT
;
355 if (!patch
->binary
.contains_data
) {
356 error
= apply_err("patch does not contain binary data");
360 if (!patch
->binary
.old_file
.datalen
&& !patch
->binary
.new_file
.datalen
)
363 /* first, apply the new_file delta to the given source */
364 if ((error
= apply_binary_delta(out
, source
, source_len
,
365 &patch
->binary
.new_file
)) < 0)
368 /* second, apply the old_file delta to sanity check the result */
369 if ((error
= apply_binary_delta(&reverse
, out
->ptr
, out
->size
,
370 &patch
->binary
.old_file
)) < 0)
373 /* Verify that the resulting file with the reverse patch applied matches the source file */
374 if (source_len
!= reverse
.size
||
375 (source_len
&& memcmp(source
, reverse
.ptr
, source_len
) != 0)) {
376 error
= apply_err("binary patch did not apply cleanly");
382 git_buf_dispose(out
);
384 git_buf_dispose(&reverse
);
388 int git_apply__patch(
389 git_buf
*contents_out
,
391 unsigned int *mode_out
,
395 const git_apply_options
*given_opts
)
397 apply_hunks_ctx ctx
= { GIT_APPLY_OPTIONS_INIT
};
398 char *filename
= NULL
;
399 unsigned int mode
= 0;
402 assert(contents_out
&& filename_out
&& mode_out
&& (source
|| !source_len
) && patch
);
405 memcpy(&ctx
.opts
, given_opts
, sizeof(git_apply_options
));
407 *filename_out
= NULL
;
410 if (patch
->delta
->status
!= GIT_DELTA_DELETED
) {
411 const git_diff_file
*newfile
= &patch
->delta
->new_file
;
413 filename
= git__strdup(newfile
->path
);
414 mode
= newfile
->mode
?
415 newfile
->mode
: GIT_FILEMODE_BLOB
;
418 if (patch
->delta
->flags
& GIT_DIFF_FLAG_BINARY
)
419 error
= apply_binary(contents_out
, source
, source_len
, patch
);
420 else if (patch
->hunks
.size
)
421 error
= apply_hunks(contents_out
, source
, source_len
, patch
, &ctx
);
423 error
= git_buf_put(contents_out
, source
, source_len
);
428 if (patch
->delta
->status
== GIT_DELTA_DELETED
&&
429 git_buf_len(contents_out
) > 0) {
430 error
= apply_err("removal patch leaves file contents");
434 *filename_out
= filename
;
444 static int apply_one(
445 git_repository
*repo
,
446 git_reader
*preimage_reader
,
448 git_reader
*postimage_reader
,
449 git_index
*postimage
,
451 git_strmap
*removed_paths
,
453 const git_apply_options
*opts
)
455 git_patch
*patch
= NULL
;
456 git_buf pre_contents
= GIT_BUF_INIT
, post_contents
= GIT_BUF_INIT
;
457 const git_diff_delta
*delta
;
458 char *filename
= NULL
;
460 git_oid pre_id
, post_id
;
461 git_filemode_t pre_filemode
;
462 git_index_entry pre_entry
, post_entry
;
463 bool skip_preimage
= false;
466 if ((error
= git_patch_from_diff(&patch
, diff
, i
)) < 0)
469 delta
= git_patch_get_delta(patch
);
471 if (opts
->delta_cb
) {
472 error
= opts
->delta_cb(delta
, opts
->payload
);
483 * Ensure that the file has not been deleted or renamed if we're
484 * applying a modification delta.
486 if (delta
->status
!= GIT_DELTA_RENAMED
&&
487 delta
->status
!= GIT_DELTA_ADDED
) {
488 if (git_strmap_exists(removed_paths
, delta
->old_file
.path
)) {
489 error
= apply_err("path '%s' has been renamed or deleted", delta
->old_file
.path
);
495 * We may be applying a second delta to an already seen file. If so,
496 * use the already modified data in the postimage instead of the
497 * content from the index or working directory. (Don't do this in
498 * the case of a rename, which must be specified before additional
499 * deltas since we apply deltas to the target filename.)
501 if (delta
->status
!= GIT_DELTA_RENAMED
) {
502 if ((error
= git_reader_read(&pre_contents
, &pre_id
, &pre_filemode
,
503 postimage_reader
, delta
->old_file
.path
)) == 0) {
504 skip_preimage
= true;
505 } else if (error
== GIT_ENOTFOUND
) {
513 if (!skip_preimage
&& delta
->status
!= GIT_DELTA_ADDED
) {
514 error
= git_reader_read(&pre_contents
, &pre_id
, &pre_filemode
,
515 preimage_reader
, delta
->old_file
.path
);
517 /* ENOTFOUND means the preimage was not found; apply failed. */
518 if (error
== GIT_ENOTFOUND
)
519 error
= GIT_EAPPLYFAIL
;
521 /* When applying to BOTH, the index did not match the workdir. */
522 if (error
== GIT_READER_MISMATCH
)
523 error
= apply_err("%s: does not match index", delta
->old_file
.path
);
529 * We need to populate the preimage data structure with the
530 * contents that we are using as the preimage for this file.
531 * This allows us to apply patches to files that have been
532 * modified in the working directory. During checkout,
533 * we will use this expected preimage as the baseline, and
534 * limit checkout to only the paths affected by patch
535 * application. (Without this, we would fail to write the
536 * postimage contents to any file that had been modified
537 * from HEAD on-disk, even if the patch application succeeded.)
538 * Use the contents from the delta where available - some
539 * fields may not be available, like the old file mode (eg in
540 * an exact rename situation) so trust the patch parsing to
541 * validate and use the preimage data in that case.
544 memset(&pre_entry
, 0, sizeof(git_index_entry
));
545 pre_entry
.path
= delta
->old_file
.path
;
546 pre_entry
.mode
= delta
->old_file
.mode
? delta
->old_file
.mode
: pre_filemode
;
547 git_oid_cpy(&pre_entry
.id
, &pre_id
);
549 if ((error
= git_index_add(preimage
, &pre_entry
)) < 0)
554 if (delta
->status
!= GIT_DELTA_DELETED
) {
555 if ((error
= git_apply__patch(&post_contents
, &filename
, &mode
,
556 pre_contents
.ptr
, pre_contents
.size
, patch
, opts
)) < 0 ||
557 (error
= git_blob_create_from_buffer(&post_id
, repo
,
558 post_contents
.ptr
, post_contents
.size
)) < 0)
561 memset(&post_entry
, 0, sizeof(git_index_entry
));
562 post_entry
.path
= filename
;
563 post_entry
.mode
= mode
;
564 git_oid_cpy(&post_entry
.id
, &post_id
);
566 if ((error
= git_index_add(postimage
, &post_entry
)) < 0)
570 if (delta
->status
== GIT_DELTA_RENAMED
||
571 delta
->status
== GIT_DELTA_DELETED
)
572 error
= git_strmap_set(removed_paths
, delta
->old_file
.path
, (char *) delta
->old_file
.path
);
574 if (delta
->status
== GIT_DELTA_RENAMED
||
575 delta
->status
== GIT_DELTA_ADDED
)
576 git_strmap_delete(removed_paths
, delta
->new_file
.path
);
579 git_buf_dispose(&pre_contents
);
580 git_buf_dispose(&post_contents
);
582 git_patch_free(patch
);
587 static int apply_deltas(
588 git_repository
*repo
,
589 git_reader
*pre_reader
,
591 git_reader
*post_reader
,
592 git_index
*postimage
,
594 const git_apply_options
*opts
)
596 git_strmap
*removed_paths
;
600 if (git_strmap_new(&removed_paths
) < 0)
603 for (i
= 0; i
< git_diff_num_deltas(diff
); i
++) {
604 if ((error
= apply_one(repo
, pre_reader
, preimage
, post_reader
, postimage
, diff
, removed_paths
, i
, opts
)) < 0)
609 git_strmap_free(removed_paths
);
613 int git_apply_to_tree(
615 git_repository
*repo
,
618 const git_apply_options
*given_opts
)
620 git_index
*postimage
= NULL
;
621 git_reader
*pre_reader
= NULL
, *post_reader
= NULL
;
622 git_apply_options opts
= GIT_APPLY_OPTIONS_INIT
;
623 const git_diff_delta
*delta
;
627 assert(out
&& repo
&& preimage
&& diff
);
632 memcpy(&opts
, given_opts
, sizeof(git_apply_options
));
634 if ((error
= git_reader_for_tree(&pre_reader
, preimage
)) < 0)
638 * put the current tree into the postimage as-is - the diff will
639 * replace any entries contained therein
641 if ((error
= git_index_new(&postimage
)) < 0 ||
642 (error
= git_index_read_tree(postimage
, preimage
)) < 0 ||
643 (error
= git_reader_for_index(&post_reader
, repo
, postimage
)) < 0)
647 * Remove the old paths from the index before applying diffs -
648 * we need to do a full pass to remove them before adding deltas,
649 * in order to handle rename situations.
651 for (i
= 0; i
< git_diff_num_deltas(diff
); i
++) {
652 delta
= git_diff_get_delta(diff
, i
);
654 if (delta
->status
== GIT_DELTA_DELETED
||
655 delta
->status
== GIT_DELTA_RENAMED
) {
656 if ((error
= git_index_remove(postimage
,
657 delta
->old_file
.path
, 0)) < 0)
662 if ((error
= apply_deltas(repo
, pre_reader
, NULL
, post_reader
, postimage
, diff
, &opts
)) < 0)
669 git_index_free(postimage
);
671 git_reader_free(pre_reader
);
672 git_reader_free(post_reader
);
677 static int git_apply__to_workdir(
678 git_repository
*repo
,
681 git_index
*postimage
,
682 git_apply_location_t location
,
683 git_apply_options
*opts
)
685 git_vector paths
= GIT_VECTOR_INIT
;
686 git_checkout_options checkout_opts
= GIT_CHECKOUT_OPTIONS_INIT
;
687 const git_diff_delta
*delta
;
694 * Limit checkout to the paths affected by the diff; this ensures
695 * that other modifications in the working directory are unaffected.
697 if ((error
= git_vector_init(&paths
, git_diff_num_deltas(diff
), NULL
)) < 0)
700 for (i
= 0; i
< git_diff_num_deltas(diff
); i
++) {
701 delta
= git_diff_get_delta(diff
, i
);
703 if ((error
= git_vector_insert(&paths
, (void *)delta
->old_file
.path
)) < 0)
706 if (strcmp(delta
->old_file
.path
, delta
->new_file
.path
) &&
707 (error
= git_vector_insert(&paths
, (void *)delta
->new_file
.path
)) < 0)
711 checkout_opts
.checkout_strategy
|= GIT_CHECKOUT_SAFE
;
712 checkout_opts
.checkout_strategy
|= GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH
;
713 checkout_opts
.checkout_strategy
|= GIT_CHECKOUT_DONT_WRITE_INDEX
;
715 if (location
== GIT_APPLY_LOCATION_WORKDIR
)
716 checkout_opts
.checkout_strategy
|= GIT_CHECKOUT_DONT_UPDATE_INDEX
;
718 checkout_opts
.paths
.strings
= (char **)paths
.contents
;
719 checkout_opts
.paths
.count
= paths
.length
;
721 checkout_opts
.baseline_index
= preimage
;
723 error
= git_checkout_index(repo
, postimage
, &checkout_opts
);
726 git_vector_free(&paths
);
730 static int git_apply__to_index(
731 git_repository
*repo
,
734 git_index
*postimage
,
735 git_apply_options
*opts
)
737 git_index
*index
= NULL
;
738 const git_diff_delta
*delta
;
739 const git_index_entry
*entry
;
743 GIT_UNUSED(preimage
);
746 if ((error
= git_repository_index(&index
, repo
)) < 0)
749 /* Remove deleted (or renamed) paths from the index. */
750 for (i
= 0; i
< git_diff_num_deltas(diff
); i
++) {
751 delta
= git_diff_get_delta(diff
, i
);
753 if (delta
->status
== GIT_DELTA_DELETED
||
754 delta
->status
== GIT_DELTA_RENAMED
) {
755 if ((error
= git_index_remove(index
, delta
->old_file
.path
, 0)) < 0)
760 /* Then add the changes back to the index. */
761 for (i
= 0; i
< git_index_entrycount(postimage
); i
++) {
762 entry
= git_index_get_byindex(postimage
, i
);
764 if ((error
= git_index_add(index
, entry
)) < 0)
769 git_index_free(index
);
773 int git_apply_options_init(git_apply_options
*opts
, unsigned int version
)
775 GIT_INIT_STRUCTURE_FROM_TEMPLATE(
776 opts
, version
, git_apply_options
, GIT_APPLY_OPTIONS_INIT
);
781 * Handle the three application options ("locations"):
783 * GIT_APPLY_LOCATION_WORKDIR: the default, emulates `git apply`.
784 * Applies the diff only to the workdir items and ignores the index
787 * GIT_APPLY_LOCATION_INDEX: emulates `git apply --cached`.
788 * Applies the diff only to the index items and ignores the workdir
791 * GIT_APPLY_LOCATION_BOTH: emulates `git apply --index`.
792 * Applies the diff to both the index items and the working directory
797 git_repository
*repo
,
799 git_apply_location_t location
,
800 const git_apply_options
*given_opts
)
802 git_indexwriter indexwriter
= GIT_INDEXWRITER_INIT
;
803 git_index
*index
= NULL
, *preimage
= NULL
, *postimage
= NULL
;
804 git_reader
*pre_reader
= NULL
, *post_reader
= NULL
;
805 git_apply_options opts
= GIT_APPLY_OPTIONS_INIT
;
806 int error
= GIT_EINVALID
;
808 assert(repo
&& diff
);
810 GIT_ERROR_CHECK_VERSION(
811 given_opts
, GIT_APPLY_OPTIONS_VERSION
, "git_apply_options");
814 memcpy(&opts
, given_opts
, sizeof(git_apply_options
));
817 * by default, we apply a patch directly to the working directory;
818 * in `--cached` or `--index` mode, we apply to the contents already
822 case GIT_APPLY_LOCATION_BOTH
:
823 error
= git_reader_for_workdir(&pre_reader
, repo
, true);
825 case GIT_APPLY_LOCATION_INDEX
:
826 error
= git_reader_for_index(&pre_reader
, repo
, NULL
);
828 case GIT_APPLY_LOCATION_WORKDIR
:
829 error
= git_reader_for_workdir(&pre_reader
, repo
, false);
839 * Build the preimage and postimage (differences). Note that
840 * this is not the complete preimage or postimage, it only
841 * contains the files affected by the patch. We want to avoid
842 * having the full repo index, so we will limit our checkout
843 * to only write these files that were affected by the diff.
845 if ((error
= git_index_new(&preimage
)) < 0 ||
846 (error
= git_index_new(&postimage
)) < 0 ||
847 (error
= git_reader_for_index(&post_reader
, repo
, postimage
)) < 0)
850 if (!(opts
.flags
& GIT_APPLY_CHECK
))
851 if ((error
= git_repository_index(&index
, repo
)) < 0 ||
852 (error
= git_indexwriter_init(&indexwriter
, index
)) < 0)
855 if ((error
= apply_deltas(repo
, pre_reader
, preimage
, post_reader
, postimage
, diff
, &opts
)) < 0)
858 if ((opts
.flags
& GIT_APPLY_CHECK
))
862 case GIT_APPLY_LOCATION_BOTH
:
863 error
= git_apply__to_workdir(repo
, diff
, preimage
, postimage
, location
, &opts
);
865 case GIT_APPLY_LOCATION_INDEX
:
866 error
= git_apply__to_index(repo
, diff
, preimage
, postimage
, &opts
);
868 case GIT_APPLY_LOCATION_WORKDIR
:
869 error
= git_apply__to_workdir(repo
, diff
, preimage
, postimage
, location
, &opts
);
878 error
= git_indexwriter_commit(&indexwriter
);
881 git_indexwriter_cleanup(&indexwriter
);
882 git_index_free(postimage
);
883 git_index_free(preimage
);
884 git_index_free(index
);
885 git_reader_free(pre_reader
);
886 git_reader_free(post_reader
);