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 { ...@@ -152,6 +152,12 @@ typedef enum {
*/ */
GIT_DIFF_UPDATE_INDEX = (1u << 15), 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 * Options controlling how output will be generated
*/ */
...@@ -237,6 +243,7 @@ typedef enum { ...@@ -237,6 +243,7 @@ typedef enum {
GIT_DELTA_IGNORED = 6, /**< entry is ignored item in workdir */ GIT_DELTA_IGNORED = 6, /**< entry is ignored item in workdir */
GIT_DELTA_UNTRACKED = 7, /**< entry is untracked 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_TYPECHANGE = 8, /**< type of entry changed between old and new */
GIT_DELTA_UNREADABLE = 9, /**< entry is unreadable */
} git_delta_t; } git_delta_t;
/** /**
......
...@@ -43,6 +43,7 @@ typedef enum { ...@@ -43,6 +43,7 @@ typedef enum {
GIT_STATUS_WT_DELETED = (1u << 9), GIT_STATUS_WT_DELETED = (1u << 9),
GIT_STATUS_WT_TYPECHANGE = (1u << 10), GIT_STATUS_WT_TYPECHANGE = (1u << 10),
GIT_STATUS_WT_RENAMED = (1u << 11), GIT_STATUS_WT_RENAMED = (1u << 11),
GIT_STATUS_WT_UNREADABLE = (1u << 12),
GIT_STATUS_IGNORED = (1u << 14), GIT_STATUS_IGNORED = (1u << 14),
} git_status_t; } git_status_t;
...@@ -147,6 +148,8 @@ typedef enum { ...@@ -147,6 +148,8 @@ typedef enum {
GIT_STATUS_OPT_RENAMES_FROM_REWRITES = (1u << 11), GIT_STATUS_OPT_RENAMES_FROM_REWRITES = (1u << 11),
GIT_STATUS_OPT_NO_REFRESH = (1u << 12), GIT_STATUS_OPT_NO_REFRESH = (1u << 12),
GIT_STATUS_OPT_UPDATE_INDEX = (1u << 13), 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; } git_status_opt_t;
#define GIT_STATUS_OPT_DEFAULTS \ #define GIT_STATUS_OPT_DEFAULTS \
......
...@@ -198,7 +198,7 @@ typedef enum { ...@@ -198,7 +198,7 @@ typedef enum {
/** Valid modes for index and tree entries. */ /** Valid modes for index and tree entries. */
typedef enum { typedef enum {
GIT_FILEMODE_NEW = 0000000, GIT_FILEMODE_UNREADABLE = 0000000,
GIT_FILEMODE_TREE = 0040000, GIT_FILEMODE_TREE = 0040000,
GIT_FILEMODE_BLOB = 0100644, GIT_FILEMODE_BLOB = 0100644,
GIT_FILEMODE_BLOB_EXECUTABLE = 0100755, GIT_FILEMODE_BLOB_EXECUTABLE = 0100755,
......
...@@ -119,6 +119,7 @@ static int checkout_notify( ...@@ -119,6 +119,7 @@ static int checkout_notify(
case GIT_DELTA_ADDED: case GIT_DELTA_ADDED:
case GIT_DELTA_IGNORED: case GIT_DELTA_IGNORED:
case GIT_DELTA_UNTRACKED: case GIT_DELTA_UNTRACKED:
case GIT_DELTA_UNREADABLE:
target = &delta->new_file; target = &delta->new_file;
break; break;
case GIT_DELTA_DELETED: case GIT_DELTA_DELETED:
...@@ -2143,6 +2144,7 @@ int git_checkout_iterator( ...@@ -2143,6 +2144,7 @@ int git_checkout_iterator(
diff_opts.flags = diff_opts.flags =
GIT_DIFF_INCLUDE_UNMODIFIED | GIT_DIFF_INCLUDE_UNMODIFIED |
GIT_DIFF_INCLUDE_UNREADABLE |
GIT_DIFF_INCLUDE_UNTRACKED | GIT_DIFF_INCLUDE_UNTRACKED |
GIT_DIFF_RECURSE_UNTRACKED_DIRS | /* needed to match baseline */ GIT_DIFF_RECURSE_UNTRACKED_DIRS | /* needed to match baseline */
GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_IGNORED |
......
...@@ -93,6 +93,10 @@ static int diff_delta__from_one( ...@@ -93,6 +93,10 @@ static int diff_delta__from_one(
DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_UNTRACKED)) DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_UNTRACKED))
return 0; return 0;
if (status == GIT_DELTA_UNREADABLE &&
DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_UNREADABLE))
return 0;
if (!git_pathspec__match( if (!git_pathspec__match(
&diff->pathspec, entry->path, &diff->pathspec, entry->path,
DIFF_FLAG_IS_SET(diff, GIT_DIFF_DISABLE_PATHSPEC_MATCH), DIFF_FLAG_IS_SET(diff, GIT_DIFF_DISABLE_PATHSPEC_MATCH),
...@@ -196,6 +200,7 @@ static git_diff_delta *diff_delta__last_for_item( ...@@ -196,6 +200,7 @@ static git_diff_delta *diff_delta__last_for_item(
if (git_oid__cmp(&delta->new_file.id, &item->id) == 0) if (git_oid__cmp(&delta->new_file.id, &item->id) == 0)
return delta; return delta;
break; break;
case GIT_DELTA_UNREADABLE:
case GIT_DELTA_UNTRACKED: case GIT_DELTA_UNTRACKED:
if (diff->strcomp(delta->new_file.path, item->path) == 0 && if (diff->strcomp(delta->new_file.path, item->path) == 0 &&
git_oid__cmp(&delta->new_file.id, &item->id) == 0) git_oid__cmp(&delta->new_file.id, &item->id) == 0)
...@@ -293,6 +298,10 @@ bool git_diff_delta__should_skip( ...@@ -293,6 +298,10 @@ bool git_diff_delta__should_skip(
(flags & GIT_DIFF_INCLUDE_UNTRACKED) == 0) (flags & GIT_DIFF_INCLUDE_UNTRACKED) == 0)
return true; return true;
if (delta->status == GIT_DELTA_UNREADABLE &&
(flags & GIT_DIFF_INCLUDE_UNREADABLE) == 0)
return true;
return false; return false;
} }
...@@ -734,6 +743,11 @@ static int maybe_modified( ...@@ -734,6 +743,11 @@ static int maybe_modified(
else if (GIT_MODE_TYPE(omode) != GIT_MODE_TYPE(nmode)) { else if (GIT_MODE_TYPE(omode) != GIT_MODE_TYPE(nmode)) {
if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_TYPECHANGE)) if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_TYPECHANGE))
status = GIT_DELTA_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 { else {
if (!(error = diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem))) if (!(error = diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem)))
error = diff_delta__from_one(diff, GIT_DELTA_ADDED, nitem); error = diff_delta__from_one(diff, GIT_DELTA_ADDED, nitem);
...@@ -954,6 +968,13 @@ static int handle_unmatched_new_item( ...@@ -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 */ /* Actually create the record for this item if necessary */
if ((error = diff_delta__from_one(diff, delta_type, nitem)) != 0) if ((error = diff_delta__from_one(diff, delta_type, nitem)) != 0)
return error; return error;
......
...@@ -112,6 +112,7 @@ int git_diff_file_content__init_from_diff( ...@@ -112,6 +112,7 @@ int git_diff_file_content__init_from_diff(
has_data = !use_old && has_data = !use_old &&
(diff->opts.flags & GIT_DIFF_SHOW_UNTRACKED_CONTENT) != 0; (diff->opts.flags & GIT_DIFF_SHOW_UNTRACKED_CONTENT) != 0;
break; break;
case GIT_DELTA_UNREADABLE:
case GIT_DELTA_MODIFIED: case GIT_DELTA_MODIFIED:
case GIT_DELTA_COPIED: case GIT_DELTA_COPIED:
case GIT_DELTA_RENAMED: case GIT_DELTA_RENAMED:
......
...@@ -89,6 +89,7 @@ char git_diff_status_char(git_delta_t status) ...@@ -89,6 +89,7 @@ char git_diff_status_char(git_delta_t status)
case GIT_DELTA_COPIED: code = 'C'; break; case GIT_DELTA_COPIED: code = 'C'; break;
case GIT_DELTA_IGNORED: code = 'I'; break; case GIT_DELTA_IGNORED: code = 'I'; break;
case GIT_DELTA_UNTRACKED: code = '?'; break; case GIT_DELTA_UNTRACKED: code = '?'; break;
case GIT_DELTA_UNREADABLE: code = 'X'; break;
default: code = ' '; break; default: code = ' '; break;
} }
...@@ -441,6 +442,7 @@ static int diff_print_patch_file( ...@@ -441,6 +442,7 @@ static int diff_print_patch_file(
if (S_ISDIR(delta->new_file.mode) || if (S_ISDIR(delta->new_file.mode) ||
delta->status == GIT_DELTA_UNMODIFIED || delta->status == GIT_DELTA_UNMODIFIED ||
delta->status == GIT_DELTA_IGNORED || delta->status == GIT_DELTA_IGNORED ||
delta->status == GIT_DELTA_UNREADABLE ||
(delta->status == GIT_DELTA_UNTRACKED && (delta->status == GIT_DELTA_UNTRACKED &&
(pi->flags & GIT_DIFF_SHOW_UNTRACKED_CONTENT) == 0)) (pi->flags & GIT_DIFF_SHOW_UNTRACKED_CONTENT) == 0))
return 0; return 0;
......
...@@ -114,7 +114,7 @@ static git_diff_delta *diff_delta__merge_like_cgit_reversed( ...@@ -114,7 +114,7 @@ static git_diff_delta *diff_delta__merge_like_cgit_reversed(
if ((dup = diff_delta__dup(a, pool)) == NULL) if ((dup = diff_delta__dup(a, pool)) == NULL)
return 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; return dup;
if (dup->status == GIT_DELTA_DELETED) { if (dup->status == GIT_DELTA_DELETED) {
...@@ -732,6 +732,7 @@ static bool is_rename_source( ...@@ -732,6 +732,7 @@ static bool is_rename_source(
switch (delta->status) { switch (delta->status) {
case GIT_DELTA_ADDED: case GIT_DELTA_ADDED:
case GIT_DELTA_UNTRACKED: case GIT_DELTA_UNTRACKED:
case GIT_DELTA_UNREADABLE:
case GIT_DELTA_IGNORED: case GIT_DELTA_IGNORED:
return false; return false;
...@@ -786,6 +787,7 @@ GIT_INLINE(bool) delta_is_new_only(git_diff_delta *delta) ...@@ -786,6 +787,7 @@ GIT_INLINE(bool) delta_is_new_only(git_diff_delta *delta)
{ {
return (delta->status == GIT_DELTA_ADDED || return (delta->status == GIT_DELTA_ADDED ||
delta->status == GIT_DELTA_UNTRACKED || delta->status == GIT_DELTA_UNTRACKED ||
delta->status == GIT_DELTA_UNREADABLE ||
delta->status == GIT_DELTA_IGNORED); delta->status == GIT_DELTA_IGNORED);
} }
......
...@@ -1116,6 +1116,14 @@ int git_path_dirload_with_stat( ...@@ -1116,6 +1116,14 @@ int git_path_dirload_with_stat(
git_vector_remove(contents, i--); git_vector_remove(contents, i--);
continue; 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; break;
} }
......
...@@ -62,6 +62,9 @@ static unsigned int workdir_delta2status( ...@@ -62,6 +62,9 @@ static unsigned int workdir_delta2status(
case GIT_DELTA_UNTRACKED: case GIT_DELTA_UNTRACKED:
st = GIT_STATUS_WT_NEW; st = GIT_STATUS_WT_NEW;
break; break;
case GIT_DELTA_UNREADABLE:
st = GIT_STATUS_WT_UNREADABLE;
break;
case GIT_DELTA_DELETED: case GIT_DELTA_DELETED:
st = GIT_STATUS_WT_DELETED; st = GIT_STATUS_WT_DELETED;
break; break;
...@@ -310,6 +313,10 @@ int git_status_list_new( ...@@ -310,6 +313,10 @@ int git_status_list_new(
diffopt.flags = diffopt.flags | GIT_DIFF_IGNORE_SUBMODULES; diffopt.flags = diffopt.flags | GIT_DIFF_IGNORE_SUBMODULES;
if ((flags & GIT_STATUS_OPT_UPDATE_INDEX) != 0) if ((flags & GIT_STATUS_OPT_UPDATE_INDEX) != 0)
diffopt.flags = diffopt.flags | GIT_DIFF_UPDATE_INDEX; 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) if ((flags & GIT_STATUS_OPT_RENAMES_FROM_REWRITES) != 0)
findopt.flags = findopt.flags | findopt.flags = findopt.flags |
...@@ -329,8 +336,9 @@ int git_status_list_new( ...@@ -329,8 +336,9 @@ int git_status_list_new(
if (show != GIT_STATUS_SHOW_INDEX_ONLY) { if (show != GIT_STATUS_SHOW_INDEX_ONLY) {
if ((error = git_diff_index_to_workdir( if ((error = git_diff_index_to_workdir(
&status->idx2wd, repo, index, &diffopt)) < 0) &status->idx2wd, repo, index, &diffopt)) < 0) {
goto done; goto done;
}
if ((flags & GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR) != 0 && if ((flags & GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR) != 0 &&
(error = git_diff_find_similar(status->idx2wd, &findopt)) < 0) (error = git_diff_find_similar(status->idx2wd, &findopt)) < 0)
...@@ -407,8 +415,9 @@ int git_status_foreach_ext( ...@@ -407,8 +415,9 @@ int git_status_foreach_ext(
size_t i; size_t i;
int error = 0; 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; return error;
}
git_vector_foreach(&status->paired, i, status_entry) { git_vector_foreach(&status->paired, i, status_entry) {
const char *path = status_entry->head_to_index ? const char *path = status_entry->head_to_index ?
......
...@@ -82,6 +82,9 @@ int cb_status__print( ...@@ -82,6 +82,9 @@ int cb_status__print(
if (status_flags & GIT_STATUS_IGNORED) { if (status_flags & GIT_STATUS_IGNORED) {
wstatus = 'I'; wcount++; wstatus = 'I'; wcount++;
} }
if (status_flags & GIT_STATUS_WT_UNREADABLE) {
wstatus = 'X'; wcount++;
}
fprintf(stderr, "%c%c %s (%d/%d%s)\n", fprintf(stderr, "%c%c %s (%d/%d%s)\n",
istatus, wstatus, path, icount, wcount, istatus, wstatus, path, icount, wcount,
......
...@@ -935,3 +935,102 @@ void test_status_worktree__update_stat_cache_0(void) ...@@ -935,3 +935,102 @@ void test_status_worktree__update_stat_cache_0(void)
git_status_list_free(status); 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