From 18d9c8479f8534cc19765345560d2d39b8920a4d Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Tue, 20 Feb 2018 00:32:38 +0000 Subject: testrepo: add new branch Add a new branch to the `testrepo` repository, where the `README` file has changed to executable. This branch enables typechange tests between the new `executable` branch and `master`. --- tests/iterator/workdir.c | 3 ++- tests/refs/list.c | 4 ++-- .../objects/57/43a3ef145d3638a0fa28233ca92897117ad74f | Bin 0 -> 160 bytes .../objects/f9/ed4af42472941da45a3ce44458455ed227a6be | 2 ++ tests/resources/testrepo/.gitted/refs/heads/executable | 1 + tests/revwalk/basic.c | 2 +- 6 files changed, 8 insertions(+), 4 deletions(-) create mode 100644 tests/resources/testrepo/.gitted/objects/57/43a3ef145d3638a0fa28233ca92897117ad74f create mode 100644 tests/resources/testrepo/.gitted/objects/f9/ed4af42472941da45a3ce44458455ed227a6be create mode 100644 tests/resources/testrepo/.gitted/refs/heads/executable 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 new file mode 100644 index 000000000..85abb27cc Binary files /dev/null and b/tests/resources/testrepo/.gitted/objects/57/43a3ef145d3638a0fa28233ca92897117ad74f differ 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) -- cgit v1.2.1 From ec96db577922712f88e0b67cd2149de1f345bbbb Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Tue, 20 Feb 2018 00:32:38 +0000 Subject: checkout test: add core.filemode checkout tests Add two tests for filemode. The first ensures that `core.filemode=true` is honored: if we have changed the filemode such that a file that _was_ executable (mode 0755) is now executable (mode 0644) and we go to check out a branch that has otherwise changed the contents of the file, then we should raise a checkout conflict for that file. The second ensures that `core.filemode=false` is honored: in the same situation, we set a file that was executable to be non-executable, and check out the branch that changes the contents of the file. However, since `core.filemode` is false, we do not detect the filemode change. We run these tests on both operating systems that obey `core.filemode` (eg, POSIX) and those that have no conception of filemode (eg, Win32). This ensures that `core.filemode` is always honored, as it is a cache of the underlying filesystem's settings. This ensures that we do not make assumptions based on the operating system, and honor the configuration setting even if it were misconfigured. --- tests/checkout/head.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/tests/checkout/head.c b/tests/checkout/head.c index 46b22570d..866eb9a0b 100644 --- a/tests/checkout/head.c +++ b/tests/checkout/head.c @@ -181,3 +181,59 @@ void test_checkout_head__typechange_index_and_workdir(void) git_object_free(target); git_index_free(index); } + +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); +} -- cgit v1.2.1 From 275693e29cc90024bb21716a4943b8e64cd5d16d Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Tue, 20 Feb 2018 12:45:40 +0000 Subject: checkout test: ensure workdir mode is simplified Ensure that when examining the working directory for checkout that the mode is correctly simplified. Git only pays attention to whether a file is executable or not. When examining a working directory, we should coalesce modes in the working directory to either `0755` (indicating that a file is executable) or `0644` (indicating that it is not). Test this by giving the file an exotic mode, and ensuring that when checkout out a branch that changes the file's contents, that we do not have a checkout conflict. --- tests/checkout/head.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/tests/checkout/head.c b/tests/checkout/head.c index 866eb9a0b..99061466f 100644 --- a/tests/checkout/head.c +++ b/tests/checkout/head.c @@ -182,6 +182,33 @@ void test_checkout_head__typechange_index_and_workdir(void) 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; -- cgit v1.2.1 From c214ba19848a536bcff13d798812a8023d6c0771 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Tue, 20 Feb 2018 00:35:27 +0000 Subject: checkout: respect core.filemode when comparing filemodes Fixes #4504 --- src/checkout.c | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 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; -- cgit v1.2.1