diff options
author | Edward Thomson <ethomson@edwardthomson.com> | 2021-11-11 15:14:59 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-11-11 15:14:59 -0500 |
commit | 9ab351c0d5330106b5562524cd303cbd5789a1d8 (patch) | |
tree | f3675254e7fc3c3c431f676914fcf198003ec7d2 | |
parent | dfd5b32f35f68a7aae0fb06217c5d0d31f1edf82 (diff) | |
parent | f45ff6c92d8d27ff0c3eb007267eadb4aeeaf16f (diff) | |
download | libgit2-9ab351c0d5330106b5562524cd303cbd5789a1d8.tar.gz |
Merge pull request #6107 from joshtriplett/refresh-handling
Support checking for object existence without refresh
-rw-r--r-- | include/git2/odb.h | 22 | ||||
-rw-r--r-- | include/git2/sys/odb_backend.h | 7 | ||||
-rw-r--r-- | src/odb.c | 7 | ||||
-rw-r--r-- | tests/odb/backend/backend_helpers.c | 17 | ||||
-rw-r--r-- | tests/odb/backend/backend_helpers.h | 4 | ||||
-rw-r--r-- | tests/odb/backend/multiple.c | 4 | ||||
-rw-r--r-- | tests/odb/backend/nonrefreshing.c | 2 | ||||
-rw-r--r-- | tests/odb/backend/refreshing.c | 176 | ||||
-rw-r--r-- | tests/odb/backend/simple.c | 2 |
9 files changed, 228 insertions, 13 deletions
diff --git a/include/git2/odb.h b/include/git2/odb.h index dd484553f..0691aa4d0 100644 --- a/include/git2/odb.h +++ b/include/git2/odb.h @@ -22,6 +22,17 @@ */ GIT_BEGIN_DECL +/** Flags controlling the behavior of ODB lookup operations */ +typedef enum { + /** + * Don't call `git_odb_refresh` if the lookup fails. Useful when doing + * a batch of lookup operations for objects that may legitimately not + * exist. When using this flag, you may wish to manually call + * `git_odb_refresh` before processing a batch of objects. + */ + GIT_ODB_LOOKUP_NO_REFRESH = (1 << 0) +} git_odb_lookup_flags_t; + /** * Function type for callbacks from git_odb_foreach. */ @@ -156,6 +167,17 @@ GIT_EXTERN(int) git_odb_read_header(size_t *len_out, git_object_t *type_out, git GIT_EXTERN(int) git_odb_exists(git_odb *db, const git_oid *id); /** + * Determine if the given object can be found in the object database, with + * extended options. + * + * @param db database to be searched for the given object. + * @param id the object to search for. + * @param flags flags affecting the lookup (see `git_odb_lookup_flags_t`) + * @return 1 if the object was found, 0 otherwise + */ +GIT_EXTERN(int) git_odb_exists_ext(git_odb *db, const git_oid *id, unsigned int flags); + +/** * Determine if an object can be found in the object database by an * abbreviated object ID. * diff --git a/include/git2/sys/odb_backend.h b/include/git2/sys/odb_backend.h index 9ae0ed9b3..8598f94e5 100644 --- a/include/git2/sys/odb_backend.h +++ b/include/git2/sys/odb_backend.h @@ -69,11 +69,8 @@ struct git_odb_backend { * If the backend implements a refreshing mechanism, it should be exposed * through this endpoint. Each call to `git_odb_refresh()` will invoke it. * - * However, the backend implementation should try to stay up-to-date as much - * as possible by itself as libgit2 will not automatically invoke - * `git_odb_refresh()`. For instance, a potential strategy for the backend - * implementation to achieve this could be to internally invoke this - * endpoint on failed lookups (ie. `exists()`, `read()`, `read_header()`). + * The odb layer will automatically call this when needed on failed + * lookups (ie. `exists()`, `read()`, `read_header()`). */ int GIT_CALLBACK(refresh)(git_odb_backend *); @@ -883,6 +883,11 @@ int git_odb__freshen(git_odb *db, const git_oid *id) int git_odb_exists(git_odb *db, const git_oid *id) { + return git_odb_exists_ext(db, id, 0); +} + +int git_odb_exists_ext(git_odb *db, const git_oid *id, unsigned int flags) +{ git_odb_object *object; GIT_ASSERT_ARG(db); @@ -899,7 +904,7 @@ int git_odb_exists(git_odb *db, const git_oid *id) if (odb_exists_1(db, id, false)) return 1; - if (!git_odb_refresh(db)) + if (!(flags & GIT_ODB_LOOKUP_NO_REFRESH) && !git_odb_refresh(db)) return odb_exists_1(db, id, true); /* Failed to refresh, hence not found */ diff --git a/tests/odb/backend/backend_helpers.c b/tests/odb/backend/backend_helpers.c index 139d2fc72..542799242 100644 --- a/tests/odb/backend/backend_helpers.c +++ b/tests/odb/backend/backend_helpers.c @@ -123,6 +123,18 @@ static int fake_backend__read_prefix( return 0; } +static int fake_backend__refresh(git_odb_backend *backend) +{ + fake_backend *fake; + + fake = (fake_backend *)backend; + + fake->refresh_calls++; + + return 0; +} + + static void fake_backend__free(git_odb_backend *_backend) { fake_backend *backend; @@ -134,7 +146,8 @@ static void fake_backend__free(git_odb_backend *_backend) int build_fake_backend( git_odb_backend **out, - const fake_object *objects) + const fake_object *objects, + bool support_refresh) { fake_backend *backend; @@ -143,12 +156,12 @@ int build_fake_backend( backend->parent.version = GIT_ODB_BACKEND_VERSION; - backend->parent.refresh = NULL; backend->objects = objects; backend->parent.read = fake_backend__read; backend->parent.read_prefix = fake_backend__read_prefix; backend->parent.read_header = fake_backend__read_header; + backend->parent.refresh = support_refresh ? fake_backend__refresh : NULL; backend->parent.exists = fake_backend__exists; backend->parent.exists_prefix = fake_backend__exists_prefix; backend->parent.free = &fake_backend__free; diff --git a/tests/odb/backend/backend_helpers.h b/tests/odb/backend/backend_helpers.h index 5c393c0a5..32d7a8bf0 100644 --- a/tests/odb/backend/backend_helpers.h +++ b/tests/odb/backend/backend_helpers.h @@ -13,10 +13,12 @@ typedef struct { int read_calls; int read_header_calls; int read_prefix_calls; + int refresh_calls; const fake_object *objects; } fake_backend; int build_fake_backend( git_odb_backend **out, - const fake_object *objects); + const fake_object *objects, + bool support_refresh); diff --git a/tests/odb/backend/multiple.c b/tests/odb/backend/multiple.c index 1c6068df3..5f1eacd52 100644 --- a/tests/odb/backend/multiple.c +++ b/tests/odb/backend/multiple.c @@ -29,10 +29,10 @@ void test_odb_backend_multiple__initialize(void) _obj = NULL; _repo = cl_git_sandbox_init("testrepo.git"); - cl_git_pass(build_fake_backend(&backend, _objects_filled)); + cl_git_pass(build_fake_backend(&backend, _objects_filled, false)); _fake_filled = (fake_backend *)backend; - cl_git_pass(build_fake_backend(&backend, _objects_empty)); + cl_git_pass(build_fake_backend(&backend, _objects_empty, false)); _fake_empty = (fake_backend *)backend; } diff --git a/tests/odb/backend/nonrefreshing.c b/tests/odb/backend/nonrefreshing.c index ede48ade6..2db10efbc 100644 --- a/tests/odb/backend/nonrefreshing.c +++ b/tests/odb/backend/nonrefreshing.c @@ -23,7 +23,7 @@ static void setup_repository_and_backend(void) _repo = cl_git_sandbox_init("testrepo.git"); - cl_git_pass(build_fake_backend(&backend, _objects)); + cl_git_pass(build_fake_backend(&backend, _objects, false)); cl_git_pass(git_repository_odb__weakptr(&odb, _repo)); cl_git_pass(git_odb_add_backend(odb, backend, 10)); diff --git a/tests/odb/backend/refreshing.c b/tests/odb/backend/refreshing.c new file mode 100644 index 000000000..9e49298a8 --- /dev/null +++ b/tests/odb/backend/refreshing.c @@ -0,0 +1,176 @@ +#include "clar_libgit2.h" +#include "repository.h" +#include "backend_helpers.h" + +static git_repository *_repo; +static fake_backend *_fake; + +#define NONEXISTING_HASH "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef" +#define EXISTING_HASH "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391" + +static const fake_object _objects[] = { + { EXISTING_HASH, "" }, + { NULL, NULL } +}; + +static git_oid _nonexisting_oid; +static git_oid _existing_oid; + +static void setup_repository_and_backend(void) +{ + git_odb *odb = NULL; + git_odb_backend *backend = NULL; + + _repo = cl_git_sandbox_init("testrepo.git"); + + cl_git_pass(build_fake_backend(&backend, _objects, true)); + + cl_git_pass(git_repository_odb__weakptr(&odb, _repo)); + cl_git_pass(git_odb_add_backend(odb, backend, 10)); + + _fake = (fake_backend *)backend; +} + +void test_odb_backend_refreshing__initialize(void) +{ + git_oid_fromstr(&_nonexisting_oid, NONEXISTING_HASH); + git_oid_fromstr(&_existing_oid, EXISTING_HASH); + setup_repository_and_backend(); +} + +void test_odb_backend_refreshing__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +void test_odb_backend_refreshing__exists_is_invoked_twice_on_failure(void) +{ + git_odb *odb; + + cl_git_pass(git_repository_odb__weakptr(&odb, _repo)); + cl_assert_equal_b(false, git_odb_exists(odb, &_nonexisting_oid)); + + cl_assert_equal_i(2, _fake->exists_calls); + cl_assert_equal_i(1, _fake->refresh_calls); +} + +void test_odb_backend_refreshing__read_is_invoked_twice_on_failure(void) +{ + git_object *obj; + + cl_git_fail_with( + git_object_lookup(&obj, _repo, &_nonexisting_oid, GIT_OBJECT_ANY), + GIT_ENOTFOUND); + + cl_assert_equal_i(2, _fake->read_calls); + cl_assert_equal_i(1, _fake->refresh_calls); +} + +void test_odb_backend_refreshing__readprefix_is_invoked_twice_on_failure(void) +{ + git_object *obj; + + cl_git_fail_with( + git_object_lookup_prefix(&obj, _repo, &_nonexisting_oid, 7, GIT_OBJECT_ANY), + GIT_ENOTFOUND); + + cl_assert_equal_i(2, _fake->read_prefix_calls); + cl_assert_equal_i(1, _fake->refresh_calls); +} + +void test_odb_backend_refreshing__readheader_is_invoked_twice_on_failure(void) +{ + git_odb *odb; + size_t len; + git_object_t type; + + cl_git_pass(git_repository_odb__weakptr(&odb, _repo)); + + cl_git_fail_with( + git_odb_read_header(&len, &type, odb, &_nonexisting_oid), + GIT_ENOTFOUND); + + cl_assert_equal_i(2, _fake->read_header_calls); + cl_assert_equal_i(1, _fake->refresh_calls); +} + +void test_odb_backend_refreshing__exists_is_invoked_once_on_success(void) +{ + git_odb *odb; + + cl_git_pass(git_repository_odb__weakptr(&odb, _repo)); + cl_assert_equal_b(true, git_odb_exists(odb, &_existing_oid)); + + cl_assert_equal_i(1, _fake->exists_calls); + cl_assert_equal_i(0, _fake->refresh_calls); +} + +void test_odb_backend_refreshing__read_is_invoked_once_on_success(void) +{ + git_object *obj; + + cl_git_pass(git_object_lookup(&obj, _repo, &_existing_oid, GIT_OBJECT_ANY)); + + cl_assert_equal_i(1, _fake->read_calls); + cl_assert_equal_i(0, _fake->refresh_calls); + + git_object_free(obj); +} + +void test_odb_backend_refreshing__readprefix_is_invoked_once_on_success(void) +{ + git_object *obj; + + cl_git_pass(git_object_lookup_prefix(&obj, _repo, &_existing_oid, 7, GIT_OBJECT_ANY)); + + cl_assert_equal_i(1, _fake->read_prefix_calls); + cl_assert_equal_i(0, _fake->refresh_calls); + + git_object_free(obj); +} + +void test_odb_backend_refreshing__readheader_is_invoked_once_on_success(void) +{ + git_odb *odb; + size_t len; + git_object_t type; + + cl_git_pass(git_repository_odb__weakptr(&odb, _repo)); + + cl_git_pass(git_odb_read_header(&len, &type, odb, &_existing_oid)); + + cl_assert_equal_i(1, _fake->read_header_calls); + cl_assert_equal_i(0, _fake->refresh_calls); +} + +void test_odb_backend_refreshing__read_is_invoked_twice_when_revparsing_a_full_oid(void) +{ + git_object *obj; + + cl_git_fail_with( + git_revparse_single(&obj, _repo, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"), + GIT_ENOTFOUND); + + cl_assert_equal_i(2, _fake->read_calls); + cl_assert_equal_i(1, _fake->refresh_calls); +} + +void test_odb_backend_refreshing__refresh_is_invoked(void) +{ + git_odb *odb; + + cl_git_pass(git_repository_odb__weakptr(&odb, _repo)); + cl_assert_equal_i(0, git_odb_refresh(odb)); + + cl_assert_equal_i(1, _fake->refresh_calls); +} + +void test_odb_backend_refreshing__refresh_suppressed_with_no_refresh(void) +{ + git_odb *odb; + + cl_git_pass(git_repository_odb__weakptr(&odb, _repo)); + cl_assert_equal_b(false, git_odb_exists_ext(odb, &_nonexisting_oid, GIT_ODB_LOOKUP_NO_REFRESH)); + + cl_assert_equal_i(0, _fake->refresh_calls); +} diff --git a/tests/odb/backend/simple.c b/tests/odb/backend/simple.c index 484dcba5a..6c9293ac0 100644 --- a/tests/odb/backend/simple.c +++ b/tests/odb/backend/simple.c @@ -13,7 +13,7 @@ static void setup_backend(const fake_object *objs) { git_odb_backend *backend; - cl_git_pass(build_fake_backend(&backend, objs)); + cl_git_pass(build_fake_backend(&backend, objs, false)); cl_git_pass(git_repository_odb__weakptr(&_odb, _repo)); cl_git_pass(git_odb_add_backend(_odb, backend, 10)); |