Commit 82b1c93d088319c4e385c11ce738b68103eab96c

Edward Thomson 2015-06-20T13:44:22

stash: don't allow apply with staged changes

diff --git a/include/git2/errors.h b/include/git2/errors.h
index 5ff0f35..a81aa05 100644
--- a/include/git2/errors.h
+++ b/include/git2/errors.h
@@ -47,6 +47,7 @@ typedef enum {
 	GIT_EPEEL           = -19,      /**< The requested peel operation is not possible */
 	GIT_EEOF            = -20,      /**< Unexpected EOF */
 	GIT_EINVALID        = -21,      /**< Invalid operation or input */
+	GIT_EUNCOMMITTED    = -22,	/**< Uncommitted changes in index prevented operation */
 
 	GIT_PASSTHROUGH     = -30,	/**< Internal only */
 	GIT_ITEROVER        = -31,	/**< Signals end of iteration with iterator */
diff --git a/src/stash.c b/src/stash.c
index 9010c47..14cbeb6 100644
--- a/src/stash.c
+++ b/src/stash.c
@@ -733,6 +733,29 @@ int git_stash_apply_init_options(git_stash_apply_options *opts, unsigned int ver
 		}							\
 	} while(false);
 
+static int ensure_clean_index(git_repository *repo, git_index *index)
+{
+	git_tree *head_tree = NULL;
+	git_diff *index_diff = NULL;
+	int error = 0;
+
+	if ((error = git_repository_head_tree(&head_tree, repo)) < 0 ||
+		(error = git_diff_tree_to_index(
+			&index_diff, repo, head_tree, index, NULL)) < 0)
+		goto done;
+
+	if (git_diff_num_deltas(index_diff) > 0) {
+		giterr_set(GITERR_STASH, "%d uncommitted changes exist in the index",
+			git_diff_num_deltas(index_diff));
+		error = GIT_EUNCOMMITTED;
+	}
+
+done:
+	git_diff_free(index_diff);
+	git_tree_free(head_tree);
+	return error;
+}
+
 int git_stash_apply(
 	git_repository *repo,
 	size_t index,
@@ -775,6 +798,9 @@ int git_stash_apply(
 
 	NOTIFY_PROGRESS(opts, GIT_STASH_APPLY_PROGRESS_ANALYZE_INDEX);
 
+	if ((error = ensure_clean_index(repo, repo_index)) < 0)
+		goto cleanup;
+
 	/* Restore index if required */
 	if ((opts.flags & GIT_STASH_APPLY_REINSTATE_INDEX) &&
 		git_oid_cmp(git_tree_id(stash_parent_tree), git_tree_id(index_tree))) {
diff --git a/tests/stash/apply.c b/tests/stash/apply.c
index bd6bb56..db145d5 100644
--- a/tests/stash/apply.c
+++ b/tests/stash/apply.c
@@ -118,13 +118,14 @@ void test_stash_apply__conflict_index_with_reinstate_index(void)
 	cl_git_rewritefile("stash/who", "nothing\n");
 	cl_git_pass(git_index_add_bypath(repo_index, "who"));
 	cl_git_pass(git_index_write(repo_index));
+	cl_repo_commit_from_index(NULL, repo, signature, 0, "Other commit");
 
 	cl_git_fail_with(git_stash_apply(repo, 0, &opts), GIT_ECONFLICT);
 
 	cl_assert_equal_i(git_index_has_conflicts(repo_index), 0);
 	assert_status(repo, "what", GIT_STATUS_CURRENT);
 	assert_status(repo, "how", GIT_STATUS_CURRENT);
-	assert_status(repo, "who", GIT_STATUS_INDEX_MODIFIED);
+	assert_status(repo, "who", GIT_STATUS_CURRENT);
 	assert_status(repo, "when", GIT_ENOTFOUND);
 	assert_status(repo, "why", GIT_ENOTFOUND);
 }
@@ -238,6 +239,22 @@ void test_stash_apply__conflict_commit_with_reinstate_index(void)
 	assert_status(repo, "why", GIT_STATUS_INDEX_NEW);
 }
 
+void test_stash_apply__fails_with_uncommitted_changes_in_index(void)
+{
+	cl_git_rewritefile("stash/who", "nothing\n");
+	cl_git_pass(git_index_add_bypath(repo_index, "who"));
+	cl_git_pass(git_index_write(repo_index));
+
+	cl_git_fail_with(git_stash_apply(repo, 0, NULL), GIT_EUNCOMMITTED);
+
+	cl_assert_equal_i(git_index_has_conflicts(repo_index), 0);
+	assert_status(repo, "what", GIT_STATUS_CURRENT);
+	assert_status(repo, "how", GIT_STATUS_CURRENT);
+	assert_status(repo, "who", GIT_STATUS_INDEX_MODIFIED);
+	assert_status(repo, "when", GIT_ENOTFOUND);
+	assert_status(repo, "why", GIT_ENOTFOUND);
+}
+
 void test_stash_apply__pop(void)
 {
 	cl_git_pass(git_stash_pop(repo, 0, NULL));