diff --git a/src/checkout.c b/src/checkout.c index 539939e9f..0b6e298a0 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -1794,9 +1794,6 @@ 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) { @@ -1818,8 +1815,6 @@ static int checkout_create_the_new( } } - git_index_set_caps(data->index, caps); - return 0; } @@ -2548,12 +2543,7 @@ int git_checkout_iterator( cleanup: if (!error && data.index != NULL && (data.strategy & CHECKOUT_INDEX_DONT_WRITE_MASK) == 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); diff --git a/src/index.c b/src/index.c index dbcc37a58..0d2a03e72 100644 --- a/src/index.c +++ b/src/index.c @@ -987,9 +987,10 @@ static int index_no_dups(void **old, void *new) * it, then it will return an error **and also free the entry**. When * it replaces an existing entry, it will update the entry_ptr with the * actual entry in the index (and free the passed in one). + * trust_mode is whether we trust the mode in entry_ptr. */ static int index_insert( - git_index *index, git_index_entry **entry_ptr, int replace) + git_index *index, git_index_entry **entry_ptr, int replace, bool trust_mode) { int error = 0; size_t path_length, position; @@ -1021,7 +1022,10 @@ static int index_insert( &position, index, entry->path, 0, GIT_IDXENTRY_STAGE(entry), false)) { existing = index->entries.contents[position]; /* update filemode to existing values if stat is not trusted */ - entry->mode = index_merge_mode(index, existing, entry->mode); + if (trust_mode) + entry->mode = git_index__create_mode(entry->mode); + else + entry->mode = index_merge_mode(index, existing, entry->mode); } /* look for tree / blob name collisions, removing conflicts if requested */ @@ -1122,7 +1126,7 @@ int git_index_add_frombuffer( git_oid_cpy(&entry->id, &id); entry->file_size = len; - if ((error = index_insert(index, &entry, 1)) < 0) + if ((error = index_insert(index, &entry, 1, true)) < 0) return error; /* Adding implies conflict was resolved, move conflict entries to REUC */ @@ -1142,7 +1146,7 @@ int git_index_add_bypath(git_index *index, const char *path) assert(index && path); if ((ret = index_entry_init(&entry, index, path)) < 0 || - (ret = index_insert(index, &entry, 1)) < 0) + (ret = index_insert(index, &entry, 1, false)) < 0) return ret; /* Adding implies conflict was resolved, move conflict entries to REUC */ @@ -1182,7 +1186,7 @@ int git_index_add(git_index *index, const git_index_entry *source_entry) } if ((ret = index_entry_dup(&entry, INDEX_OWNER(index), source_entry)) < 0 || - (ret = index_insert(index, &entry, 1)) < 0) + (ret = index_insert(index, &entry, 1, true)) < 0) return ret; git_tree_cache_invalidate_path(index->tree, entry->path); @@ -1313,7 +1317,7 @@ int git_index_conflict_add(git_index *index, /* Make sure stage is correct */ GIT_IDXENTRY_STAGE_SET(entries[i], i + 1); - if ((ret = index_insert(index, &entries[i], 1)) < 0) + if ((ret = index_insert(index, &entries[i], 1, true)) < 0) goto on_error; entries[i] = NULL; /* don't free if later entry fails */ @@ -2537,7 +2541,7 @@ int git_index_add_all( entry->id = blobid; /* add working directory item to index */ - if ((error = index_insert(index, &entry, 1)) < 0) + if ((error = index_insert(index, &entry, 1, false)) < 0) break; git_tree_cache_invalidate_path(index->tree, wd->path); diff --git a/tests/index/filemodes.c b/tests/index/filemodes.c index 58d7935a0..b3907996b 100644 --- a/tests/index/filemodes.c +++ b/tests/index/filemodes.c @@ -153,6 +153,85 @@ void test_index_filemodes__trusted(void) git_index_free(index); } +#define add_entry_and_check_mode(I,FF,X) add_entry_and_check_mode_(I,FF,X,__FILE__,__LINE__) + +static void add_entry_and_check_mode_( + git_index *index, bool from_file, git_filemode_t mode, + const char *file, int line) +{ + size_t pos; + const git_index_entry* entry; + git_index_entry new_entry; + + /* If old_filename exists, we copy that to the new file, and test + * git_index_add(), otherwise create a new entry testing git_index_add_frombuffer + */ + if (from_file) + { + clar__assert(!git_index_find(&pos, index, "exec_off"), + file, line, "Cannot find original index entry", NULL, 1); + + entry = git_index_get_byindex(index, pos); + + memcpy(&new_entry, entry, sizeof(new_entry)); + } + else + memset(&new_entry, 0x0, sizeof(git_index_entry)); + + new_entry.path = "filemodes/explicit_test"; + new_entry.mode = mode; + + if (from_file) + { + clar__assert(!git_index_add(index, &new_entry), + file, line, "Cannot add index entry", NULL, 1); + } + else + { + const char *content = "hey there\n"; + clar__assert(!git_index_add_frombuffer(index, &new_entry, content, strlen(content)), + file, line, "Cannot add index entry from buffer", NULL, 1); + } + + clar__assert(!git_index_find(&pos, index, "filemodes/explicit_test"), + file, line, "Cannot find new index entry", NULL, 1); + + entry = git_index_get_byindex(index, pos); + + clar__assert_equal(file, line, "Expected mode does not match index", + 1, "%07o", (unsigned int)entry->mode, (unsigned int)mode); +} + +void test_index_filemodes__explicit(void) +{ + git_index *index; + + /* These tests should run and work everywhere, as the filemode is + * given explicitly to git_index_add or git_index_add_frombuffer + */ + cl_repo_set_bool(g_repo, "core.filemode", false); + + cl_git_pass(git_repository_index(&index, g_repo)); + + /* Each of these tests keeps overwriting the same file in the index. */ + /* 1 - add new 0644 entry */ + add_entry_and_check_mode(index, true, GIT_FILEMODE_BLOB); + + /* 2 - add 0755 entry over existing 0644 */ + add_entry_and_check_mode(index, true, GIT_FILEMODE_BLOB_EXECUTABLE); + + /* 3 - add 0644 entry over existing 0755 */ + add_entry_and_check_mode(index, true, GIT_FILEMODE_BLOB); + + /* 4 - add 0755 buffer entry over existing 0644 */ + add_entry_and_check_mode(index, false, GIT_FILEMODE_BLOB_EXECUTABLE); + + /* 5 - add 0644 buffer entry over existing 0755 */ + add_entry_and_check_mode(index, false, GIT_FILEMODE_BLOB); + + git_index_free(index); +} + void test_index_filemodes__invalid(void) { git_index *index;