Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit c9d59c6

Browse files
authored
Merge pull request libgit2#4545 from libgit2/ethomson/checkout_filemode
Respect core.filemode in checkout
2 parents b4dde78 + c214ba1 commit c9d59c6

File tree

8 files changed

+112
-17
lines changed

8 files changed

+112
-17
lines changed

src/checkout.c

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ typedef struct {
7070
git_buf tmp;
7171
unsigned int strategy;
7272
int can_symlink;
73+
int respect_filemode;
7374
bool reload_submodules;
7475
size_t total_steps;
7576
size_t completed_steps;
@@ -159,17 +160,20 @@ GIT_INLINE(bool) is_workdir_base_or_new(
159160
git_oid__cmp(&newitem->id, workdir_id) == 0);
160161
}
161162

162-
GIT_INLINE(bool) is_file_mode_changed(git_filemode_t a, git_filemode_t b)
163+
GIT_INLINE(bool) is_filemode_changed(git_filemode_t a, git_filemode_t b, int respect_filemode)
163164
{
164-
#ifdef GIT_WIN32
165-
/*
166-
* On Win32 we do not support the executable bit; the file will
167-
* always be 0100644 on disk, don't bother doing a test.
168-
*/
169-
return false;
170-
#else
171-
return (S_ISREG(a) && S_ISREG(b) && a != b);
172-
#endif
165+
/* If core.filemode = false, ignore links in the repository and executable bit changes */
166+
if (!respect_filemode) {
167+
if (a == S_IFLNK)
168+
a = GIT_FILEMODE_BLOB;
169+
if (b == S_IFLNK)
170+
b = GIT_FILEMODE_BLOB;
171+
172+
a &= ~0111;
173+
b &= ~0111;
174+
}
175+
176+
return (a != b);
173177
}
174178

