Commit d0dd3fcee7943321784f5745d34130cacd5d9fb4

Edward Thomson 2015-02-18T15:16:05

stash apply: check out a tree, not piecewise

diff --git a/src/stash.c b/src/stash.c
index f8bba3a..a316d18 100644
--- a/src/stash.c
+++ b/src/stash.c
@@ -652,7 +652,7 @@ static int apply_index(
 	git_tree *index_parent_tree,
 	git_tree *index_tree)
 {
-	git_index* unstashed_index = NULL;
+	git_index *unstashed_index = NULL;
 	git_merge_options options = GIT_MERGE_OPTIONS_INIT;
 	int error;
 	git_oid oid;
@@ -675,43 +675,22 @@ cleanup:
 
 static int apply_untracked(
 	git_repository *repo,
+	git_tree *start_index_tree,
 	git_tree *untracked_tree)
 {
 	git_checkout_options options = GIT_CHECKOUT_OPTIONS_INIT;
-	size_t i, count;
-	unsigned int status;
+	git_index *merged_index = NULL;
 	int error;
 
-	for (i = 0, count = git_tree_entrycount(untracked_tree); i < count; ++i) {
-		const git_tree_entry *entry = git_tree_entry_byindex(untracked_tree, i);
-		const char* path = git_tree_entry_name(entry);
-		error = git_status_file(&status, repo, path);
-		if (!error) {
-			giterr_set(GITERR_STASH, "Untracked or ignored file '%s' already exists", path);
-			return GIT_EEXISTS;
-		}
-	}
+	options.checkout_strategy =
+		GIT_CHECKOUT_SAFE | GIT_CHECKOUT_DONT_UPDATE_INDEX;
 
-	/*
-	 The untracked tree only contains the untracked / ignores files so checking
-	 it out would remove all other files in the workdir. Since git_checkout_tree()
-	 does not have a mode to leave removed files alone, we emulate it by checking
-	 out files from the untracked tree one by one.
-	 */
-
-	options.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_DONT_UPDATE_INDEX;
-	options.paths.count = 1;
-	for (i = 0, count = git_tree_entrycount(untracked_tree); i < count; ++i) {
-		const git_tree_entry *entry = git_tree_entry_byindex(untracked_tree, i);
-
-		const char* name = git_tree_entry_name(entry);
-		options.paths.strings = (char**)&name;
-		if ((error = git_checkout_tree(
-				repo, (git_object*)untracked_tree, &options)) < 0)
-			return error;
-	}
+	if ((error = git_merge_trees(&merged_index,
+			repo, NULL, start_index_tree, untracked_tree, NULL)) == 0)
+		error = git_checkout_index(repo, merged_index, &options);
 
-	return 0;
+	git_index_free(merged_index);
+	return error;
 }
 
 static int checkout_modified_notify_callback(
@@ -871,7 +850,7 @@ int git_stash_apply(
 
 	/* If applicable, restore untracked / ignored files in workdir */
 	if (untracked_tree) {
-		if ((error = apply_untracked(repo, untracked_tree)) < 0)
+		if ((error = apply_untracked(repo, start_index_tree, untracked_tree)) < 0)
 			goto cleanup;
 	}
 
diff --git a/tests/stash/apply.c b/tests/stash/apply.c
index 5f3cc9a..c0cdcab 100644
--- a/tests/stash/apply.c
+++ b/tests/stash/apply.c
@@ -121,7 +121,7 @@ void test_stash_apply__conflict_untracked_with_default(void)
 {
 	cl_git_mkfile("stash/when", "nothing\n");
 
-	cl_git_fail_with(git_stash_apply(repo, 0, GIT_APPLY_DEFAULT), GIT_EEXISTS);
+	cl_git_fail_with(git_stash_apply(repo, 0, GIT_APPLY_DEFAULT), GIT_EMERGECONFLICT);
 
 	cl_assert_equal_i(git_index_has_conflicts(repo_index), 0);
 	assert_status(repo, "what", GIT_STATUS_CURRENT);
@@ -134,7 +134,7 @@ void test_stash_apply__conflict_untracked_with_reinstate_index(void)
 {
 	cl_git_mkfile("stash/when", "nothing\n");
 
-	cl_git_fail_with(git_stash_apply(repo, 0, GIT_APPLY_REINSTATE_INDEX), GIT_EEXISTS);
+	cl_git_fail_with(git_stash_apply(repo, 0, GIT_APPLY_REINSTATE_INDEX), GIT_EMERGECONFLICT);
 
 	cl_assert_equal_i(git_index_has_conflicts(repo_index), 0);
 	assert_status(repo, "what", GIT_STATUS_CURRENT);