checkout: FORCE doesn't halt on dirty index If the index is dirty, allow `GIT_CHECKOUT_FORCE` to obliterate unsaved changes. This is in keeping with its name and description.
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 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
diff --git a/src/checkout.c b/src/checkout.c
index d72f227..2a4e5c4 100644
--- a/src/checkout.c
+++ b/src/checkout.c
@@ -2412,16 +2412,25 @@ static int checkout_data_init(
* and those should not be overwritten.)
*/
if (data->index != git_iterator_index(target)) {
- if ((error = git_index_read_safely(data->index)) < 0)
- goto cleanup;
-
- /* cannot checkout if unresolved conflicts exist */
- if ((data->opts.checkout_strategy & GIT_CHECKOUT_FORCE) == 0 &&
- git_index_has_conflicts(data->index)) {
- error = GIT_ECONFLICT;
- giterr_set(GITERR_CHECKOUT,
- "unresolved conflicts exist in the index");
- goto cleanup;
+ if (data->opts.checkout_strategy & GIT_CHECKOUT_FORCE) {
+ /* When forcing, we can blindly re-read the index */
+ if ((error = git_index_read(data->index, false)) < 0)
+ goto cleanup;
+ } else {
+ /*
+ * When not being forced, we need to check for unresolved
+ * conflicts and unsaved changes in the index before
+ * proceeding.
+ */
+ if (git_index_has_conflicts(data->index)) {
+ error = GIT_ECONFLICT;
+ giterr_set(GITERR_CHECKOUT,
+ "unresolved conflicts exist in the index");
+ goto cleanup;
+ }
+
+ if ((error = git_index_read_safely(data->index)) < 0)
+ goto cleanup;
}
/* clean conflict data in the current index */
diff --git a/tests/checkout/tree.c b/tests/checkout/tree.c
index 97a2fb4..a78bf82 100644
--- a/tests/checkout/tree.c
+++ b/tests/checkout/tree.c
@@ -672,12 +672,9 @@ void test_checkout_tree__can_cancel_checkout_from_notify(void)
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
git_oid oid;
git_object *obj = NULL;
- git_index *index = NULL;
assert_on_branch(g_repo, "master");
- cl_git_pass(git_repository_index(&index, g_repo));
-
cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir"));
cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY));
@@ -704,8 +701,6 @@ void test_checkout_tree__can_cancel_checkout_from_notify(void)
else
cl_assert_equal_i(4, ca.count);
- cl_git_pass(git_index_read(index, 1));
-
/* and again with a different stopping point and return code */
ca.filename = "README";
ca.error = 123;
@@ -721,7 +716,6 @@ void test_checkout_tree__can_cancel_checkout_from_notify(void)
cl_assert_equal_i(1, ca.count);
git_object_free(obj);
- git_index_free(index);
}
void test_checkout_tree__can_checkout_with_last_workdir_item_missing(void)
@@ -1502,11 +1496,9 @@ void test_checkout_tree__baseline_is_empty_when_no_index(void)
git_reference *head;
git_object *obj;
size_t conflicts = 0;
- git_index *index;
assert_on_branch(g_repo, "master");
- cl_git_pass(git_repository_index(&index, g_repo));
cl_git_pass(git_repository_head(&head, g_repo));
cl_git_pass(git_reference_peel(&obj, head, GIT_OBJ_COMMIT));
@@ -1525,8 +1517,6 @@ void test_checkout_tree__baseline_is_empty_when_no_index(void)
cl_git_fail_with(GIT_ECONFLICT, git_checkout_tree(g_repo, obj, &opts));
cl_assert_equal_i(4, conflicts);
- cl_git_pass(git_index_read(index, 1));
-
/* but force should succeed and update the index */
opts.checkout_strategy |= GIT_CHECKOUT_FORCE;
cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
@@ -1535,7 +1525,6 @@ void test_checkout_tree__baseline_is_empty_when_no_index(void)
git_object_free(obj);
git_reference_free(head);
- git_index_free(index);
}
void test_checkout_tree__mode_change_is_force_updated(void)
diff --git a/tests/checkout/typechange.c b/tests/checkout/typechange.c
index 5cd9508..7299f8d 100644
--- a/tests/checkout/typechange.c
+++ b/tests/checkout/typechange.c
@@ -259,7 +259,6 @@ static int make_submodule_dirty(git_submodule *sm, const char *name, void *paylo
void test_checkout_typechange__checkout_with_conflicts(void)
{
int i;
- git_index *index;
git_object *obj;
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
notify_counts cts = {0};
@@ -290,10 +289,6 @@ void test_checkout_typechange__checkout_with_conflicts(void)
cl_assert_equal_i(cts.updates, 0);
cl_assert_equal_i(cts.ignored, 0);
- cl_git_pass(git_repository_index(&index, g_repo));
- cl_git_pass(git_index_read(index, 1));
- git_index_free(index);
-
opts.checkout_strategy =
GIT_CHECKOUT_FORCE | GIT_CHECKOUT_REMOVE_UNTRACKED;
memset(&cts, 0, sizeof(cts));