175179
static bool checkout_is_workdir_modified(
@@ -217,11 +221,11 @@ static bool checkout_is_workdir_modified(
217221
if (ie != NULL &&
218222
git_index_time_eq(&wditem->mtime, &ie->mtime) &&
219223
wditem->file_size == ie->file_size &&
220-
!is_file_mode_changed(wditem->mode, ie->mode)) {
224+
!is_filemode_changed(wditem->mode, ie->mode, data->respect_filemode)) {
221225

222226
/* The workdir is modified iff the index entry is modified */
223227
return !is_workdir_base_or_new(&ie->id, baseitem, newitem) ||
224-
is_file_mode_changed(baseitem->mode, ie->mode);
228+
is_filemode_changed(baseitem->mode, ie->mode, data->respect_filemode);
225229
}
226230

227231
/* depending on where base is coming from, we may or may not know
@@ -234,7 +238,7 @@ static bool checkout_is_workdir_modified(
234238
if (S_ISDIR(wditem->mode))
235239
return false;
236240

237-
if (is_file_mode_changed(baseitem->mode, wditem->mode))
241+
if (is_filemode_changed(baseitem->mode, wditem->mode, data->respect_filemode))
238242
return true;
239243

240244
if (git_diff__oid_for_entry(&oid, data->diff, wditem, wditem->mode, NULL) < 0)
@@ -2454,6 +2458,10 @@ static int checkout_data_init(
24542458
&data->can_symlink, repo, GIT_CVAR_SYMLINKS)) < 0)
24552459
goto cleanup;
24562460

2461+
if ((error = git_repository__cvar(
2462+
&data->respect_filemode, repo, GIT_CVAR_FILEMODE)) < 0)
2463+
goto cleanup;
2464+
24572465
if (!data->opts.baseline && !data->opts.baseline_index) {
24582466
data->opts_free_baseline = true;
24592467
error = 0;

tests/checkout/head.c

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,3 +181,86 @@ void test_checkout_head__typechange_index_and_workdir(void)
181181
git_object_free(target);
182182
git_index_free(index);
183183
}
184+
185+
void test_checkout_head__workdir_filemode_is_simplified(void)
186+
{
187+
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
188+
git_object *target, *branch;
189+
190+
opts.checkout_strategy = GIT_CHECKOUT_FORCE;
191+
192+
cl_git_pass(git_revparse_single(&target, g_repo, "a38d028f71eaa590febb7d716b1ca32350cf70da"));
193+
cl_git_pass(git_reset(g_repo, target, GIT_RESET_HARD, NULL));
194+
195+
cl_must_pass(p_chmod("testrepo/branch_file.txt", 0666));
196+
197+
/*
198+
* Checkout should not fail with a conflict; though the file mode
199+
* on disk is literally different to the base (0666 vs 0644), Git
200+
* ignores the actual mode and simply treats both as non-executable.
201+
*/
202+
cl_git_pass(git_revparse_single(&branch, g_repo, "099fabac3a9ea935598528c27f866e34089c2eff"));
203+
204+
opts.checkout_strategy &= ~GIT_CHECKOUT_FORCE;
205+
opts.checkout_strategy |= GIT_CHECKOUT_SAFE;
206+
cl_git_pass(git_checkout_tree(g_repo, branch, NULL));
207+
208+
git_object_free(branch);
209+
git_object_free(target);
210+
}
211+
212+
void test_checkout_head__obeys_filemode_true(void)
213+
{
214+
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
215+
git_object *target, *branch;
216+
217+
opts.checkout_strategy = GIT_CHECKOUT_FORCE;
218+
219+
/* In this commit, `README` is executable */
220+
cl_git_pass(git_revparse_single(&target, g_repo, "f9ed4af42472941da45a3c"));
221+
cl_git_pass(git_reset(g_repo, target, GIT_RESET_HARD, NULL));
222+
223+
cl_repo_set_bool(g_repo, "core.filemode", true);
224+
cl_must_pass(p_chmod("testrepo/README", 0644));
225+
226+
/*
227+
* Checkout will fail with a conflict; the file mode is updated in
228+
* the checkout target, but the contents have changed in our branch.
229+
*/
230+
cl_git_pass(git_revparse_single(&branch, g_repo, "099fabac3a9ea935598528c27f866e34089c2eff"));
231+
232+
opts.checkout_strategy &= ~GIT_CHECKOUT_FORCE;
233+
opts.checkout_strategy |= GIT_CHECKOUT_SAFE;
234+
cl_git_fail_with(GIT_ECONFLICT, git_checkout_tree(g_repo, branch, NULL));
235+
236+
git_object_free(branch);
237+
git_object_free(target);
238+
}
239+
240+
void test_checkout_head__obeys_filemode_false(void)
241+
{
242+
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
243+
git_object *target, *branch;
244+
245+
opts.checkout_strategy = GIT_CHECKOUT_FORCE;
246+
247+
/* In this commit, `README` is executable */
248+
cl_git_pass(git_revparse_single(&target, g_repo, "f9ed4af42472941da45a3c"));
249+
cl_git_pass(git_reset(g_repo, target, GIT_RESET_HARD, NULL));
250+
251+
cl_repo_set_bool(g_repo, "core.filemode", false);
252+
cl_must_pass(p_chmod("testrepo/README", 0644));
253+
254+
/*
255+
* Checkout will fail with a conflict; the file contents are updated
256+
* in the checkout target, but the filemode has changed in our branch.
257+
*/
258+
cl_git_pass(git_revparse_single(&branch, g_repo, "099fabac3a9ea935598528c27f866e34089c2eff"));
259+
260+
opts.checkout_strategy &= ~GIT_CHECKOUT_FORCE;
261+
opts.checkout_strategy |= GIT_CHECKOUT_SAFE;
262+
cl_git_pass(git_checkout_tree(g_repo, branch, NULL));
263+
264+
git_object_free(branch);
265+
git_object_free(target);
266+
}

tests/iterator/workdir.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -610,6 +610,7 @@ void test_iterator_workdir__filesystem2(void)
610610
static const char *expect_base[] = {
611611
"heads/br2",
612612
"heads/dir",
613+
"heads/executable",
613614
"heads/ident",
614615
"heads/long-file-name",
615616
"heads/master",
@@ -630,7 +631,7 @@ void test_iterator_workdir__filesystem2(void)
630631

631632
cl_git_pass(git_iterator_for_filesystem(
632633
&i, "testrepo/.git/refs", NULL));
633-
expect_iterator_items(i, 15, expect_base, 15, expect_base);
634+
expect_iterator_items(i, 16, expect_base, 16, expect_base);
634635
git_iterator_free(i);
635636
}
636637

tests/refs/list.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ void test_refs_list__all(void)
3636
/* We have exactly 12 refs in total if we include the packed ones:
3737
* there is a reference that exists both in the packfile and as
3838
* loose, but we only list it once */
39-
cl_assert_equal_i((int)ref_list.count, 17);
39+
cl_assert_equal_i((int)ref_list.count, 18);
4040

4141
git_strarray_free(&ref_list);
4242
}
@@ -51,7 +51,7 @@ void test_refs_list__do_not_retrieve_references_which_name_end_with_a_lock_exten
5151
"144344043ba4d4a405da03de3844aa829ae8be0e\n");
5252

5353
cl_git_pass(git_reference_list(&ref_list, g_repo));
54-
cl_assert_equal_i((int)ref_list.count, 17);
54+
cl_assert_equal_i((int)ref_list.count, 18);
5555

5656
git_strarray_free(&ref_list);
5757
}
Binary file not shown.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
x�N[
2+
B!��U��z�ꁈ~�A8W�� 3�K-?�v�|�f&�R.]�6�1K�-p��%� ��d���&���S�6�;5�u�3��� �9 ΄h|��`U�h8gAk_j������y��Qor����#����ZR;�*�1*�j@ w���g��ǵ�|e��O�
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
f9ed4af42472941da45a3ce44458455ed227a6be

tests/revwalk/basic.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ void test_revwalk_basic__glob_heads_with_invalid(void)
177177
/* walking */;
178178

179179
/* git log --branches --oneline | wc -l => 16 */
180-
cl_assert_equal_i(19, i);
180+
cl_assert_equal_i(20, i);
181181
}
182182

183183
void test_revwalk_basic__push_head(void)

0 commit comments

Comments
 (0)