summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/apply.c39
-rw-r--r--tests/apply/apply_helpers.h22
-rw-r--r--tests/apply/both.c25
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);
+}