diff options
author | Vicent Martà <vicent@github.com> | 2013-11-05 06:05:32 -0800 |
---|---|---|
committer | Vicent Martà <vicent@github.com> | 2013-11-05 06:05:32 -0800 |
commit | ffd040532a1f3c7f4e268be682bb91fe724693be (patch) | |
tree | 4a283f083fe2fe0c0678a6209a19b9a29f2c8f51 | |
parent | b7fbfbb21f4248bf4103a2c13479bf65ba175f36 (diff) | |
parent | 1eab9f0e32178a9aac941583c69e1b9cf9849f77 (diff) | |
download | libgit2-ffd040532a1f3c7f4e268be682bb91fe724693be.tar.gz |
Merge pull request #1941 from libgit2/rb/preserve-iterator-error
Preserve error messages during file system iterator cleanup
-rw-r--r-- | include/git2/errors.h | 14 | ||||
-rw-r--r-- | src/errors.c | 18 | ||||
-rw-r--r-- | src/iterator.c | 13 | ||||
-rw-r--r-- | tests-clar/repo/iterator.c | 34 |
4 files changed, 79 insertions, 0 deletions
diff --git a/include/git2/errors.h b/include/git2/errors.h index a454ac956..be7a31d8e 100644 --- a/include/git2/errors.h +++ b/include/git2/errors.h @@ -8,6 +8,7 @@ #define INCLUDE_git_errors_h__ #include "common.h" +#include "buffer.h" /** * @file git2/errors.h @@ -45,6 +46,7 @@ typedef struct { /** Error classes */ typedef enum { + GITERR_NONE = 0, GITERR_NOMEMORY, GITERR_OS, GITERR_INVALID, @@ -85,6 +87,18 @@ GIT_EXTERN(const git_error *) giterr_last(void); GIT_EXTERN(void) giterr_clear(void); /** + * Get the last error data and clear it. + * + * This copies the last error into the given `git_error` struct + * and returns 0 if the copy was successful, leaving the error + * cleared as if `giterr_clear` had been called. + * + * If there was no existing error in the library, -1 will be returned + * and the contents of `cpy` will be left unmodified. + */ +GIT_EXTERN(int) giterr_detach(git_error *cpy); + +/** * Set the error message string for this thread. * * This function is public so that custom ODB backends and the like can diff --git a/src/errors.c b/src/errors.c index c9d9e4e37..d04da4ca9 100644 --- a/src/errors.c +++ b/src/errors.c @@ -112,6 +112,24 @@ void giterr_clear(void) #endif } +int giterr_detach(git_error *cpy) +{ + git_error *error = GIT_GLOBAL->last_error; + + assert(cpy); + + if (!error) + return -1; + + cpy->message = error->message; + cpy->klass = error->klass; + + error->message = NULL; + giterr_clear(); + + return 0; +} + const git_error *giterr_last(void) { return GIT_GLOBAL->last_error; diff --git a/src/iterator.c b/src/iterator.c index c0d7862ff..8646399ab 100644 --- a/src/iterator.c +++ b/src/iterator.c @@ -991,8 +991,21 @@ static int fs_iterator__expand_dir(fs_iterator *fi) fi->base.start, fi->base.end, &ff->entries); if (error < 0) { + git_error last_error = {0}; + + giterr_detach(&last_error); + + /* these callbacks may clear the error message */ fs_iterator__free_frame(ff); fs_iterator__advance_over(NULL, (git_iterator *)fi); + /* next time return value we skipped to */ + fi->base.flags &= ~GIT_ITERATOR_FIRST_ACCESS; + + if (last_error.message) { + giterr_set_str(last_error.klass, last_error.message); + free(last_error.message); + } + return error; } diff --git a/tests-clar/repo/iterator.c b/tests-clar/repo/iterator.c index 1c513e9e7..56b51852c 100644 --- a/tests-clar/repo/iterator.c +++ b/tests-clar/repo/iterator.c @@ -926,3 +926,37 @@ void test_repo_iterator__fs2(void) expect_iterator_items(i, 12, expect_base, 12, expect_base); git_iterator_free(i); } + +void test_repo_iterator__fs_preserves_error(void) +{ + git_iterator *i; + const git_index_entry *e; + + if (!cl_is_chmod_supported()) + return; + + g_repo = cl_git_sandbox_init("empty_standard_repo"); + + cl_must_pass(p_mkdir("empty_standard_repo/r", 0777)); + cl_git_mkfile("empty_standard_repo/r/a", "hello"); + cl_must_pass(p_mkdir("empty_standard_repo/r/b", 0777)); + cl_git_mkfile("empty_standard_repo/r/b/problem", "not me"); + cl_must_pass(p_chmod("empty_standard_repo/r/b", 0000)); + cl_must_pass(p_mkdir("empty_standard_repo/r/c", 0777)); + cl_git_mkfile("empty_standard_repo/r/d", "final"); + + cl_git_pass(git_iterator_for_filesystem( + &i, "empty_standard_repo/r", 0, NULL, NULL)); + + cl_git_pass(git_iterator_advance(&e, i)); /* a */ + cl_git_fail(git_iterator_advance(&e, i)); /* b */ + cl_assert(giterr_last()); + cl_assert(giterr_last()->message != NULL); + /* skip 'c/' empty directory */ + cl_git_pass(git_iterator_advance(&e, i)); /* d */ + cl_assert_equal_i(GIT_ITEROVER, git_iterator_advance(&e, i)); + + cl_must_pass(p_chmod("empty_standard_repo/r/b", 0777)); + + git_iterator_free(i); +} |