Skip to content

Commit 9ab351c

Browse files
authored
Merge pull request libgit2#6107 from joshtriplett/refresh-handling
Support checking for object existence without refresh
2 parents dfd5b32 + f45ff6c commit 9ab351c

File tree

9 files changed

+228
-13
lines changed

9 files changed

+228
-13
lines changed

include/git2/odb.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,17 @@
2222
*/
2323
GIT_BEGIN_DECL
2424

25+
/** Flags controlling the behavior of ODB lookup operations */
26+
typedef enum {
27+
/**
28+
* Don't call `git_odb_refresh` if the lookup fails. Useful when doing
29+
* a batch of lookup operations for objects that may legitimately not
30+
* exist. When using this flag, you may wish to manually call
31+
* `git_odb_refresh` before processing a batch of objects.
32+
*/
33+
GIT_ODB_LOOKUP_NO_REFRESH = (1 << 0)
34+
} git_odb_lookup_flags_t;
35+
2536
/**
2637
* Function type for callbacks from git_odb_foreach.
2738
*/
@@ -155,6 +166,17 @@ GIT_EXTERN(int) git_odb_read_header(size_t *len_out, git_object_t *type_out, git
155166
*/
156167
GIT_EXTERN(int) git_odb_exists(git_odb *db, const git_oid *id);
157168

169+
/**
170+
* Determine if the given object can be found in the object database, with
171+
* extended options.
172+
*
173+
* @param db database to be searched for the given object.
174+
* @param id the object to search for.
175+
* @param flags flags affecting the lookup (see `git_odb_lookup_flags_t`)
176+
* @return 1 if the object was found, 0 otherwise
177+
*/
178+
GIT_EXTERN(int) git_odb_exists_ext(git_odb *db, const git_oid *id, unsigned int flags);
179+
158180
/**
159181
* Determine if an object can be found in the object database by an
160182
* abbreviated object ID.

include/git2/sys/odb_backend.h

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,11 +69,8 @@ struct git_odb_backend {
6969
* If the backend implements a refreshing mechanism, it should be exposed
7070
* through this endpoint. Each call to `git_odb_refresh()` will invoke it.
7171
*
72-
* However, the backend implementation should try to stay up-to-date as much
73-
* as possible by itself as libgit2 will not automatically invoke
74-
* `git_odb_refresh()`. For instance, a potential strategy for the backend
75-
* implementation to achieve this could be to internally invoke this
76-
* endpoint on failed lookups (ie. `exists()`, `read()`, `read_header()`).
72+
* The odb layer will automatically call this when needed on failed
73+
* lookups (ie. `exists()`, `read()`, `read_header()`).
7774
*/
7875
int GIT_CALLBACK(refresh)(git_odb_backend *);
7976

src/odb.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -882,6 +882,11 @@ int git_odb__freshen(git_odb *db, const git_oid *id)
882882
}
883883

