diff options
-rw-r--r-- | src/apply.c | 39 | ||||
-rw-r--r-- | tests/apply/apply_helpers.h | 22 | ||||
-rw-r--r-- | tests/apply/both.c | 25 |
3 files changed, 79 insertions, 7 deletions
diff --git a/src/apply.c b/src/apply.c index a8ca106c5..ca500c123 100644 --- a/src/apply.c +++ b/src/apply.c @@ -422,6 +422,7 @@ static int apply_one( git_repository *repo, git_reader *preimage_reader, git_index *preimage, + git_reader *postimage_reader, git_index *postimage, git_diff *diff, size_t i, @@ -435,6 +436,7 @@ static int apply_one( git_oid pre_id, post_id; git_filemode_t pre_filemode; git_index_entry pre_entry, post_entry; + bool skip_preimage = false; int error; if ((error = git_patch_from_diff(&patch, diff, i)) < 0) @@ -453,7 +455,26 @@ static int apply_one( } } - if (delta->status != GIT_DELTA_ADDED) { + /* + * 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. (Renames must be + * specified before additional deltas since we are applying deltas + * to the _target_ filename.) + */ + if (delta->status != GIT_DELTA_RENAMED) { + 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) { + giterr_clear(); + error = 0; + } else { + goto done; + } + } + + if (!skip_preimage && delta->status != GIT_DELTA_ADDED) { error = git_reader_read(&pre_contents, &pre_id, &pre_filemode, preimage_reader, delta->old_file.path); @@ -527,7 +548,7 @@ int git_apply_to_tree( const git_apply_options *given_opts) { git_index *postimage = NULL; - git_reader *pre_reader = NULL; + git_reader *pre_reader = NULL, *post_reader = NULL; git_apply_options opts = GIT_APPLY_OPTIONS_INIT; const git_diff_delta *delta; size_t i; @@ -548,7 +569,8 @@ int git_apply_to_tree( * replace any entries contained therein */ if ((error = git_index_new(&postimage)) < 0 || - (error = git_index_read_tree(postimage, preimage)) < 0) + (error = git_index_read_tree(postimage, preimage)) < 0 || + (error = git_reader_for_index(&post_reader, repo, postimage)) < 0) goto done; /* @@ -565,7 +587,7 @@ int git_apply_to_tree( } for (i = 0; i < git_diff_num_deltas(diff); i++) { - if ((error = apply_one(repo, pre_reader, NULL, postimage, diff, i, &opts)) < 0) + if ((error = apply_one(repo, pre_reader, NULL, post_reader, postimage, diff, i, &opts)) < 0) goto done; } @@ -576,6 +598,7 @@ done: git_index_free(postimage); git_reader_free(pre_reader); + git_reader_free(post_reader); return error; } @@ -700,7 +723,7 @@ int git_apply( { git_indexwriter indexwriter = GIT_INDEXWRITER_INIT; git_index *index = NULL, *preimage = NULL, *postimage = NULL; - git_reader *pre_reader = NULL; + git_reader *pre_reader = NULL, *post_reader = NULL; git_apply_options opts = GIT_APPLY_OPTIONS_INIT; size_t i; int error = GIT_EINVALID; @@ -743,7 +766,8 @@ int git_apply( * to only write these files that were affected by the diff. */ if ((error = git_index_new(&preimage)) < 0 || - (error = git_index_new(&postimage)) < 0) + (error = git_index_new(&postimage)) < 0 || + (error = git_reader_for_index(&post_reader, repo, postimage)) < 0) goto done; if ((error = git_repository_index(&index, repo)) < 0 || @@ -751,7 +775,7 @@ int git_apply( goto done; for (i = 0; i < git_diff_num_deltas(diff); i++) { - if ((error = apply_one(repo, pre_reader, preimage, postimage, diff, i, &opts)) < 0) + if ((error = apply_one(repo, pre_reader, preimage, post_reader, postimage, diff, i, &opts)) < 0) goto done; } @@ -780,6 +804,7 @@ done: git_index_free(preimage); git_index_free(index); git_reader_free(pre_reader); + git_reader_free(post_reader); return error; } diff --git a/tests/apply/apply_helpers.h b/tests/apply/apply_helpers.h index f6342fb8b..964f16723 100644 --- a/tests/apply/apply_helpers.h +++ b/tests/apply/apply_helpers.h @@ -255,6 +255,28 @@ "rename from asparagus.txt\n" \ "rename to 2.txt\n" +#define DIFF_TWO_DELTAS_ONE_FILE \ + "diff --git a/beef.txt b/beef.txt\n" \ + "index 68f6182..235069d 100644\n" \ + "--- a/beef.txt\n" \ + "+++ b/beef.txt\n" \ + "@@ -1,4 +1,4 @@\n" \ + "-BEEF SOUP.\n" \ + "+BEEF SOUP!\n" \ + "\n" \ + " Take the hind shin of beef, cut off all the flesh off the leg-bone,\n" \ + " which must be taken away entirely, or the soup will be greasy. Wash the\n" \ + "diff --git a/beef.txt b/beef.txt\n" \ + "index 68f6182..e059eb5 100644\n" \ + "--- a/beef.txt\n" \ + "+++ b/beef.txt\n" \ + "@@ -19,4 +19,4 @@ a ladle full of the soup, a little at a time; stirring it all the while.\n" \ + " Strain this browning and mix it well with the soup; take out the bundle\n" \ + " of thyme and parsley, put the nicest pieces of meat in your tureen, and\n" \ + " pour on the soup and vegetables; put in some toasted bread cut in dice,\n" \ + "-and serve it up.\n" \ + "+and serve it up!\n" + struct iterator_compare_data { struct merge_index_entry *expected; size_t cnt; diff --git a/tests/apply/both.c b/tests/apply/both.c index 0a417723f..faafc9a50 100644 --- a/tests/apply/both.c +++ b/tests/apply/both.c @@ -574,3 +574,28 @@ void test_apply_both__rename_1_to_2(void) git_diff_free(diff); } + +void test_apply_both__two_deltas_one_file(void) +{ + git_diff *diff; + + struct merge_index_entry both_expected[] = { + { 0100644, "f51658077d85f2264fa179b4d0848268cb3475c3", 0, "asparagus.txt" }, + { 0100644, "0a9fd4415635e72573f0f6b5e68084cfe18f5075", 0, "beef.txt" }, + { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" }, + { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" }, + { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" }, + { 0100644, "94d2c01087f48213bd157222d54edfefd77c9bba", 0, "veal.txt" } + }; + size_t both_expected_cnt = sizeof(both_expected) / + sizeof(struct merge_index_entry); + + cl_git_pass(git_diff_from_buffer(&diff, DIFF_TWO_DELTAS_ONE_FILE, + strlen(DIFF_TWO_DELTAS_ONE_FILE))); + cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL)); + + validate_apply_index(repo, both_expected, both_expected_cnt); + validate_apply_workdir(repo, both_expected, both_expected_cnt); + + git_diff_free(diff); +} |