From cf7206f8d32a46b348c2b48bea47583c9bd9929d Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Wed, 15 Jun 2016 17:37:37 -0500 Subject: checkout: dirty index should conflict on typechange When the target of a checkout has a typechange of an item, a modified index entry should cause a conflict even when the workdir has been reverted to the baseline. --- src/checkout.c | 14 ++++++++++++-- tests/checkout/tree.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/src/checkout.c b/src/checkout.c index 0663bb612..a6d7df0a6 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -236,6 +236,7 @@ static bool is_index_modified( const git_index_entry *wditem) { const git_index_entry *ie; + const char *lookup_path; /* Don't bother investigate if we're checking out the current * index, it is canonical. @@ -243,7 +244,16 @@ static bool is_index_modified( if (data->index == git_iterator_index(data->target)) return false; - if ((ie = git_index_get_bypath(data->index, wditem->path, 0)) == NULL) + if (!S_ISDIR(baseitem->mode)) + lookup_path = baseitem->path; + else if (!S_ISDIR(newitem->mode)) + lookup_path = newitem->path; + else if (!S_ISDIR(wditem->mode)) + lookup_path = wditem->path; + else + return true; + + if ((ie = git_index_get_bypath(data->index, lookup_path, 0)) == NULL) return true; /* consider the index entry modified if it's different than both @@ -552,7 +562,7 @@ static int checkout_action_with_wd( } else *action = CHECKOUT_ACTION_IF(FORCE, REMOVE, CONFLICT); } - else if (is_workdir_modified(data, &delta->old_file, &delta->new_file, wd)) + else if (is_workdir_or_index_modified(data, &delta->old_file, &delta->new_file, wd)) *action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT); else *action = CHECKOUT_ACTION_IF(SAFE, REMOVE_AND_UPDATE, NONE); diff --git a/tests/checkout/tree.c b/tests/checkout/tree.c index e26b1eb96..6485c6f0c 100644 --- a/tests/checkout/tree.c +++ b/tests/checkout/tree.c @@ -1687,3 +1687,48 @@ void test_checkout_tree__modified_in_index_reverted_in_workdir_modified_in_targe git_index_free(index); } +void test_checkout_tree__modified_in_index_reverted_in_workdir_typechange_in_target_is_conflict(void) +{ + git_index *index; + git_reference *head; + git_oid target_id; + git_object *obj, *target; + git_status_list *status; + git_index_entry modified_entry = {{0}}; + + 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)); + + cl_git_pass(git_reset(g_repo, obj, GIT_RESET_HARD, NULL)); + + /* refs/heads/br2 */ + cl_git_pass(git_oid_fromstr(&target_id, "6759f8c30605b2928a4e07a5a8a850d5676286cb")); + cl_git_pass(git_object_lookup(&target, g_repo, &target_id, GIT_OBJ_COMMIT)); + + /* change the file's id in the index */ + modified_entry.mode = GIT_FILEMODE_BLOB; + modified_entry.path = "README"; + cl_git_pass(git_oid_fromstr(&modified_entry.id, "a71586c1dfe8a71c6cbf6c129f404c5642ff31bd")); + + cl_git_pass(git_index_add(index, &modified_entry)); + cl_git_pass(git_index_write(index)); + + g_opts.checkout_strategy &= ~GIT_CHECKOUT_FORCE; + cl_git_fail_with(GIT_ECONFLICT, git_checkout_tree(g_repo, target, &g_opts)); + + g_opts.checkout_strategy |= GIT_CHECKOUT_FORCE; + cl_git_pass(git_checkout_tree(g_repo, target, &g_opts)); + cl_git_pass(git_repository_set_head_detached(g_repo, &target_id)); + + cl_git_pass(git_status_list_new(&status, g_repo, NULL)); + cl_assert_equal_i(0, git_status_list_entrycount(status)); + git_status_list_free(status); + + git_object_free(target); + git_object_free(obj); + git_reference_free(head); + git_index_free(index); +} + -- cgit v1.2.1