stash: don't allow apply with staged changes
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
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));