Skip to content

Commit 38e769c

Browse files
authored
Merge pull request libgit2#4369 from libgit2/ethomson/checkout_typechange
Checkout typechange-only deltas
2 parents 21e6a11 + 19e8fab commit 38e769c

File tree

2 files changed

+63
-1
lines changed

2 files changed

+63
-1
lines changed

src/checkout.c

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,19 @@ GIT_INLINE(bool) is_workdir_base_or_new(
159159
git_oid__cmp(&newitem->id, workdir_id) == 0);
160160
}
161161

162+
GIT_INLINE(bool) is_file_mode_changed(git_filemode_t a, git_filemode_t b)
163+
{
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
173+
}
174+
162175
static bool checkout_is_workdir_modified(
163176
checkout_data *data,
164177
const git_diff_file *baseitem,
@@ -200,7 +213,8 @@ static bool checkout_is_workdir_modified(
200213
*/
201214
if ((ie = git_index_get_bypath(data->index, wditem->path, 0)) != NULL) {
202215
if (git_index_time_eq(&wditem->mtime, &ie->mtime) &&
203-
wditem->file_size == ie->file_size)
216+
wditem->file_size == ie->file_size &&
217+
!is_file_mode_changed(wditem->mode, ie->mode))
204218
return !is_workdir_base_or_new(&ie->id, baseitem, newitem);
205219
}
206220

@@ -214,6 +228,9 @@ static bool checkout_is_workdir_modified(
214228
if (S_ISDIR(wditem->mode))
215229
return false;
216230

231+
if (is_file_mode_changed(baseitem->mode, wditem->mode))
232+
return true;
233+
217234
if (git_diff__oid_for_entry(&oid, data->diff, wditem, wditem->mode, NULL) < 0)
218235
return false;
219236

tests/checkout/tree.c

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1514,6 +1514,51 @@ void test_checkout_tree__baseline_is_empty_when_no_index(void)
15141514
git_reference_free(head);
15151515
}
15161516

1517+
void test_checkout_tree__mode_change_is_force_updated(void)
1518+
{
1519+
git_index *index;
1520+
git_reference *head;
1521+
git_object *obj;
1522+
git_status_list *status;
1523+
1524+
if (!cl_is_chmod_supported())
1525+
clar__skip();
1526+
1527+
assert_on_branch(g_repo, "master");
1528+
cl_git_pass(git_repository_index(&index, g_repo));
1529+
cl_git_pass(git_repository_head(&head, g_repo));
1530+
cl_git_pass(git_reference_peel(&obj, head, GIT_OBJ_COMMIT));
1531+
1532+
cl_git_pass(git_reset(g_repo, obj, GIT_RESET_HARD, NULL));
1533+
1534+
cl_git_pass(git_status_list_new(&status, g_repo, NULL));
1535+
cl_assert_equal_i(0, git_status_list_entrycount(status));
1536+
git_status_list_free(status);
1537+
1538+
/* update the mode on-disk */
1539+
cl_must_pass(p_chmod("testrepo/README", 0755));
1540+
1541+
cl_git_pass(git_checkout_tree(g_repo, obj, &g_opts));
1542+
1543+
cl_git_pass(git_status_list_new(&status, g_repo, NULL));
1544+
cl_assert_equal_i(0, git_status_list_entrycount(status));
1545+
git_status_list_free(status);
1546+
1547+
/* update the mode on-disk and in the index */
1548+
cl_must_pass(p_chmod("testrepo/README", 0755));
1549+
cl_must_pass(git_index_add_bypath(index, "README"));
1550+
1551+
cl_git_pass(git_checkout_tree(g_repo, obj, &g_opts));
1552+
1553+
cl_git_pass(git_status_list_new(&status, g_repo, NULL));
1554+
cl_assert_equal_i(0, git_status_list_entrycount(status));
1555+
git_status_list_free(status);
1556+
1557+
git_object_free(obj);
1558+
git_reference_free(head);
1559+
git_index_free(index);
1560+
}
1561+
15171562
void test_checkout_tree__nullopts(void)
15181563
{
15191564
cl_git_pass(git_checkout_tree(g_repo, NULL, NULL));

0 commit comments

Comments
 (0)