summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorEdward Thomson <ethomson@edwardthomson.com>2018-11-04 13:01:03 +0000
committerEdward Thomson <ethomson@edwardthomson.com>2018-11-05 15:53:59 +0000
commitdf4258ade061be35e8940bba27d2d80cdffed8df (patch)
tree3231247547ee75c518eac54ce7dfb91e0e540970 /src
parentc71e964a125ad22f403597037bbd424d72eb5174 (diff)
downloadlibgit2-df4258ade061be35e8940bba27d2d80cdffed8df.tar.gz
apply: handle multiple deltas to the same file
git allows a patch file to contain multiple deltas to the same file: although it does not produce files in this format itself, this could be the result of concatenating two different patch files that affected the same file. git apply behaves by applying this next delta to the existing postimage of the file. We should do the same. If we have previously seen a file, and produced a postimage for it, we will load that postimage and apply the current delta to that. If we have not, get the file from the preimage.
Diffstat (limited to 'src')
-rw-r--r--src/apply.c39
1 files changed, 32 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;
}