Commit f8b9493b521494f0fd928bf4708abd7450fcea44

Edward Thomson 2018-11-05T15:46:08

apply: test re-adding a file after removing it Ensure that we can add a file back after it's been removed. Update the renamed/deleted validation in application to not apply to deltas that are adding files to support this.

diff --git a/src/apply.c b/src/apply.c
index c337982..614baf7 100644
--- a/src/apply.c
+++ b/src/apply.c
@@ -458,23 +458,26 @@ static int apply_one(
 	}
 
 	/*
-	 * 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.  (Don't do this in
-	 * the case of a rename, which must be specified before additional
-	 * deltas since we apply deltas to the target filename.)
-	 *
-	 * Additionally, make sure that the file has not been deleted or renamed
-	 * out of the way; again, except in the rename case, since we support
-	 * renaming a single file into two target files.
+	 * Ensure that the file has not been deleted or renamed if we're
+	 * applying a modification delta.
 	 */
-	if (delta->status != GIT_DELTA_RENAMED) {
+	if (delta->status != GIT_DELTA_RENAMED &&
+	    delta->status != GIT_DELTA_ADDED) {
 		pos = git_strmap_lookup_index(removed_paths, delta->old_file.path);
 		if (git_strmap_valid_index(removed_paths, pos)) {
 			error = apply_err("path '%s' has been renamed or deleted", delta->old_file.path);
 			goto done;
 		}
+	}
 
+	/*
+	 * 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.  (Don't do this in
+	 * the case of a rename, which must be specified before additional
+	 * deltas since we apply 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;
diff --git a/tests/apply/apply_helpers.h b/tests/apply/apply_helpers.h
index a2f4dab..9cc9e44 100644
--- a/tests/apply/apply_helpers.h
+++ b/tests/apply/apply_helpers.h
@@ -394,6 +394,31 @@
 	" Put into a pot three quarts of water, three onions cut small, one\n" \
 	" spoonful of black pepper pounded, and two of salt, with two or three\n"
 
+#define DIFF_DELETE_AND_READD_FILE \
+	"diff --git a/asparagus.txt b/asparagus.txt\n" \
+	"deleted file mode 100644\n" \
+	"index f516580..0000000\n" \
+	"--- a/asparagus.txt\n" \
+	"+++ /dev/null\n" \
+	"@@ -1,10 +0,0 @@\n" \
+	"-ASPARAGUS SOUP!\n" \
+	"-\n" \
+	"-Take four large bunches of asparagus, scrape it nicely, cut off one inch\n" \
+	"-of the tops, and lay them in water, chop the stalks and put them on the\n" \
+	"-fire with a piece of bacon, a large onion cut up, and pepper and salt;\n" \
+	"-add two quarts of water, boil them till the stalks are quite soft, then\n" \
+	"-pulp them through a sieve, and strain the water to it, which must be put\n" \
+	"-back in the pot; put into it a chicken cut up, with the tops of\n" \
+	"-asparagus which had been laid by, boil it until these last articles are\n" \
+	"-sufficiently done, thicken with flour, butter and milk, and serve it up.\n" \
+	"diff --git a/asparagus.txt b/asparagus.txt\n" \
+	"new file mode 100644\n" \
+	"index 0000000..2dc7f8b\n" \
+	"--- /dev/null\n" \
+	"+++ b/asparagus.txt\n" \
+	"@@ -0,0 +1 @@\n" \
+	"+New file.\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 f5bb3e4..cac5acb 100644
--- a/tests/apply/both.c
+++ b/tests/apply/both.c
@@ -698,3 +698,28 @@ void test_apply_both__cant_modify_source_path_after_rename(void)
 
 	git_diff_free(diff);
 }
+
+void test_apply_both__readd_deleted_file(void)
+{
+	git_diff *diff;
+
+	struct merge_index_entry both_expected[] = {
+		{ 0100644, "2dc7f8b24ba27f3888368bd180df03ff4c6c6fab", 0, "asparagus.txt" },
+		{ 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 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_DELETE_AND_READD_FILE,
+		strlen(DIFF_DELETE_AND_READD_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);
+}