mirror of
https://git.proxmox.com/git/libgit2
synced 2026-01-02 22:36:57 +00:00
Merge pull request #2484 from libgit2/fix-git-status-list-new-unreadable-folder
Fix git status list new unreadable folder
This commit is contained in:
commit
243db06ce3
@ -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 |
|
||||
|
||||
21
src/diff.c
21
src/diff.c
@ -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);
|
||||
}
|
||||
|
||||
|
||||
10
src/path.c
10
src/path.c
@ -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;
|
||||
}
|
||||
|
||||
|
||||
13
src/status.c
13
src/status.c
@ -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);
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user