Skip to content

Commit

Permalink
Fix git_checkout_tree() to do index filemodes correctly on Windows.
Browse files Browse the repository at this point in the history
git_checkout_tree() has some fallback behaviors for file systems
which don't have full support of filemodes.  Generally works fine,
but if a given file had a change of type from a 0644 to 0755 (i.e.,
you add executable permissions), the fallback behavior incorrectly
triggers when writing hte updated index.

This would cause a git_checkout_tree() command, even with the
GIT_CHECKOUT_FORCE option set, to leave a dirty index on Windows.

Also added checks to an existing test to catch this case.

Conflicts:
	src/checkout.c
  • Loading branch information
jfultz authored and carlosmn committed May 18, 2015
1 parent 7dc1b1c commit 04162eb
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 2 deletions.
10 changes: 10 additions & 0 deletions src/checkout.c
Expand Up @@ -1749,6 +1749,9 @@ static int checkout_create_the_new(
int error = 0;
git_diff_delta *delta;
size_t i;
int caps = git_index_caps(data->index);

git_index_set_caps(data->index, caps & ~GIT_INDEXCAP_NO_FILEMODE);

git_vector_foreach(&data->diff->deltas, i, delta) {
if (actions[i] & CHECKOUT_ACTION__DEFER_REMOVE) {
Expand All @@ -1770,6 +1773,8 @@ static int checkout_create_the_new(
}
}

git_index_set_caps(data->index, caps);

return 0;
}

Expand Down Expand Up @@ -2471,7 +2476,12 @@ int git_checkout_iterator(
cleanup:
if (!error && data.index != NULL &&
(data.strategy & GIT_CHECKOUT_DONT_UPDATE_INDEX) == 0)
{
int caps = git_index_caps(data.index);
git_index_set_caps(data.index, caps & ~GIT_INDEXCAP_NO_FILEMODE);
error = git_index_write(data.index);
git_index_set_caps(data.index, caps);
}

git_diff_free(data.diff);
git_iterator_free(workdir);
Expand Down
29 changes: 27 additions & 2 deletions tests/checkout/tree.c
Expand Up @@ -923,18 +923,43 @@ void test_checkout_tree__filemode_preserved_in_index(void)
git_index *index;
const git_index_entry *entry;

opts.checkout_strategy = GIT_CHECKOUT_FORCE;

cl_git_pass(git_repository_index(&index, g_repo));

/* test a freshly added executable */
cl_git_pass(git_oid_fromstr(&executable_oid, "afe4393b2b2a965f06acf2ca9658eaa01e0cd6b6"));
cl_git_pass(git_commit_lookup(&commit, g_repo, &executable_oid));

opts.checkout_strategy = GIT_CHECKOUT_FORCE;

cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts));
cl_assert(entry = git_index_get_bypath(index, "executable.txt", 0));
cl_assert_equal_i(0100755, entry->mode);

git_commit_free(commit);


/* Now start with a commit which has a text file */
cl_git_pass(git_oid_fromstr(&executable_oid, "cf80f8de9f1185bf3a05f993f6121880dd0cfbc9"));
cl_git_pass(git_commit_lookup(&commit, g_repo, &executable_oid));

cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts));
cl_assert(entry = git_index_get_bypath(index, "a/b.txt", 0));
cl_assert_equal_i(0100644, entry->mode);

git_commit_free(commit);


/* And then check out to a commit which converts the text file to an executable */
cl_git_pass(git_oid_fromstr(&executable_oid, "144344043ba4d4a405da03de3844aa829ae8be0e"));
cl_git_pass(git_commit_lookup(&commit, g_repo, &executable_oid));

cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts));
cl_assert(entry = git_index_get_bypath(index, "a/b.txt", 0));
cl_assert_equal_i(0100755, entry->mode);

git_commit_free(commit);


git_index_free(index);
}

Expand Down

0 comments on commit 04162eb

Please sign in to comment.