Unverified Commit c9d59c61 by Edward Thomson Committed by GitHub

Merge pull request #4545 from libgit2/ethomson/checkout_filemode

Respect core.filemode in checkout
parents b4dde78a c214ba19
...@@ -70,6 +70,7 @@ typedef struct { ...@@ -70,6 +70,7 @@ typedef struct {
git_buf tmp; git_buf tmp;
unsigned int strategy; unsigned int strategy;
int can_symlink; int can_symlink;
int respect_filemode;
bool reload_submodules; bool reload_submodules;
size_t total_steps; size_t total_steps;
size_t completed_steps; size_t completed_steps;
...@@ -159,17 +160,20 @@ GIT_INLINE(bool) is_workdir_base_or_new( ...@@ -159,17 +160,20 @@ GIT_INLINE(bool) is_workdir_base_or_new(
git_oid__cmp(&newitem->id, workdir_id) == 0); 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 /* If core.filemode = false, ignore links in the repository and executable bit changes */
/* if (!respect_filemode) {
* On Win32 we do not support the executable bit; the file will if (a == S_IFLNK)
* always be 0100644 on disk, don't bother doing a test. a = GIT_FILEMODE_BLOB;
*/ if (b == S_IFLNK)
return false; b = GIT_FILEMODE_BLOB;
#else
return (S_ISREG(a) && S_ISREG(b) && a != b); a &= ~0111;
#endif b &= ~0111;
}
return (a != b);
} }
static bool checkout_is_workdir_modified( static bool checkout_is_workdir_modified(
...@@ -217,11 +221,11 @@ static bool checkout_is_workdir_modified( ...@@ -217,11 +221,11 @@ static bool checkout_is_workdir_modified(
if (ie != NULL && if (ie != NULL &&
git_index_time_eq(&wditem->mtime, &ie->mtime) && git_index_time_eq(&wditem->mtime, &ie->mtime) &&
wditem->file_size == ie->file_size && 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 */ /* The workdir is modified iff the index entry is modified */
return !is_workdir_base_or_new(&ie->id, baseitem, newitem) || 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 /* depending on where base is coming from, we may or may not know
...@@ -234,7 +238,7 @@ static bool checkout_is_workdir_modified( ...@@ -234,7 +238,7 @@ static bool checkout_is_workdir_modified(
if (S_ISDIR(wditem->mode)) if (S_ISDIR(wditem->mode))
return false; return false;
if (is_file_mode_changed(baseitem->mode, wditem->mode)) if (is_filemode_changed(baseitem->mode, wditem->mode, data->respect_filemode))
return true; return true;
if (git_diff__oid_for_entry(&oid, data->diff, wditem, wditem->mode, NULL) < 0) if (git_diff__oid_for_entry(&oid, data->diff, wditem, wditem->mode, NULL) < 0)
...@@ -2454,6 +2458,10 @@ static int checkout_data_init( ...@@ -2454,6 +2458,10 @@ static int checkout_data_init(
&data->can_symlink, repo, GIT_CVAR_SYMLINKS)) < 0) &data->can_symlink, repo, GIT_CVAR_SYMLINKS)) < 0)
goto cleanup; 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) { if (!data->opts.baseline && !data->opts.baseline_index) {
data->opts_free_baseline = true; data->opts_free_baseline = true;
error = 0; error = 0;
......
...@@ -181,3 +181,86 @@ void test_checkout_head__typechange_index_and_workdir(void) ...@@ -181,3 +181,86 @@ void test_checkout_head__typechange_index_and_workdir(void)
git_object_free(target); git_object_free(target);
git_index_free(index); 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);
}
...@@ -610,6 +610,7 @@ void test_iterator_workdir__filesystem2(void) ...@@ -610,6 +610,7 @@ void test_iterator_workdir__filesystem2(void)
static const char *expect_base[] = { static const char *expect_base[] = {
"heads/br2", "heads/br2",
"heads/dir", "heads/dir",
"heads/executable",
"heads/ident", "heads/ident",
"heads/long-file-name", "heads/long-file-name",
"heads/master", "heads/master",
...@@ -630,7 +631,7 @@ void test_iterator_workdir__filesystem2(void) ...@@ -630,7 +631,7 @@ void test_iterator_workdir__filesystem2(void)
cl_git_pass(git_iterator_for_filesystem( cl_git_pass(git_iterator_for_filesystem(
&i, "testrepo/.git/refs", NULL)); &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); git_iterator_free(i);
} }
......
...@@ -36,7 +36,7 @@ void test_refs_list__all(void) ...@@ -36,7 +36,7 @@ void test_refs_list__all(void)
/* We have exactly 12 refs in total if we include the packed ones: /* 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 * there is a reference that exists both in the packfile and as
* loose, but we only list it once */ * 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); git_strarray_free(&ref_list);
} }
...@@ -51,7 +51,7 @@ void test_refs_list__do_not_retrieve_references_which_name_end_with_a_lock_exten ...@@ -51,7 +51,7 @@ void test_refs_list__do_not_retrieve_references_which_name_end_with_a_lock_exten
"144344043ba4d4a405da03de3844aa829ae8be0e\n"); "144344043ba4d4a405da03de3844aa829ae8be0e\n");
cl_git_pass(git_reference_list(&ref_list, g_repo)); 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); git_strarray_free(&ref_list);
} }
f9ed4af42472941da45a3ce44458455ed227a6be
...@@ -177,7 +177,7 @@ void test_revwalk_basic__glob_heads_with_invalid(void) ...@@ -177,7 +177,7 @@ void test_revwalk_basic__glob_heads_with_invalid(void)
/* walking */; /* walking */;
/* git log --branches --oneline | wc -l => 16 */ /* 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) void test_revwalk_basic__push_head(void)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment