summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEdward Thomson <ethomson@edwardthomson.com>2018-07-01 15:14:36 +0100
committerEdward Thomson <ethomson@edwardthomson.com>2018-11-05 15:53:34 +0000
commit813f08029727f7d07989f63bb16dc798c5db7027 (patch)
tree26145dc7916d11d6981068c7998d633380cb43ec
parent0f4b2f028ab3a70b76b9bd67f073747df7078559 (diff)
downloadlibgit2-813f08029727f7d07989f63bb16dc798c5db7027.tar.gz
apply: validate workdir contents match index for BOTH
When applying to both the index and the working directory, ensure that the index contents match the working directory. This mirrors the requirement in `git apply --index`. This also means that - along with the prior commit that uses the working directory contents as the checkout baseline - we no longer expect conflicts during checkout. So remove the special-case error handling for checkout conflicts. (Any checkout conflict now would be because the file was actually modified between the start of patch application and the checkout.)
-rw-r--r--src/apply.c40
-rw-r--r--tests/apply/both.c37
2 files changed, 60 insertions, 17 deletions
diff --git a/src/apply.c b/src/apply.c
index d4c7dd0db..df1d0d8f2 100644
--- a/src/apply.c
+++ b/src/apply.c
@@ -406,15 +406,19 @@ static int apply_one(
delta = git_patch_get_delta(patch);
if (delta->status != GIT_DELTA_ADDED) {
- if ((error = git_reader_read(&pre_contents, &pre_id,
- preimage_reader, delta->old_file.path)) < 0) {
+ error = git_reader_read(&pre_contents, &pre_id,
+ preimage_reader, delta->old_file.path);
- /* ENOTFOUND is really an application error */
- if (error == GIT_ENOTFOUND)
- error = GIT_EAPPLYFAIL;
+ /* ENOTFOUND means the preimage was not found; apply failed. */
+ if (error == GIT_ENOTFOUND)
+ error = GIT_EAPPLYFAIL;
+ /* When applying to BOTH, the index did not match the workdir. */
+ if (error == GIT_READER_MISMATCH)
+ error = apply_err("%s: does not match index", delta->old_file.path);
+
+ if (error < 0)
goto done;
- }
/*
* We need to populate the preimage data structure with the
@@ -563,13 +567,6 @@ static int git_apply__to_workdir(
error = git_checkout_index(repo, postimage, &checkout_opts);
- /*
- * When there's a checkout conflict, the file in the working directory
- * has been modified. Upgrade this error to an application error.
- */
- if (error == GIT_ECONFLICT)
- error = GIT_EAPPLYFAIL;
-
done:
git_vector_free(&paths);
return error;
@@ -645,7 +642,7 @@ int git_apply(
git_reader *pre_reader = NULL;
git_apply_options opts = GIT_APPLY_OPTIONS_INIT;
size_t i;
- int error;
+ int error = GIT_EINVALID;
assert(repo && diff);
@@ -660,10 +657,19 @@ int git_apply(
* in `--cached` or `--index` mode, we apply to the contents already
* in the index.
*/
- if (opts.location == GIT_APPLY_LOCATION_WORKDIR)
- error = git_reader_for_workdir(&pre_reader, repo, false);
- else
+ switch (opts.location) {
+ case GIT_APPLY_LOCATION_BOTH:
+ error = git_reader_for_workdir(&pre_reader, repo, true);
+ break;
+ case GIT_APPLY_LOCATION_INDEX:
error = git_reader_for_index(&pre_reader, repo, NULL);
+ break;
+ case GIT_APPLY_LOCATION_WORKDIR:
+ error = git_reader_for_workdir(&pre_reader, repo, false);
+ break;
+ default:
+ assert(false);
+ }
if (error < 0)
goto done;
diff --git a/tests/apply/both.c b/tests/apply/both.c
index 1500e2c0c..2691af966 100644
--- a/tests/apply/both.c
+++ b/tests/apply/both.c
@@ -185,6 +185,43 @@ void test_apply_both__application_failure_leaves_index_unmodified(void)
git_diff_free(diff);
}
+void test_apply_both__index_must_match_workdir(void)
+{
+ git_diff *diff;
+ git_index *index;
+ git_index_entry idx_entry;
+ git_apply_options opts = GIT_APPLY_OPTIONS_INIT;
+
+ const char *diff_file = DIFF_MODIFY_TWO_FILES;
+
+ /*
+ * Append a line to the end of the file in both the index and the
+ * working directory. Although the appended line would allow for
+ * patch application in each, the line appended is different in
+ * each, so the application should not be allowed.
+ */
+ cl_git_append2file("merge-recursive/asparagus.txt",
+ "This is a modification.\n");
+
+ cl_git_pass(git_repository_index(&index, repo));
+
+ memset(&idx_entry, 0, sizeof(git_index_entry));
+ idx_entry.mode = 0100644;
+ idx_entry.path = "asparagus.txt";
+ cl_git_pass(git_oid_fromstr(&idx_entry.id, "06d3fefb8726ab1099acc76e02dfb85e034b2538"));
+ cl_git_pass(git_index_add(index, &idx_entry));
+
+ cl_git_pass(git_index_write(index));
+ git_index_free(index);
+
+ cl_git_pass(git_diff_from_buffer(&diff, diff_file, strlen(diff_file)));
+
+ opts.location = GIT_APPLY_LOCATION_BOTH;
+ cl_git_fail_with(GIT_EAPPLYFAIL, git_apply(repo, diff, &opts));
+
+ git_diff_free(diff);
+}
+
void test_apply_both__application_failure_leaves_workdir_unmodified(void)
{
git_diff *diff;