884884
int git_odb_exists(git_odb *db, const git_oid *id)
885+
{
886+
return git_odb_exists_ext(db, id, 0);
887+
}
888+
889+
int git_odb_exists_ext(git_odb *db, const git_oid *id, unsigned int flags)
885890
{
886891
git_odb_object *object;
887892

@@ -899,7 +904,7 @@ int git_odb_exists(git_odb *db, const git_oid *id)
899904
if (odb_exists_1(db, id, false))
900905
return 1;
901906

902-
if (!git_odb_refresh(db))
907+
if (!(flags & GIT_ODB_LOOKUP_NO_REFRESH) && !git_odb_refresh(db))
903908
return odb_exists_1(db, id, true);
904909

905910
/* Failed to refresh, hence not found */

tests/odb/backend/backend_helpers.c

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,18 @@ static int fake_backend__read_prefix(
123123
return 0;
124124
}
125125

126+
static int fake_backend__refresh(git_odb_backend *backend)
127+
{
128+
fake_backend *fake;
129+
130+
fake = (fake_backend *)backend;
131+
132+
fake->refresh_calls++;
133+
134+
return 0;
135+
}
136+
137+
126138
static void fake_backend__free(git_odb_backend *_backend)
127139
{
128140
fake_backend *backend;
@@ -134,7 +146,8 @@ static void fake_backend__free(git_odb_backend *_backend)
134146

135147
int build_fake_backend(
136148
git_odb_backend **out,
137-
const fake_object *objects)
149+
const fake_object *objects,
150+
bool support_refresh)
138151
{
139152
fake_backend *backend;
140153

@@ -143,12 +156,12 @@ int build_fake_backend(
143156

144157
backend->parent.version = GIT_ODB_BACKEND_VERSION;
145158

146-
backend->parent.refresh = NULL;
147159
backend->objects = objects;
148160

149161
backend->parent.read = fake_backend__read;
150162
backend->parent.read_prefix = fake_backend__read_prefix;
151163
backend->parent.read_header = fake_backend__read_header;
164+
backend->parent.refresh = support_refresh ? fake_backend__refresh : NULL;
152165
backend->parent.exists = fake_backend__exists;
153166
backend->parent.exists_prefix = fake_backend__exists_prefix;
154167
backend->parent.free = &fake_backend__free;

tests/odb/backend/backend_helpers.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@ typedef struct {
1313
int read_calls;
1414
int read_header_calls;
1515
int read_prefix_calls;
16+
int refresh_calls;
1617

1718
const fake_object *objects;
1819
} fake_backend;
1920

2021
int build_fake_backend(
2122
git_odb_backend **out,
22-
const fake_object *objects);
23+
const fake_object *objects,
24+
bool support_refresh);

tests/odb/backend/multiple.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,10 @@ void test_odb_backend_multiple__initialize(void)
2929
_obj = NULL;
3030
_repo = cl_git_sandbox_init("testrepo.git");
3131

32-
cl_git_pass(build_fake_backend(&backend, _objects_filled));
32+
cl_git_pass(build_fake_backend(&backend, _objects_filled, false));
3333
_fake_filled = (fake_backend *)backend;
3434

35-
cl_git_pass(build_fake_backend(&backend, _objects_empty));
35+
cl_git_pass(build_fake_backend(&backend, _objects_empty, false));
3636
_fake_empty = (fake_backend *)backend;
3737
}
3838

tests/odb/backend/nonrefreshing.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ static void setup_repository_and_backend(void)
2323

2424
_repo = cl_git_sandbox_init("testrepo.git");
2525

26-
cl_git_pass(build_fake_backend(&backend, _objects));
26+
cl_git_pass(build_fake_backend(&backend, _objects, false));
2727

2828
cl_git_pass(git_repository_odb__weakptr(&odb, _repo));
2929
cl_git_pass(git_odb_add_backend(odb, backend, 10));

tests/odb/backend/refreshing.c

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
#include "clar_libgit2.h"
2+
#include "repository.h"
3+
#include "backend_helpers.h"
4+
5+
static git_repository *_repo;
6+
static fake_backend *_fake;
7+
8+
#define NONEXISTING_HASH "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"
9+
#define EXISTING_HASH "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391"
10+
11+
static const fake_object _objects[] = {
12+
{ EXISTING_HASH, "" },
13+
{ NULL, NULL }
14+
};
15+
16+
static git_oid _nonexisting_oid;
17+
static git_oid _existing_oid;
18+
19+
static void setup_repository_and_backend(void)
20+
{
21+
git_odb *odb = NULL;
22+
git_odb_backend *backend = NULL;
23+
24+
_repo = cl_git_sandbox_init("testrepo.git");
25+
26+
cl_git_pass(build_fake_backend(&backend, _objects, true));
27+
28+
cl_git_pass(git_repository_odb__weakptr(&odb, _repo));
29+
cl_git_pass(git_odb_add_backend(odb, backend, 10));
30+
31+
_fake = (fake_backend *)backend;
32+
}
33+
34+
void test_odb_backend_refreshing__initialize(void)
35+
{
36+
git_oid_fromstr(&_nonexisting_oid, NONEXISTING_HASH);
37+
git_oid_fromstr(&_existing_oid, EXISTING_HASH);
38+
setup_repository_and_backend();
39+
}
40+
41+
void test_odb_backend_refreshing__cleanup(void)
42+
{
43+
cl_git_sandbox_cleanup();
44+
}
45+
46+
void test_odb_backend_refreshing__exists_is_invoked_twice_on_failure(void)
47+
{
48+
git_odb *odb;
49+
50+
cl_git_pass(git_repository_odb__weakptr(&odb, _repo));
51+
cl_assert_equal_b(false, git_odb_exists(odb, &_nonexisting_oid));
52+
53+
cl_assert_equal_i(2, _fake->exists_calls);
54+
cl_assert_equal_i(1, _fake->refresh_calls);
55+
}
56+
57+
void test_odb_backend_refreshing__read_is_invoked_twice_on_failure(void)
58+
{
59+
git_object *obj;
60+
61+
cl_git_fail_with(
62+
git_object_lookup(&obj, _repo, &_nonexisting_oid, GIT_OBJECT_ANY),
63+
GIT_ENOTFOUND);
64+
65+
cl_assert_equal_i(2, _fake->read_calls);
66+
cl_assert_equal_i(1, _fake->refresh_calls);
67+
}
68+
69+
void test_odb_backend_refreshing__readprefix_is_invoked_twice_on_failure(void)
70+
{
71+
git_object *obj;
72+
73+
cl_git_fail_with(
74+
git_object_lookup_prefix(&obj, _repo, &_nonexisting_oid, 7, GIT_OBJECT_ANY),
75+
GIT_ENOTFOUND);
76+
77+
cl_assert_equal_i(2, _fake->read_prefix_calls);
78+
cl_assert_equal_i(1, _fake->refresh_calls);
79+
}
80+
81+
void test_odb_backend_refreshing__readheader_is_invoked_twice_on_failure(void)
82+
{
83+
git_odb *odb;
84+
size_t len;
85+
git_object_t type;
86+
87+
cl_git_pass(git_repository_odb__weakptr(&odb, _repo));
88+
89+
cl_git_fail_with(
90+
git_odb_read_header(&len, &type, odb, &_nonexisting_oid),
91+
GIT_ENOTFOUND);
92+
93+
cl_assert_equal_i(2, _fake->read_header_calls);
94+
cl_assert_equal_i(1, _fake->refresh_calls);
95+
}
96+
97+
void test_odb_backend_refreshing__exists_is_invoked_once_on_success(void)
98+
{
99+
git_odb *odb;
100+
101+
cl_git_pass(git_repository_odb__weakptr(&odb, _repo));
102+
cl_assert_equal_b(true, git_odb_exists(odb, &_existing_oid));
103+
104+
cl_assert_equal_i(1, _fake->exists_calls);
105+
cl_assert_equal_i(0, _fake->refresh_calls);
106+
}
107+
108+
void test_odb_backend_refreshing__read_is_invoked_once_on_success(void)
109+
{
110+
git_object *obj;
111+
112+
cl_git_pass(git_object_lookup(&obj, _repo, &_existing_oid, GIT_OBJECT_ANY));
113+
114+
cl_assert_equal_i(1, _fake->read_calls);
115+
cl_assert_equal_i(0, _fake->refresh_calls);
116+
117+
git_object_free(obj);
118+
}
119+
120+
void test_odb_backend_refreshing__readprefix_is_invoked_once_on_success(void)
121+
{
122+
git_object *obj;
123+
124+
cl_git_pass(git_object_lookup_prefix(&obj, _repo, &_existing_oid, 7, GIT_OBJECT_ANY));
125+
126+
cl_assert_equal_i(1, _fake->read_prefix_calls);
127+
cl_assert_equal_i(0, _fake->refresh_calls);
128+
129+
git_object_free(obj);
130+
}
131+
132+
void test_odb_backend_refreshing__readheader_is_invoked_once_on_success(void)
133+
{
134+
git_odb *odb;
135+
size_t len;
136+
git_object_t type;
137+
138+
cl_git_pass(git_repository_odb__weakptr(&odb, _repo));
139+
140+
cl_git_pass(git_odb_read_header(&len, &type, odb, &_existing_oid));
141+
142+
cl_assert_equal_i(1, _fake->read_header_calls);
143+
cl_assert_equal_i(0, _fake->refresh_calls);
144+
}
145+
146+
void test_odb_backend_refreshing__read_is_invoked_twice_when_revparsing_a_full_oid(void)
147+
{
148+
git_object *obj;
149+
150+
cl_git_fail_with(
151+
git_revparse_single(&obj, _repo, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"),
152+
GIT_ENOTFOUND);
153+
154+
cl_assert_equal_i(2, _fake->read_calls);
155+
cl_assert_equal_i(1, _fake->refresh_calls);
156+
}
157+
158+
void test_odb_backend_refreshing__refresh_is_invoked(void)
159+
{
160+
git_odb *odb;
161+
162+
cl_git_pass(git_repository_odb__weakptr(&odb, _repo));
163+
cl_assert_equal_i(0, git_odb_refresh(odb));
164+
165+
cl_assert_equal_i(1, _fake->refresh_calls);
166+
}
167+
168+
void test_odb_backend_refreshing__refresh_suppressed_with_no_refresh(void)
169+
{
170+
git_odb *odb;
171+
172+
cl_git_pass(git_repository_odb__weakptr(&odb, _repo));
173+
cl_assert_equal_b(false, git_odb_exists_ext(odb, &_nonexisting_oid, GIT_ODB_LOOKUP_NO_REFRESH));
174+
175+
cl_assert_equal_i(0, _fake->refresh_calls);
176+
}

tests/odb/backend/simple.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ static void setup_backend(const fake_object *objs)
1313
{
1414
git_odb_backend *backend;
1515

16-
cl_git_pass(build_fake_backend(&backend, objs));
16+
cl_git_pass(build_fake_backend(&backend, objs, false));
1717

1818
cl_git_pass(git_repository_odb__weakptr(&_odb, _repo));
1919
cl_git_pass(git_odb_add_backend(_odb, backend, 10));

0 commit comments

Comments
 (0)