Commit 243db06c by Vicent Marti

Merge pull request #2484 from libgit2/fix-git-status-list-new-unreadable-folder

Fix git status list new unreadable folder
parents bf9a7e06 85b7268e
......@@ -152,6 +152,12 @@ typedef enum {
*/
GIT_DIFF_UPDATE_INDEX = (1u << 15),
/** Include unreadable files in the diff */
GIT_DIFF_INCLUDE_UNREADABLE = (1u << 16),
/** Include unreadable files in the diff */
GIT_DIFF_INCLUDE_UNREADABLE_AS_UNTRACKED = (1u << 17),
/*
* Options controlling how output will be generated
*/
......@@ -237,6 +243,7 @@ typedef enum {
GIT_DELTA_IGNORED = 6, /**< entry is ignored item in workdir */
GIT_DELTA_UNTRACKED = 7, /**< entry is untracked item in workdir */
GIT_DELTA_TYPECHANGE = 8, /**< type of entry changed between old and new */
GIT_DELTA_UNREADABLE = 9, /**< entry is unreadable */
} git_delta_t;
/**
......
......@@ -43,6 +43,7 @@ typedef enum {
GIT_STATUS_WT_DELETED = (1u << 9),
GIT_STATUS_WT_TYPECHANGE = (1u << 10),
GIT_STATUS_WT_RENAMED = (1u << 11),
GIT_STATUS_WT_UNREADABLE = (1u << 12),
GIT_STATUS_IGNORED = (1u << 14),
} git_status_t;
......@@ -133,20 +134,22 @@ typedef enum {
* together as `GIT_STATUS_OPT_DEFAULTS` if you want them as a baseline.
*/
typedef enum {
GIT_STATUS_OPT_INCLUDE_UNTRACKED = (1u << 0),
GIT_STATUS_OPT_INCLUDE_IGNORED = (1u << 1),
GIT_STATUS_OPT_INCLUDE_UNMODIFIED = (1u << 2),
GIT_STATUS_OPT_EXCLUDE_SUBMODULES = (1u << 3),
GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS = (1u << 4),
GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH = (1u << 5),
GIT_STATUS_OPT_RECURSE_IGNORED_DIRS = (1u << 6),
GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX = (1u << 7),
GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR = (1u << 8),
GIT_STATUS_OPT_SORT_CASE_SENSITIVELY = (1u << 9),
GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY = (1u << 10),
GIT_STATUS_OPT_RENAMES_FROM_REWRITES = (1u << 11),
GIT_STATUS_OPT_NO_REFRESH = (1u << 12),
GIT_STATUS_OPT_UPDATE_INDEX = (1u << 13),
GIT_STATUS_OPT_INCLUDE_UNTRACKED = (1u << 0),
GIT_STATUS_OPT_INCLUDE_IGNORED = (1u << 1),
GIT_STATUS_OPT_INCLUDE_UNMODIFIED = (1u << 2),
GIT_STATUS_OPT_EXCLUDE_SUBMODULES = (1u << 3),
GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS = (1u << 4),
GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH = (1u << 5),
GIT_STATUS_OPT_RECURSE_IGNORED_DIRS = (1u << 6),
GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX = (1u << 7),
GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR = (1u << 8),
GIT_STATUS_OPT_SORT_CASE_SENSITIVELY = (1u << 9),
GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY = (1u << 10),
GIT_STATUS_OPT_RENAMES_FROM_REWRITES = (1u << 11),
GIT_STATUS_OPT_NO_REFRESH = (1u << 12),
GIT_STATUS_OPT_UPDATE_INDEX = (1u << 13),
GIT_STATUS_OPT_INCLUDE_UNREADABLE = (1u << 14),
GIT_STATUS_OPT_INCLUDE_UNREADABLE_AS_UNTRACKED = (1u << 15),
} git_status_opt_t;
#define GIT_STATUS_OPT_DEFAULTS \
......
......@@ -198,12 +198,12 @@ typedef enum {
/** Valid modes for index and tree entries. */
typedef enum {
GIT_FILEMODE_NEW = 0000000,
GIT_FILEMODE_TREE = 0040000,
GIT_FILEMODE_BLOB = 0100644,
GIT_FILEMODE_BLOB_EXECUTABLE = 0100755,
GIT_FILEMODE_LINK = 0120000,
GIT_FILEMODE_COMMIT = 0160000,
GIT_FILEMODE_UNREADABLE = 0000000,
GIT_FILEMODE_TREE = 0040000,
GIT_FILEMODE_BLOB = 0100644,
GIT_FILEMODE_BLOB_EXECUTABLE = 0100755,
GIT_FILEMODE_LINK = 0120000,
GIT_FILEMODE_COMMIT = 0160000,
} git_filemode_t;
typedef struct git_refspec git_refspec;
......
......@@ -119,6 +119,7 @@ static int checkout_notify(
case GIT_DELTA_ADDED:
case GIT_DELTA_IGNORED:
case GIT_DELTA_UNTRACKED:
case GIT_DELTA_UNREADABLE:
target = &delta->new_file;
break;
case GIT_DELTA_DELETED:
......@@ -2143,6 +2144,7 @@ int git_checkout_iterator(
diff_opts.flags =
GIT_DIFF_INCLUDE_UNMODIFIED |
GIT_DIFF_INCLUDE_UNREADABLE |
GIT_DIFF_INCLUDE_UNTRACKED |
GIT_DIFF_RECURSE_UNTRACKED_DIRS | /* needed to match baseline */
GIT_DIFF_INCLUDE_IGNORED |
......
......@@ -92,6 +92,10 @@ static int diff_delta__from_one(
if (status == GIT_DELTA_UNTRACKED &&
DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_UNTRACKED))
return 0;
if (status == GIT_DELTA_UNREADABLE &&
DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_UNREADABLE))
return 0;
if (!git_pathspec__match(
&diff->pathspec, entry->path,
......@@ -196,6 +200,7 @@ static git_diff_delta *diff_delta__last_for_item(
if (git_oid__cmp(&delta->new_file.id, &item->id) == 0)
return delta;
break;
case GIT_DELTA_UNREADABLE:
case GIT_DELTA_UNTRACKED:
if (diff->strcomp(delta->new_file.path, item->path) == 0 &&
git_oid__cmp(&delta->new_file.id, &item->id) == 0)
......@@ -293,6 +298,10 @@ bool git_diff_delta__should_skip(
(flags & GIT_DIFF_INCLUDE_UNTRACKED) == 0)
return true;
if (delta->status == GIT_DELTA_UNREADABLE &&
(flags & GIT_DIFF_INCLUDE_UNREADABLE) == 0)
return true;
return false;
}
......@@ -734,6 +743,11 @@ static int maybe_modified(
else if (GIT_MODE_TYPE(omode) != GIT_MODE_TYPE(nmode)) {
if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_TYPECHANGE))
status = GIT_DELTA_TYPECHANGE;
else if (nmode == GIT_FILEMODE_UNREADABLE) {
if (!(error = diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem)))
error = diff_delta__from_one(diff, GIT_DELTA_UNREADABLE, nitem);
return error;
}
else {
if (!(error = diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem)))
error = diff_delta__from_one(diff, GIT_DELTA_ADDED, nitem);
......@@ -954,6 +968,13 @@ static int handle_unmatched_new_item(
}
}
else if (nitem->mode == GIT_FILEMODE_UNREADABLE) {
if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_UNREADABLE_AS_UNTRACKED))
delta_type = GIT_DELTA_UNTRACKED;
else
delta_type = GIT_DELTA_UNREADABLE;
}
/* Actually create the record for this item if necessary */
if ((error = diff_delta__from_one(diff, delta_type, nitem)) != 0)
return error;
......
......@@ -112,6 +112,7 @@ int git_diff_file_content__init_from_diff(
has_data = !use_old &&
(diff->opts.flags & GIT_DIFF_SHOW_UNTRACKED_CONTENT) != 0;
break;
case GIT_DELTA_UNREADABLE:
case GIT_DELTA_MODIFIED:
case GIT_DELTA_COPIED:
case GIT_DELTA_RENAMED:
......
......@@ -82,14 +82,15 @@ char git_diff_status_char(git_delta_t status)
char code;
switch (status) {
case GIT_DELTA_ADDED: code = 'A'; break;
case GIT_DELTA_DELETED: code = 'D'; break;
case GIT_DELTA_MODIFIED: code = 'M'; break;
case GIT_DELTA_RENAMED: code = 'R'; break;
case GIT_DELTA_COPIED: code = 'C'; break;
case GIT_DELTA_IGNORED: code = 'I'; break;
case GIT_DELTA_UNTRACKED: code = '?'; break;
default: code = ' '; break;
case GIT_DELTA_ADDED: code = 'A'; break;
case GIT_DELTA_DELETED: code = 'D'; break;
case GIT_DELTA_MODIFIED: code = 'M'; break;
case GIT_DELTA_RENAMED: code = 'R'; break;
case GIT_DELTA_COPIED: code = 'C'; break;
case GIT_DELTA_IGNORED: code = 'I'; break;
case GIT_DELTA_UNTRACKED: code = '?'; break;
case GIT_DELTA_UNREADABLE: code = 'X'; break;
default: code = ' '; break;
}
return code;
......@@ -441,6 +442,7 @@ static int diff_print_patch_file(
if (S_ISDIR(delta->new_file.mode) ||
delta->status == GIT_DELTA_UNMODIFIED ||
delta->status == GIT_DELTA_IGNORED ||
delta->status == GIT_DELTA_UNREADABLE ||
(delta->status == GIT_DELTA_UNTRACKED &&
(pi->flags & GIT_DIFF_SHOW_UNTRACKED_CONTENT) == 0))
return 0;
......
......@@ -114,7 +114,7 @@ static git_diff_delta *diff_delta__merge_like_cgit_reversed(
if ((dup = diff_delta__dup(a, pool)) == NULL)
return NULL;
if (b->status == GIT_DELTA_UNMODIFIED || b->status == GIT_DELTA_UNTRACKED)
if (b->status == GIT_DELTA_UNMODIFIED || b->status == GIT_DELTA_UNTRACKED || b->status == GIT_DELTA_UNREADABLE)
return dup;
if (dup->status == GIT_DELTA_DELETED) {
......@@ -732,6 +732,7 @@ static bool is_rename_source(
switch (delta->status) {
case GIT_DELTA_ADDED:
case GIT_DELTA_UNTRACKED:
case GIT_DELTA_UNREADABLE:
case GIT_DELTA_IGNORED:
return false;
......@@ -786,6 +787,7 @@ GIT_INLINE(bool) delta_is_new_only(git_diff_delta *delta)
{
return (delta->status == GIT_DELTA_ADDED ||
delta->status == GIT_DELTA_UNTRACKED ||
delta->status == GIT_DELTA_UNREADABLE ||
delta->status == GIT_DELTA_IGNORED);
}
......
......@@ -1116,7 +1116,15 @@ int git_path_dirload_with_stat(
git_vector_remove(contents, i--);
continue;
}
/* Treat the file as unreadable if we get any other error */
if (error != 0) {
giterr_clear();
error = 0;
memset(&ps->st, 0, sizeof(ps->st));
ps->st.st_mode = GIT_FILEMODE_UNREADABLE;
continue;
}
break;
}
......
......@@ -62,6 +62,9 @@ static unsigned int workdir_delta2status(
case GIT_DELTA_UNTRACKED:
st = GIT_STATUS_WT_NEW;
break;
case GIT_DELTA_UNREADABLE:
st = GIT_STATUS_WT_UNREADABLE;
break;
case GIT_DELTA_DELETED:
st = GIT_STATUS_WT_DELETED;
break;
......@@ -310,6 +313,10 @@ int git_status_list_new(
diffopt.flags = diffopt.flags | GIT_DIFF_IGNORE_SUBMODULES;
if ((flags & GIT_STATUS_OPT_UPDATE_INDEX) != 0)
diffopt.flags = diffopt.flags | GIT_DIFF_UPDATE_INDEX;
if ((flags & GIT_STATUS_OPT_INCLUDE_UNREADABLE) != 0)
diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_UNREADABLE;
if ((flags & GIT_STATUS_OPT_INCLUDE_UNREADABLE_AS_UNTRACKED) != 0)
diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_UNREADABLE_AS_UNTRACKED;
if ((flags & GIT_STATUS_OPT_RENAMES_FROM_REWRITES) != 0)
findopt.flags = findopt.flags |
......@@ -329,8 +336,9 @@ int git_status_list_new(
if (show != GIT_STATUS_SHOW_INDEX_ONLY) {
if ((error = git_diff_index_to_workdir(
&status->idx2wd, repo, index, &diffopt)) < 0)
&status->idx2wd, repo, index, &diffopt)) < 0) {
goto done;
}
if ((flags & GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR) != 0 &&
(error = git_diff_find_similar(status->idx2wd, &findopt)) < 0)
......@@ -407,8 +415,9 @@ int git_status_foreach_ext(
size_t i;
int error = 0;
if ((error = git_status_list_new(&status, repo, opts)) < 0)
if ((error = git_status_list_new(&status, repo, opts)) < 0) {
return error;
}
git_vector_foreach(&status->paired, i, status_entry) {
const char *path = status_entry->head_to_index ?
......
......@@ -82,6 +82,9 @@ int cb_status__print(
if (status_flags & GIT_STATUS_IGNORED) {
wstatus = 'I'; wcount++;
}
if (status_flags & GIT_STATUS_WT_UNREADABLE) {
wstatus = 'X'; wcount++;
}
fprintf(stderr, "%c%c %s (%d/%d%s)\n",
istatus, wstatus, path, icount, wcount,
......
......@@ -935,3 +935,102 @@ void test_status_worktree__update_stat_cache_0(void)
git_status_list_free(status);
}
void test_status_worktree__unreadable(void)
{
const char *expected_paths[] = { "no_permission/foo" };
const unsigned int expected_statuses[] = {GIT_STATUS_WT_UNREADABLE};
git_repository *repo = cl_git_sandbox_init("empty_standard_repo");
git_status_options opts = GIT_STATUS_OPTIONS_INIT;
status_entry_counts counts = {0};
/* Create directory with no read permission */
cl_git_pass(git_futils_mkdir_r("empty_standard_repo/no_permission", NULL, 0777));
cl_git_mkfile("empty_standard_repo/no_permission/foo", "dummy");
p_chmod("empty_standard_repo/no_permission", 0644);
counts.expected_entry_count = 1;
counts.expected_paths = expected_paths;
counts.expected_statuses = expected_statuses;
opts.show = GIT_STATUS_SHOW_WORKDIR_ONLY;
opts.flags = GIT_STATUS_OPT_DEFAULTS | GIT_STATUS_OPT_INCLUDE_UNREADABLE;
cl_git_pass(
git_status_foreach_ext(repo, &opts, cb_status__normal, &counts) );
/* Restore permissions so we can cleanup :) */
p_chmod("empty_standard_repo/no_permission", 0777);
cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
cl_assert_equal_i(0, counts.wrong_status_flags_count);
cl_assert_equal_i(0, counts.wrong_sorted_path);
}
void test_status_worktree__unreadable_not_included(void)
{
const char *expected_paths[] = { "no_permission/" };
const unsigned int expected_statuses[] = {GIT_STATUS_WT_NEW};
git_repository *repo = cl_git_sandbox_init("empty_standard_repo");
git_status_options opts = GIT_STATUS_OPTIONS_INIT;
status_entry_counts counts = {0};
/* Create directory with no read permission */
cl_git_pass(git_futils_mkdir_r("empty_standard_repo/no_permission", NULL, 0777));
cl_git_mkfile("empty_standard_repo/no_permission/foo", "dummy");
p_chmod("empty_standard_repo/no_permission", 0644);
counts.expected_entry_count = 1;
counts.expected_paths = expected_paths;
counts.expected_statuses = expected_statuses;
opts.show = GIT_STATUS_SHOW_WORKDIR_ONLY;
opts.flags = (GIT_STATUS_OPT_INCLUDE_IGNORED | GIT_STATUS_OPT_INCLUDE_UNTRACKED);
cl_git_pass(
git_status_foreach_ext(repo, &opts, cb_status__normal, &counts) );
/* Restore permissions so we can cleanup :) */
p_chmod("empty_standard_repo/no_permission", 0777);
cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
cl_assert_equal_i(0, counts.wrong_status_flags_count);
cl_assert_equal_i(0, counts.wrong_sorted_path);
}
void test_status_worktree__unreadable_as_untracked(void)
{
const char *expected_paths[] = { "no_permission/foo" };
const unsigned int expected_statuses[] = {GIT_STATUS_WT_NEW};
git_repository *repo = cl_git_sandbox_init("empty_standard_repo");
git_status_options opts = GIT_STATUS_OPTIONS_INIT;
status_entry_counts counts = {0};
/* Create directory with no read permission */
cl_git_pass(git_futils_mkdir_r("empty_standard_repo/no_permission", NULL, 0777));
cl_git_mkfile("empty_standard_repo/no_permission/foo", "dummy");
p_chmod("empty_standard_repo/no_permission", 0644);
counts.expected_entry_count = 1;
counts.expected_paths = expected_paths;
counts.expected_statuses = expected_statuses;
opts.show = GIT_STATUS_SHOW_WORKDIR_ONLY;
opts.flags = GIT_STATUS_OPT_DEFAULTS |
GIT_STATUS_OPT_INCLUDE_UNREADABLE |
GIT_STATUS_OPT_INCLUDE_UNREADABLE_AS_UNTRACKED;
cl_git_pass(
git_status_foreach_ext(repo, &opts, cb_status__normal, &counts) );
/* Restore permissions so we can cleanup :) */
p_chmod("empty_standard_repo/no_permission", 0777);
cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
cl_assert_equal_i(0, counts.wrong_status_flags_count);
cl_assert_equal_i(0, counts.wrong_sorted_path);
}
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