From 2b5a99d8ffbaf3e2a42ada38ce570ecbadba8818 Mon Sep 17 00:00:00 2001 From: Alan Rogers Date: Wed, 14 May 2014 17:02:07 +1000 Subject: [PATCH 01/28] Add a test (failing) for a work tree status. When thees is an unreadable folder, we should still be able to enumerate status. --- tests/status/worktree.c | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/tests/status/worktree.c b/tests/status/worktree.c index ca9068aba..8d1e4dfca 100644 --- a/tests/status/worktree.c +++ b/tests/status/worktree.c @@ -935,3 +935,41 @@ void test_status_worktree__update_stat_cache_0(void) git_status_list_free(status); } + +void test_status_worktree__nopermissions(void) +{ + char path[260*4+1]; + const char *expected_paths[] = {path}; + const unsigned int expected_statuses[] = {GIT_STATUS_WT_NEW}; + const char *folder_name = "no_permission"; + + 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 + sprintf(path, "empty_standard_repo/%s", folder_name); + cl_git_pass(git_futils_mkdir_r(path, NULL, 0777)); + sprintf(path, "empty_standard_repo/%s/foo", folder_name); + cl_git_mkfile(path, "dummy"); + sprintf(path, "empty_standard_repo/%s", folder_name); + p_chmod(path, 0644); + + sprintf(path, "%s/", folder_name); + 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; + + cl_git_pass( + git_status_foreach_ext(repo, &opts, cb_status__normal, &counts) ); + 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); + + // Restore permissions so we can cleanup :) + sprintf(path, "empty_standard_repo/%s", folder_name); + p_chmod(path, 0777); +} From 8d3a2d5fc58a08a22e355beaa0f9fc146facc3b3 Mon Sep 17 00:00:00 2001 From: Alan Rogers Date: Thu, 15 May 2014 16:33:26 +1000 Subject: [PATCH 02/28] Simplify the test. --- tests/status/worktree.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/tests/status/worktree.c b/tests/status/worktree.c index 8d1e4dfca..5f4b7d647 100644 --- a/tests/status/worktree.c +++ b/tests/status/worktree.c @@ -941,21 +941,17 @@ void test_status_worktree__nopermissions(void) char path[260*4+1]; const char *expected_paths[] = {path}; const unsigned int expected_statuses[] = {GIT_STATUS_WT_NEW}; - const char *folder_name = "no_permission"; 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 - sprintf(path, "empty_standard_repo/%s", folder_name); - cl_git_pass(git_futils_mkdir_r(path, NULL, 0777)); - sprintf(path, "empty_standard_repo/%s/foo", folder_name); - cl_git_mkfile(path, "dummy"); - sprintf(path, "empty_standard_repo/%s", folder_name); - p_chmod(path, 0644); + 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); - sprintf(path, "%s/", folder_name); + sprintf(path, "%s/", "no_permission"); counts.expected_entry_count = 1; counts.expected_paths = expected_paths; counts.expected_statuses = expected_statuses; @@ -970,6 +966,5 @@ void test_status_worktree__nopermissions(void) cl_assert_equal_i(0, counts.wrong_sorted_path); // Restore permissions so we can cleanup :) - sprintf(path, "empty_standard_repo/%s", folder_name); - p_chmod(path, 0777); + p_chmod("empty_standard_repo/no_permission", 0777); } From 158c8ba1ee81fe20d3beb16650f30f3be02054f7 Mon Sep 17 00:00:00 2001 From: Alan Rogers Date: Thu, 15 May 2014 16:54:46 +1000 Subject: [PATCH 03/28] Return a specific error for EACCES. --- include/git2/errors.h | 1 + src/path.c | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/include/git2/errors.h b/include/git2/errors.h index e22f0d86d..f09e72566 100644 --- a/include/git2/errors.h +++ b/include/git2/errors.h @@ -41,6 +41,7 @@ typedef enum { GIT_EMERGECONFLICT = -13, /*< Merge conflicts prevented operation */ GIT_ELOCKED = -14, /*< Lock file prevented operation */ GIT_EMODIFIED = -15, /*< Reference value does not match expected */ + GIT_ENOACCESS = -16, /*< Access denied attempting operation */ GIT_PASSTHROUGH = -30, /*< Internal only */ GIT_ITEROVER = -31, /*< Signals end of iteration with iterator */ diff --git a/src/path.c b/src/path.c index e0b00a086..55790ff7c 100644 --- a/src/path.c +++ b/src/path.c @@ -561,6 +561,10 @@ int git_path_set_error(int errno_value, const char *path, const char *action) giterr_set(GITERR_OS, "Failed %s - '%s' already exists", action, path); return GIT_EEXISTS; + case EACCES: + giterr_set(GITERR_OS, "Failed %s - '%s' permission denied", action, path); + return GIT_ENOACCESS; + default: giterr_set(GITERR_OS, "Could not %s '%s'", action, path); return -1; From dc4906f12aedd608b01d04415fdca838eba045f6 Mon Sep 17 00:00:00 2001 From: Alan Rogers Date: Thu, 15 May 2014 17:40:28 +1000 Subject: [PATCH 04/28] Skip unreadable files for now. --- src/path.c | 3 +-- tests/status/worktree.c | 7 +++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/path.c b/src/path.c index 55790ff7c..a056f6983 100644 --- a/src/path.c +++ b/src/path.c @@ -1108,13 +1108,12 @@ int git_path_dirload_with_stat( if ((error = git_buf_joinpath(&full, full.ptr, ps->path)) < 0 || (error = git_path_lstat(full.ptr, &ps->st)) < 0) { - if (error == GIT_ENOTFOUND) { + if (error == GIT_ENOTFOUND || error == GIT_ENOACCESS) { giterr_clear(); error = 0; git_vector_remove(contents, i--); continue; } - break; } diff --git a/tests/status/worktree.c b/tests/status/worktree.c index 5f4b7d647..1fdc112d9 100644 --- a/tests/status/worktree.c +++ b/tests/status/worktree.c @@ -955,16 +955,19 @@ void test_status_worktree__nopermissions(void) counts.expected_entry_count = 1; counts.expected_paths = expected_paths; counts.expected_statuses = expected_statuses; + counts.debug = 1; opts.show = GIT_STATUS_SHOW_WORKDIR_ONLY; opts.flags = GIT_STATUS_OPT_DEFAULTS; 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); - // Restore permissions so we can cleanup :) - p_chmod("empty_standard_repo/no_permission", 0777); } From 9055347944ff13fd5e91de0b29caeecb41c8bff4 Mon Sep 17 00:00:00 2001 From: Alan Rogers Date: Tue, 20 May 2014 17:40:28 +1000 Subject: [PATCH 05/28] Rename GIT_ENOACCESS -> GIT_EUNREADABLE --- include/git2/errors.h | 2 +- src/path.c | 11 ++++------- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/include/git2/errors.h b/include/git2/errors.h index f09e72566..3fb3f5635 100644 --- a/include/git2/errors.h +++ b/include/git2/errors.h @@ -41,7 +41,7 @@ typedef enum { GIT_EMERGECONFLICT = -13, /*< Merge conflicts prevented operation */ GIT_ELOCKED = -14, /*< Lock file prevented operation */ GIT_EMODIFIED = -15, /*< Reference value does not match expected */ - GIT_ENOACCESS = -16, /*< Access denied attempting operation */ + GIT_EUNREADABLE = -16, /*< File or folder is unreadable */ GIT_PASSTHROUGH = -30, /*< Internal only */ GIT_ITEROVER = -31, /*< Signals end of iteration with iterator */ diff --git a/src/path.c b/src/path.c index a056f6983..c3487daf2 100644 --- a/src/path.c +++ b/src/path.c @@ -560,14 +560,10 @@ int git_path_set_error(int errno_value, const char *path, const char *action) case EEXIST: giterr_set(GITERR_OS, "Failed %s - '%s' already exists", action, path); return GIT_EEXISTS; - - case EACCES: - giterr_set(GITERR_OS, "Failed %s - '%s' permission denied", action, path); - return GIT_ENOACCESS; - + default: giterr_set(GITERR_OS, "Could not %s '%s'", action, path); - return -1; + return GIT_EUNREADABLE; } } @@ -1108,12 +1104,13 @@ int git_path_dirload_with_stat( if ((error = git_buf_joinpath(&full, full.ptr, ps->path)) < 0 || (error = git_path_lstat(full.ptr, &ps->st)) < 0) { - if (error == GIT_ENOTFOUND || error == GIT_ENOACCESS) { + if (error == GIT_ENOTFOUND) { giterr_clear(); error = 0; git_vector_remove(contents, i--); continue; } + break; } From f47bc8ff5e844fec15e705e8ebd11bae742b8039 Mon Sep 17 00:00:00 2001 From: Alan Rogers Date: Tue, 20 May 2014 18:16:04 +1000 Subject: [PATCH 06/28] Skip unreadable files for now. --- src/diff.c | 6 +++--- src/status.c | 8 ++++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/diff.c b/src/diff.c index bc23e6b0d..a0cfb5890 100644 --- a/src/diff.c +++ b/src/diff.c @@ -925,14 +925,14 @@ static int handle_unmatched_new_item( error = git_iterator_advance_into(&info->nitem, info->new_iter); /* if real error or no error, proceed with iteration */ - if (error != GIT_ENOTFOUND) + if (error != GIT_ENOTFOUND && error != GIT_EUNREADABLE) return error; giterr_clear(); /* if directory is empty, can't advance into it, so either skip * it or ignore it */ - if (contains_oitem) + if (contains_oitem && error != GIT_EUNREADABLE) return git_iterator_advance(&info->nitem, info->new_iter); delta_type = GIT_DELTA_IGNORED; } @@ -981,7 +981,7 @@ static int handle_unmatched_new_item( } /* Actually create the record for this item if necessary */ - if ((error = diff_delta__from_one(diff, delta_type, nitem)) != 0) + if (error != GIT_EUNREADABLE && (error = diff_delta__from_one(diff, delta_type, nitem)) != 0) return error; /* If user requested TYPECHANGE records, then check for that instead of diff --git a/src/status.c b/src/status.c index 8d7612f72..061c9d5e1 100644 --- a/src/status.c +++ b/src/status.c @@ -329,8 +329,10 @@ 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) { + printf("git_diff_index_to_workdir failed with error %d\n", error); goto done; + } if ((flags & GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR) != 0 && (error = git_diff_find_similar(status->idx2wd, &findopt)) < 0) @@ -407,8 +409,10 @@ 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) { + printf("git_status_list_new failed with error %d\n", error); return error; + } git_vector_foreach(&status->paired, i, status_entry) { const char *path = status_entry->head_to_index ? From 61bef72dc35c593e632dc2008c4eec271a264869 Mon Sep 17 00:00:00 2001 From: Alan Rogers Date: Tue, 20 May 2014 23:57:40 +1000 Subject: [PATCH 07/28] Start adding GIT_DELTA_UNREADABLE and GIT_STATUS_WT_UNREADABLE. --- include/git2/diff.h | 4 ++++ include/git2/status.h | 1 + src/checkout.c | 2 ++ src/diff.c | 38 ++++++++++++++++++++++++++++---------- src/diff_file.c | 1 + src/diff_print.c | 18 ++++++++++-------- src/diff_tform.c | 4 +++- src/status.c | 3 +++ 8 files changed, 52 insertions(+), 19 deletions(-) diff --git a/include/git2/diff.h b/include/git2/diff.h index b40cc6135..ebf47e3c0 100644 --- a/include/git2/diff.h +++ b/include/git2/diff.h @@ -191,6 +191,9 @@ typedef enum { * can apply given diff information to binary files. */ GIT_DIFF_SHOW_BINARY = (1 << 30), + + /** Include unreadable files in the diff */ + GIT_DIFF_INCLUDE_UNREADABLE = (1 << 31), } git_diff_option_t; /** @@ -237,6 +240,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; /** diff --git a/include/git2/status.h b/include/git2/status.h index effe5e1ea..794a629af 100644 --- a/include/git2/status.h +++ b/include/git2/status.h @@ -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; diff --git a/src/checkout.c b/src/checkout.c index 20763fd35..1f793d412 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -116,6 +116,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: @@ -2063,6 +2064,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 | diff --git a/src/diff.c b/src/diff.c index a0cfb5890..32573a270 100644 --- a/src/diff.c +++ b/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, @@ -201,6 +205,11 @@ static git_diff_delta *diff_delta__last_for_item( git_oid__cmp(&delta->new_file.id, &item->id) == 0) return delta; break; + case GIT_DELTA_UNREADABLE: + if (diff->strcomp(delta->new_file.path, item->path) == 0 && + git_oid__cmp(&delta->new_file.id, &item->id) == 0) + return delta; + break; case GIT_DELTA_MODIFIED: if (git_oid__cmp(&delta->old_file.id, &item->id) == 0 || git_oid__cmp(&delta->new_file.id, &item->id) == 0) @@ -293,6 +302,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; } @@ -924,17 +937,22 @@ static int handle_unmatched_new_item( if (recurse_into_dir) { error = git_iterator_advance_into(&info->nitem, info->new_iter); - /* if real error or no error, proceed with iteration */ - if (error != GIT_ENOTFOUND && error != GIT_EUNREADABLE) - return error; - giterr_clear(); + printf("error advancing into diff %d\n", error); + if (error == GIT_EUNREADABLE) { + delta_type = GIT_DELTA_UNREADABLE; + } else { + /* if real error or no error, proceed with iteration */ + if (error != GIT_ENOTFOUND) + return error; + giterr_clear(); - /* if directory is empty, can't advance into it, so either skip - * it or ignore it - */ - if (contains_oitem && error != GIT_EUNREADABLE) - return git_iterator_advance(&info->nitem, info->new_iter); - delta_type = GIT_DELTA_IGNORED; + /* if directory is empty, can't advance into it, so either skip + * it or ignore it + */ + if (contains_oitem ) + return git_iterator_advance(&info->nitem, info->new_iter); + delta_type = GIT_DELTA_IGNORED; + } } } diff --git a/src/diff_file.c b/src/diff_file.c index f2a1d5099..96be0942b 100644 --- a/src/diff_file.c +++ b/src/diff_file.c @@ -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: diff --git a/src/diff_print.c b/src/diff_print.c index 08e1e7f90..964c49540 100644 --- a/src/diff_print.c +++ b/src/diff_print.c @@ -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; @@ -414,6 +415,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; diff --git a/src/diff_tform.c b/src/diff_tform.c index a2dab0ae2..423a0ca33 100644 --- a/src/diff_tform.c +++ b/src/diff_tform.c @@ -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); } diff --git a/src/status.c b/src/status.c index 061c9d5e1..6b8009854 100644 --- a/src/status.c +++ b/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; From 86c9d3dae2561405ec98506e6e72bf845c8315c1 Mon Sep 17 00:00:00 2001 From: Alan Rogers Date: Wed, 21 May 2014 22:54:34 +1000 Subject: [PATCH 08/28] Return GIT_FILEMODE_UNREADABLE for files that fail to stat. --- include/git2/errors.h | 1 - include/git2/types.h | 1 + src/diff.c | 31 +++++++++++++++---------------- src/path.c | 11 +++++++++-- 4 files changed, 25 insertions(+), 19 deletions(-) diff --git a/include/git2/errors.h b/include/git2/errors.h index 3fb3f5635..e22f0d86d 100644 --- a/include/git2/errors.h +++ b/include/git2/errors.h @@ -41,7 +41,6 @@ typedef enum { GIT_EMERGECONFLICT = -13, /*< Merge conflicts prevented operation */ GIT_ELOCKED = -14, /*< Lock file prevented operation */ GIT_EMODIFIED = -15, /*< Reference value does not match expected */ - GIT_EUNREADABLE = -16, /*< File or folder is unreadable */ GIT_PASSTHROUGH = -30, /*< Internal only */ GIT_ITEROVER = -31, /*< Signals end of iteration with iterator */ diff --git a/include/git2/types.h b/include/git2/types.h index 1b6f4cca1..6522ea40a 100644 --- a/include/git2/types.h +++ b/include/git2/types.h @@ -204,6 +204,7 @@ typedef enum { GIT_FILEMODE_BLOB_EXECUTABLE = 0100755, GIT_FILEMODE_LINK = 0120000, GIT_FILEMODE_COMMIT = 0160000, + GIT_FILEMODE_UNREADABLE = 0170000, } git_filemode_t; typedef struct git_refspec git_refspec; diff --git a/src/diff.c b/src/diff.c index 32573a270..c169bdacd 100644 --- a/src/diff.c +++ b/src/diff.c @@ -937,22 +937,17 @@ static int handle_unmatched_new_item( if (recurse_into_dir) { error = git_iterator_advance_into(&info->nitem, info->new_iter); - printf("error advancing into diff %d\n", error); - if (error == GIT_EUNREADABLE) { - delta_type = GIT_DELTA_UNREADABLE; - } else { - /* if real error or no error, proceed with iteration */ - if (error != GIT_ENOTFOUND) - return error; - giterr_clear(); + /* if real error or no error, proceed with iteration */ + if (error != GIT_ENOTFOUND) + return error; + giterr_clear(); - /* if directory is empty, can't advance into it, so either skip - * it or ignore it - */ - if (contains_oitem ) - return git_iterator_advance(&info->nitem, info->new_iter); - delta_type = GIT_DELTA_IGNORED; - } + /* if directory is empty, can't advance into it, so either skip + * it or ignore it + */ + if (contains_oitem ) + return git_iterator_advance(&info->nitem, info->new_iter); + delta_type = GIT_DELTA_IGNORED; } } @@ -998,8 +993,12 @@ static int handle_unmatched_new_item( } } + else if (nitem->mode == GIT_FILEMODE_UNREADABLE) { + delta_type = GIT_DELTA_UNREADABLE; + } + /* Actually create the record for this item if necessary */ - if (error != GIT_EUNREADABLE && (error = diff_delta__from_one(diff, delta_type, nitem)) != 0) + if ((error = diff_delta__from_one(diff, delta_type, nitem)) != 0) return error; /* If user requested TYPECHANGE records, then check for that instead of diff --git a/src/path.c b/src/path.c index c3487daf2..bc89a9be9 100644 --- a/src/path.c +++ b/src/path.c @@ -560,10 +560,10 @@ int git_path_set_error(int errno_value, const char *path, const char *action) case EEXIST: giterr_set(GITERR_OS, "Failed %s - '%s' already exists", action, path); return GIT_EEXISTS; - + default: giterr_set(GITERR_OS, "Could not %s '%s'", action, path); - return GIT_EUNREADABLE; + return -1; } } @@ -1110,6 +1110,13 @@ 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; + ps->st.st_mode = GIT_FILEMODE_UNREADABLE; + continue; + } break; } From 9067d5af4a05086761ea0df08e9ac825214f1bc4 Mon Sep 17 00:00:00 2001 From: Alan Rogers Date: Wed, 21 May 2014 23:13:24 +1000 Subject: [PATCH 09/28] Remove errant whitespace. --- src/diff.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/diff.c b/src/diff.c index c169bdacd..6da983eae 100644 --- a/src/diff.c +++ b/src/diff.c @@ -945,7 +945,7 @@ static int handle_unmatched_new_item( /* if directory is empty, can't advance into it, so either skip * it or ignore it */ - if (contains_oitem ) + if (contains_oitem) return git_iterator_advance(&info->nitem, info->new_iter); delta_type = GIT_DELTA_IGNORED; } From 9532edc028bfd7056e693c1ba75e08ef96448248 Mon Sep 17 00:00:00 2001 From: Alan Rogers Date: Wed, 21 May 2014 23:13:46 +1000 Subject: [PATCH 10/28] Simplify the no permission test. --- tests/status/worktree.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/tests/status/worktree.c b/tests/status/worktree.c index 1fdc112d9..5bb809de8 100644 --- a/tests/status/worktree.c +++ b/tests/status/worktree.c @@ -938,20 +938,18 @@ void test_status_worktree__update_stat_cache_0(void) void test_status_worktree__nopermissions(void) { - char path[260*4+1]; - const char *expected_paths[] = {path}; - const unsigned int expected_statuses[] = {GIT_STATUS_WT_NEW}; + const char *expected_paths[] = { "empty_standard_repo/no_permission" }; + 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 + /* 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); - sprintf(path, "%s/", "no_permission"); counts.expected_entry_count = 1; counts.expected_paths = expected_paths; counts.expected_statuses = expected_statuses; @@ -963,7 +961,7 @@ void test_status_worktree__nopermissions(void) cl_git_pass( git_status_foreach_ext(repo, &opts, cb_status__normal, &counts) ); - // Restore permissions so we can cleanup :) + /* 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); From 6580b11c2a6694d929271817005b23abb0551d43 Mon Sep 17 00:00:00 2001 From: Alan Rogers Date: Thu, 22 May 2014 19:26:55 +1000 Subject: [PATCH 11/28] Remove errant newline --- tests/status/worktree.c | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/status/worktree.c b/tests/status/worktree.c index 5bb809de8..e96d3a916 100644 --- a/tests/status/worktree.c +++ b/tests/status/worktree.c @@ -967,5 +967,4 @@ void test_status_worktree__nopermissions(void) 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); - } From e8cc3032c1ef50cebd138705025bf292dd41a717 Mon Sep 17 00:00:00 2001 From: Alan Rogers Date: Thu, 22 May 2014 19:43:58 +1000 Subject: [PATCH 12/28] Return GIT_DELTA_UNREADABLE for a file with a mode change --- src/diff.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/diff.c b/src/diff.c index 6da983eae..db22e5ee3 100644 --- a/src/diff.c +++ b/src/diff.c @@ -747,6 +747,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); From 7c4bbbf46da0287fe77f517dbea54a5a9aefc599 Mon Sep 17 00:00:00 2001 From: Alan Rogers Date: Fri, 23 May 2014 00:27:34 +1000 Subject: [PATCH 13/28] Try a value for UNREADABLE that won't get masked out?! --- include/git2/types.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/git2/types.h b/include/git2/types.h index 6522ea40a..3b6e610fa 100644 --- a/include/git2/types.h +++ b/include/git2/types.h @@ -203,8 +203,8 @@ typedef enum { GIT_FILEMODE_BLOB = 0100644, GIT_FILEMODE_BLOB_EXECUTABLE = 0100755, GIT_FILEMODE_LINK = 0120000, + GIT_FILEMODE_UNREADABLE = 0130000, GIT_FILEMODE_COMMIT = 0160000, - GIT_FILEMODE_UNREADABLE = 0170000, } git_filemode_t; typedef struct git_refspec git_refspec; From c4366096d440570c84754c8751583ddf4748b6b2 Mon Sep 17 00:00:00 2001 From: Alan Rogers Date: Fri, 23 May 2014 00:28:12 +1000 Subject: [PATCH 14/28] Don't need to duplicate this code. --- src/diff.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/diff.c b/src/diff.c index db22e5ee3..00e6fef8f 100644 --- a/src/diff.c +++ b/src/diff.c @@ -200,12 +200,8 @@ 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_UNTRACKED: - if (diff->strcomp(delta->new_file.path, item->path) == 0 && - 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) return delta; From c3252c11bf3272d86e5a4bed3e32a9034c5d4aaa Mon Sep 17 00:00:00 2001 From: Alan Rogers Date: Fri, 23 May 2014 00:29:04 +1000 Subject: [PATCH 15/28] We do expect the foo path in the nopermissions test --- tests/status/worktree.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/status/worktree.c b/tests/status/worktree.c index e96d3a916..11a4420a5 100644 --- a/tests/status/worktree.c +++ b/tests/status/worktree.c @@ -938,7 +938,7 @@ void test_status_worktree__update_stat_cache_0(void) void test_status_worktree__nopermissions(void) { - const char *expected_paths[] = { "empty_standard_repo/no_permission" }; + const char *expected_paths[] = { "empty_standard_repo/no_permission/foo" }; const unsigned int expected_statuses[] = {GIT_STATUS_WT_UNREADABLE}; git_repository *repo = cl_git_sandbox_init("empty_standard_repo"); @@ -956,7 +956,7 @@ void test_status_worktree__nopermissions(void) counts.debug = 1; opts.show = GIT_STATUS_SHOW_WORKDIR_ONLY; - opts.flags = GIT_STATUS_OPT_DEFAULTS; + opts.flags = GIT_STATUS_OPT_DEFAULTS | GIT_DIFF_INCLUDE_UNREADABLE; cl_git_pass( git_status_foreach_ext(repo, &opts, cb_status__normal, &counts) ); From a777fc378583ca88f860c1d770bb959c25bec276 Mon Sep 17 00:00:00 2001 From: Alan Rogers Date: Fri, 30 May 2014 16:21:13 -0700 Subject: [PATCH 16/28] Remove GIT_FILEMODE_NEW as it's unused. And use 0 for GIT_FILEMODE_UNREADABLE. --- include/git2/types.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/include/git2/types.h b/include/git2/types.h index 3b6e610fa..46dfd8b75 100644 --- a/include/git2/types.h +++ b/include/git2/types.h @@ -198,12 +198,11 @@ typedef enum { /** Valid modes for index and tree entries. */ typedef enum { - GIT_FILEMODE_NEW = 0000000, + GIT_FILEMODE_UNREADABLE = 0000000, GIT_FILEMODE_TREE = 0040000, GIT_FILEMODE_BLOB = 0100644, GIT_FILEMODE_BLOB_EXECUTABLE = 0100755, GIT_FILEMODE_LINK = 0120000, - GIT_FILEMODE_UNREADABLE = 0130000, GIT_FILEMODE_COMMIT = 0160000, } git_filemode_t; From 66271925a118040eac4d18230cfa88461c1e3788 Mon Sep 17 00:00:00 2001 From: Alan Rogers Date: Fri, 30 May 2014 16:21:49 -0700 Subject: [PATCH 17/28] Add GIT_STATUS_OPT_INCLUDE_UNREADABLE --- include/git2/status.h | 1 + src/status.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/include/git2/status.h b/include/git2/status.h index 794a629af..858f68841 100644 --- a/include/git2/status.h +++ b/include/git2/status.h @@ -148,6 +148,7 @@ typedef enum { 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_t; #define GIT_STATUS_OPT_DEFAULTS \ diff --git a/src/status.c b/src/status.c index 6b8009854..5a592a7c5 100644 --- a/src/status.c +++ b/src/status.c @@ -313,6 +313,8 @@ 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_RENAMES_FROM_REWRITES) != 0) findopt.flags = findopt.flags | From 59fcebaa89d6ebdadf59c08c3bee030ce84765a1 Mon Sep 17 00:00:00 2001 From: Alan Rogers Date: Fri, 30 May 2014 16:22:13 -0700 Subject: [PATCH 18/28] Use 'X' for unreadable status. --- tests/status/status_helpers.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/status/status_helpers.c b/tests/status/status_helpers.c index 088279252..5d13caa9a 100644 --- a/tests/status/status_helpers.c +++ b/tests/status/status_helpers.c @@ -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, From 523553f902490935084ea39694c06738bfae4a3e Mon Sep 17 00:00:00 2001 From: Alan Rogers Date: Fri, 30 May 2014 16:22:26 -0700 Subject: [PATCH 19/28] Fix the no permissions test. --- tests/status/worktree.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/status/worktree.c b/tests/status/worktree.c index 11a4420a5..5e1399b8a 100644 --- a/tests/status/worktree.c +++ b/tests/status/worktree.c @@ -938,7 +938,7 @@ void test_status_worktree__update_stat_cache_0(void) void test_status_worktree__nopermissions(void) { - const char *expected_paths[] = { "empty_standard_repo/no_permission/foo" }; + 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"); @@ -953,10 +953,9 @@ void test_status_worktree__nopermissions(void) counts.expected_entry_count = 1; counts.expected_paths = expected_paths; counts.expected_statuses = expected_statuses; - counts.debug = 1; opts.show = GIT_STATUS_SHOW_WORKDIR_ONLY; - opts.flags = GIT_STATUS_OPT_DEFAULTS | GIT_DIFF_INCLUDE_UNREADABLE; + opts.flags = GIT_STATUS_OPT_DEFAULTS | GIT_STATUS_OPT_INCLUDE_UNREADABLE; cl_git_pass( git_status_foreach_ext(repo, &opts, cb_status__normal, &counts) ); From 79d5b5c91683720c9055d1f5ea3f9468ca9356a4 Mon Sep 17 00:00:00 2001 From: Alan Rogers Date: Tue, 3 Jun 2014 17:42:52 -0700 Subject: [PATCH 20/28] Add GIT_STATUS_OPT_INCLUDE_UNREADABLE_AS_UNTRACKED and a (failing) test for it. --- include/git2/status.h | 32 ++++++++++++++++---------------- tests/status/worktree.c | 37 ++++++++++++++++++++++++++++++++++++- 2 files changed, 52 insertions(+), 17 deletions(-) diff --git a/include/git2/status.h b/include/git2/status.h index 858f68841..76ffab663 100644 --- a/include/git2/status.h +++ b/include/git2/status.h @@ -134,28 +134,28 @@ 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_UNREADABLE = (1u << 14), + 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 \ (GIT_STATUS_OPT_INCLUDE_IGNORED | \ GIT_STATUS_OPT_INCLUDE_UNTRACKED | \ GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS) - /** * Options to control how `git_status_foreach_ext()` will issue callbacks. * diff --git a/tests/status/worktree.c b/tests/status/worktree.c index 5e1399b8a..e3af0e5a3 100644 --- a/tests/status/worktree.c +++ b/tests/status/worktree.c @@ -936,7 +936,7 @@ void test_status_worktree__update_stat_cache_0(void) git_status_list_free(status); } -void test_status_worktree__nopermissions(void) +void test_status_worktree__unreadable(void) { const char *expected_paths[] = { "no_permission/foo" }; const unsigned int expected_statuses[] = {GIT_STATUS_WT_UNREADABLE}; @@ -967,3 +967,38 @@ void test_status_worktree__nopermissions(void) 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); +} + From 7b491a7deac7a97f440fb6d29f2f84d5ef797a42 Mon Sep 17 00:00:00 2001 From: Alan Rogers Date: Tue, 3 Jun 2014 17:50:00 -0700 Subject: [PATCH 21/28] GIT_DIFF_INCLUDE_UNREADABLE_AS_UNTRACKED --- include/git2/diff.h | 5 ++++- src/status.c | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/include/git2/diff.h b/include/git2/diff.h index ebf47e3c0..db2233f82 100644 --- a/include/git2/diff.h +++ b/include/git2/diff.h @@ -193,7 +193,10 @@ typedef enum { GIT_DIFF_SHOW_BINARY = (1 << 30), /** Include unreadable files in the diff */ - GIT_DIFF_INCLUDE_UNREADABLE = (1 << 31), + GIT_DIFF_INCLUDE_UNREADABLE = (1 << 27), + + /** Include unreadable files in the diff */ + GIT_DIFF_INCLUDE_UNREADABLE_AS_UNTRACKED = (1 << 31), } git_diff_option_t; /** diff --git a/src/status.c b/src/status.c index 5a592a7c5..e1cb60df9 100644 --- a/src/status.c +++ b/src/status.c @@ -315,6 +315,8 @@ int git_status_list_new( 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 | From 5e6542003e32ce59a8a586f00a7104aaef21fb00 Mon Sep 17 00:00:00 2001 From: Alan Rogers Date: Wed, 4 Jun 2014 11:53:44 -0700 Subject: [PATCH 22/28] Implement GIT_DIFF_INCLUDE_UNREADABLE_AS_UNTRACKED --- src/diff.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/diff.c b/src/diff.c index 00e6fef8f..b55d123c1 100644 --- a/src/diff.c +++ b/src/diff.c @@ -995,7 +995,10 @@ static int handle_unmatched_new_item( } else if (nitem->mode == GIT_FILEMODE_UNREADABLE) { - delta_type = GIT_DELTA_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 */ From a576a34257a0aa952de228ef3c2b012c5742ac09 Mon Sep 17 00:00:00 2001 From: Alan Rogers Date: Wed, 4 Jun 2014 14:47:44 -0700 Subject: [PATCH 23/28] Add another test for unreadable and not included. --- tests/status/worktree.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/tests/status/worktree.c b/tests/status/worktree.c index e3af0e5a3..2e86c03b0 100644 --- a/tests/status/worktree.c +++ b/tests/status/worktree.c @@ -968,6 +968,38 @@ void test_status_worktree__unreadable(void) 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" }; From 54c02d212d70b439f402c74c6b1f6c835daa43fc Mon Sep 17 00:00:00 2001 From: Alan Rogers Date: Wed, 4 Jun 2014 15:27:00 -0700 Subject: [PATCH 24/28] Clear out the struct. --- src/path.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/path.c b/src/path.c index bc89a9be9..0c1013fa0 100644 --- a/src/path.c +++ b/src/path.c @@ -1114,6 +1114,7 @@ int git_path_dirload_with_stat( if (error != 0) { giterr_clear(); error = 0; + memset(&ps->st, 0, sizeof(ps->st)); ps->st.st_mode = GIT_FILEMODE_UNREADABLE; continue; } From 9e2d2f30deab2cad3d2cc6dbd5f498668d18572f Mon Sep 17 00:00:00 2001 From: Alan Rogers Date: Wed, 4 Jun 2014 15:41:48 -0700 Subject: [PATCH 25/28] Whitespace wibbles. --- include/git2/status.h | 1 + include/git2/types.h | 12 ++++++------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/include/git2/status.h b/include/git2/status.h index 76ffab663..3c86e5d7b 100644 --- a/include/git2/status.h +++ b/include/git2/status.h @@ -156,6 +156,7 @@ typedef enum { (GIT_STATUS_OPT_INCLUDE_IGNORED | \ GIT_STATUS_OPT_INCLUDE_UNTRACKED | \ GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS) + /** * Options to control how `git_status_foreach_ext()` will issue callbacks. * diff --git a/include/git2/types.h b/include/git2/types.h index 3019c4677..76175b6bd 100644 --- a/include/git2/types.h +++ b/include/git2/types.h @@ -198,12 +198,12 @@ typedef enum { /** Valid modes for index and tree entries. */ typedef enum { - 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_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; From 35b1471f01c42c601804ef579a91778fceebbdab Mon Sep 17 00:00:00 2001 From: Alan Rogers Date: Tue, 22 Jul 2014 11:15:33 +1000 Subject: [PATCH 26/28] Move the UNREADABLE enums to the correct group. --- include/git2/diff.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/git2/diff.h b/include/git2/diff.h index f126453f4..8147fd31c 100644 --- a/include/git2/diff.h +++ b/include/git2/diff.h @@ -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 */ @@ -191,12 +197,6 @@ typedef enum { * can apply given diff information to binary files. */ GIT_DIFF_SHOW_BINARY = (1 << 30), - - /** Include unreadable files in the diff */ - GIT_DIFF_INCLUDE_UNREADABLE = (1 << 27), - - /** Include unreadable files in the diff */ - GIT_DIFF_INCLUDE_UNREADABLE_AS_UNTRACKED = (1 << 31), } git_diff_option_t; /** From e824e63de6724557946ba155034ff8c864f594d2 Mon Sep 17 00:00:00 2001 From: Alan Rogers Date: Tue, 22 Jul 2014 11:25:56 +1000 Subject: [PATCH 27/28] Remove debug printfs. --- src/status.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/status.c b/src/status.c index e1cb60df9..cb2490042 100644 --- a/src/status.c +++ b/src/status.c @@ -337,7 +337,6 @@ 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) { - printf("git_diff_index_to_workdir failed with error %d\n", error); goto done; } @@ -417,7 +416,6 @@ int git_status_foreach_ext( int error = 0; if ((error = git_status_list_new(&status, repo, opts)) < 0) { - printf("git_status_list_new failed with error %d\n", error); return error; } From 85b7268e387eafce2e5cf53a671e6658ae82d6f0 Mon Sep 17 00:00:00 2001 From: Alan Rogers Date: Wed, 23 Jul 2014 12:17:02 +1000 Subject: [PATCH 28/28] undo indentation change in diff_print.c --- src/diff_print.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/diff_print.c b/src/diff_print.c index 32b109bb2..fb62a5fc1 100644 --- a/src/diff_print.c +++ b/src/diff_print.c @@ -82,15 +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; - case GIT_DELTA_UNREADABLE: code = 'X'; 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;