diff options
-rw-r--r-- | src/checkout.c | 34 | ||||
-rw-r--r-- | tests/checkout/head.c | 83 | ||||
-rw-r--r-- | tests/iterator/workdir.c | 3 | ||||
-rw-r--r-- | tests/refs/list.c | 4 | ||||
-rw-r--r-- | tests/resources/testrepo/.gitted/objects/57/43a3ef145d3638a0fa28233ca92897117ad74f | bin | 0 -> 160 bytes | |||
-rw-r--r-- | tests/resources/testrepo/.gitted/objects/f9/ed4af42472941da45a3ce44458455ed227a6be | 2 | ||||
-rw-r--r-- | tests/resources/testrepo/.gitted/refs/heads/executable | 1 | ||||
-rw-r--r-- | tests/revwalk/basic.c | 2 |
8 files changed, 112 insertions, 17 deletions
diff --git a/src/checkout.c b/src/checkout.c index 2ad736afe..8ff5d897b 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -70,6 +70,7 @@ typedef struct { git_buf tmp; unsigned int strategy; int can_symlink; + int respect_filemode; bool reload_submodules; size_t total_steps; size_t completed_steps; @@ -159,17 +160,20 @@ GIT_INLINE(bool) is_workdir_base_or_new( git_oid__cmp(&newitem->id, workdir_id) == 0); } -GIT_INLINE(bool) is_file_mode_changed(git_filemode_t a, git_filemode_t b) +GIT_INLINE(bool) is_filemode_changed(git_filemode_t a, git_filemode_t b, int respect_filemode) { -#ifdef GIT_WIN32 - /* - * On Win32 we do not support the executable bit; the file will - * always be 0100644 on disk, don't bother doing a test. - */ - return false; -#else - return (S_ISREG(a) && S_ISREG(b) && a != b); -#endif + /* If core.filemode = false, ignore links in the repository and executable bit changes */ + if (!respect_filemode) { + if (a == S_IFLNK) + a = GIT_FILEMODE_BLOB; + if (b == S_IFLNK) + b = GIT_FILEMODE_BLOB; + + a &= ~0111; + b &= ~0111; + } + + return (a != b); } static bool checkout_is_workdir_modified( @@ -217,11 +221,11 @@ static bool checkout_is_workdir_modified( if (ie != NULL && git_index_time_eq(&wditem->mtime, &ie->mtime) && wditem->file_size == ie->file_size && - !is_file_mode_changed(wditem->mode, ie->mode)) { + !is_filemode_changed(wditem->mode, ie->mode, data->respect_filemode)) { /* The workdir is modified iff the index entry is modified */ return !is_workdir_base_or_new(&ie->id, baseitem, newitem) || - is_file_mode_changed(baseitem->mode, ie->mode); + is_filemode_changed(baseitem->mode, ie->mode, data->respect_filemode); } /* depending on where base is coming from, we may or may not know @@ -234,7 +238,7 @@ static bool checkout_is_workdir_modified( if (S_ISDIR(wditem->mode)) return false; - if (is_file_mode_changed(baseitem->mode, wditem->mode)) + if (is_filemode_changed(baseitem->mode, wditem->mode, data->respect_filemode)) return true; if (git_diff__oid_for_entry(&oid, data->diff, wditem, wditem->mode, NULL) < 0) @@ -2454,6 +2458,10 @@ static int checkout_data_init( &data->can_symlink, repo, GIT_CVAR_SYMLINKS)) < 0) goto cleanup; + if ((error = git_repository__cvar( + &data->respect_filemode, repo, GIT_CVAR_FILEMODE)) < 0) + goto cleanup; + if (!data->opts.baseline && !data->opts.baseline_index) { data->opts_free_baseline = true; error = 0; diff --git a/tests/checkout/head.c b/tests/checkout/head.c index 46b22570d..99061466f 100644 --- a/tests/checkout/head.c +++ b/tests/checkout/head.c @@ -181,3 +181,86 @@ void test_checkout_head__typechange_index_and_workdir(void) git_object_free(target); git_index_free(index); } + +void test_checkout_head__workdir_filemode_is_simplified(void) +{ + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; + git_object *target, *branch; + + opts.checkout_strategy = GIT_CHECKOUT_FORCE; + + cl_git_pass(git_revparse_single(&target, g_repo, "a38d028f71eaa590febb7d716b1ca32350cf70da")); + cl_git_pass(git_reset(g_repo, target, GIT_RESET_HARD, NULL)); + + cl_must_pass(p_chmod("testrepo/branch_file.txt", 0666)); + + /* + * Checkout should not fail with a conflict; though the file mode + * on disk is literally different to the base (0666 vs 0644), Git + * ignores the actual mode and simply treats both as non-executable. + */ + cl_git_pass(git_revparse_single(&branch, g_repo, "099fabac3a9ea935598528c27f866e34089c2eff")); + + opts.checkout_strategy &= ~GIT_CHECKOUT_FORCE; + opts.checkout_strategy |= GIT_CHECKOUT_SAFE; + cl_git_pass(git_checkout_tree(g_repo, branch, NULL)); + + git_object_free(branch); + git_object_free(target); +} + +void test_checkout_head__obeys_filemode_true(void) +{ + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; + git_object *target, *branch; + + opts.checkout_strategy = GIT_CHECKOUT_FORCE; + + /* In this commit, `README` is executable */ + cl_git_pass(git_revparse_single(&target, g_repo, "f9ed4af42472941da45a3c")); + cl_git_pass(git_reset(g_repo, target, GIT_RESET_HARD, NULL)); + + cl_repo_set_bool(g_repo, "core.filemode", true); + cl_must_pass(p_chmod("testrepo/README", 0644)); + + /* + * Checkout will fail with a conflict; the file mode is updated in + * the checkout target, but the contents have changed in our branch. + */ + cl_git_pass(git_revparse_single(&branch, g_repo, "099fabac3a9ea935598528c27f866e34089c2eff")); + + opts.checkout_strategy &= ~GIT_CHECKOUT_FORCE; + opts.checkout_strategy |= GIT_CHECKOUT_SAFE; + cl_git_fail_with(GIT_ECONFLICT, git_checkout_tree(g_repo, branch, NULL)); + + git_object_free(branch); + git_object_free(target); +} + +void test_checkout_head__obeys_filemode_false(void) +{ + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; + git_object *target, *branch; + + opts.checkout_strategy = GIT_CHECKOUT_FORCE; + + /* In this commit, `README` is executable */ + cl_git_pass(git_revparse_single(&target, g_repo, "f9ed4af42472941da45a3c")); + cl_git_pass(git_reset(g_repo, target, GIT_RESET_HARD, NULL)); + + cl_repo_set_bool(g_repo, "core.filemode", false); + cl_must_pass(p_chmod("testrepo/README", 0644)); + + /* + * Checkout will fail with a conflict; the file contents are updated + * in the checkout target, but the filemode has changed in our branch. + */ + cl_git_pass(git_revparse_single(&branch, g_repo, "099fabac3a9ea935598528c27f866e34089c2eff")); + + opts.checkout_strategy &= ~GIT_CHECKOUT_FORCE; + opts.checkout_strategy |= GIT_CHECKOUT_SAFE; + cl_git_pass(git_checkout_tree(g_repo, branch, NULL)); + + git_object_free(branch); + git_object_free(target); +} diff --git a/tests/iterator/workdir.c b/tests/iterator/workdir.c index 198edc7e8..81016752c 100644 --- a/tests/iterator/workdir.c +++ b/tests/iterator/workdir.c @@ -610,6 +610,7 @@ void test_iterator_workdir__filesystem2(void) static const char *expect_base[] = { "heads/br2", "heads/dir", + "heads/executable", "heads/ident", "heads/long-file-name", "heads/master", @@ -630,7 +631,7 @@ void test_iterator_workdir__filesystem2(void) cl_git_pass(git_iterator_for_filesystem( &i, "testrepo/.git/refs", NULL)); - expect_iterator_items(i, 15, expect_base, 15, expect_base); + expect_iterator_items(i, 16, expect_base, 16, expect_base); git_iterator_free(i); } diff --git a/tests/refs/list.c b/tests/refs/list.c index f7ca3f707..97461fd69 100644 --- a/tests/refs/list.c +++ b/tests/refs/list.c @@ -36,7 +36,7 @@ void test_refs_list__all(void) /* We have exactly 12 refs in total if we include the packed ones: * there is a reference that exists both in the packfile and as * loose, but we only list it once */ - cl_assert_equal_i((int)ref_list.count, 17); + cl_assert_equal_i((int)ref_list.count, 18); git_strarray_free(&ref_list); } @@ -51,7 +51,7 @@ void test_refs_list__do_not_retrieve_references_which_name_end_with_a_lock_exten "144344043ba4d4a405da03de3844aa829ae8be0e\n"); cl_git_pass(git_reference_list(&ref_list, g_repo)); - cl_assert_equal_i((int)ref_list.count, 17); + cl_assert_equal_i((int)ref_list.count, 18); git_strarray_free(&ref_list); } diff --git a/tests/resources/testrepo/.gitted/objects/57/43a3ef145d3638a0fa28233ca92897117ad74f b/tests/resources/testrepo/.gitted/objects/57/43a3ef145d3638a0fa28233ca92897117ad74f Binary files differnew file mode 100644 index 000000000..85abb27cc --- /dev/null +++ b/tests/resources/testrepo/.gitted/objects/57/43a3ef145d3638a0fa28233ca92897117ad74f diff --git a/tests/resources/testrepo/.gitted/objects/f9/ed4af42472941da45a3ce44458455ed227a6be b/tests/resources/testrepo/.gitted/objects/f9/ed4af42472941da45a3ce44458455ed227a6be new file mode 100644 index 000000000..69c52fc9f --- /dev/null +++ b/tests/resources/testrepo/.gitted/objects/f9/ed4af42472941da45a3ce44458455ed227a6be @@ -0,0 +1,2 @@ +xN[ +B!Uzꁈ~A8W 3K-?v|f&R.]61K-p% d&S6;5u3 9΄h|`Uh8gAk_jyQor#ZR;*1*j@wgǵ|eO
\ No newline at end of file diff --git a/tests/resources/testrepo/.gitted/refs/heads/executable b/tests/resources/testrepo/.gitted/refs/heads/executable new file mode 100644 index 000000000..2bdccea29 --- /dev/null +++ b/tests/resources/testrepo/.gitted/refs/heads/executable @@ -0,0 +1 @@ +f9ed4af42472941da45a3ce44458455ed227a6be diff --git a/tests/revwalk/basic.c b/tests/revwalk/basic.c index 547050c68..6a23701f3 100644 --- a/tests/revwalk/basic.c +++ b/tests/revwalk/basic.c @@ -177,7 +177,7 @@ void test_revwalk_basic__glob_heads_with_invalid(void) /* walking */; /* git log --branches --oneline | wc -l => 16 */ - cl_assert_equal_i(19, i); + cl_assert_equal_i(20, i); } void test_revwalk_basic__push_head(void) |