Fix a number of git_odb_exists_prefix bugs The git_odb_exists_prefix API was not dealing correctly when a later backend returned GIT_ENOTFOUND even if an earlier backend had found the object. Additionally, the unit tests were not properly exercising the API and had a couple mistakes in checking the results. Lastly, since the backends are not expected to behavior correctly unless all bytes of the short id are zero except for the prefix, this makes the ODB prefix APIs explicitly clear out the extra bytes so the user doesn't have to be as careful.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
diff --git a/src/odb.c b/src/odb.c
index 085eda5..550951f 100644
--- a/src/odb.c
+++ b/src/odb.c
@@ -640,7 +640,7 @@ int git_odb_exists_prefix(
{
int error = GIT_ENOTFOUND, num_found = 0;
size_t i;
- git_oid last_found = {{0}}, found;
+ git_oid key = {{0}}, last_found = {{0}}, found;
assert(db && short_id);
@@ -659,6 +659,11 @@ int git_odb_exists_prefix(
}
}
+ /* just copy valid part of short_id */
+ memcpy(&key.id, short_id->id, (len + 1) / 2);
+ if (len & 1)
+ key.id[len / 2] &= 0xF0;
+
for (i = 0; i < db->backends.length; ++i) {
backend_internal *internal = git_vector_get(&db->backends, i);
git_odb_backend *b = internal->backend;
@@ -666,7 +671,7 @@ int git_odb_exists_prefix(
if (!b->exists_prefix)
continue;
- error = b->exists_prefix(&found, b, short_id, len);
+ error = b->exists_prefix(&found, b, &key, len);
if (error == GIT_ENOTFOUND || error == GIT_PASSTHROUGH)
continue;
if (error)
@@ -683,11 +688,11 @@ int git_odb_exists_prefix(
}
if (!num_found)
- return git_odb__error_notfound("no match for id prefix", short_id);
+ return git_odb__error_notfound("no match for id prefix", &key);
if (out)
git_oid_cpy(out, &last_found);
- return error;
+ return 0;
}
int git_odb_read_header(size_t *len_p, git_otype *type_p, git_odb *db, const git_oid *id)
@@ -790,7 +795,7 @@ int git_odb_read_prefix(
{
size_t i;
int error = GIT_ENOTFOUND;
- git_oid found_full_oid = {{0}};
+ git_oid key = {{0}}, found_full_oid = {{0}};
git_rawobj raw;
void *data = NULL;
bool found = false;
@@ -809,13 +814,18 @@ int git_odb_read_prefix(
return 0;
}
+ /* just copy valid part of short_id */
+ memcpy(&key.id, short_id->id, (len + 1) / 2);
+ if (len & 1)
+ key.id[len / 2] &= 0xF0;
+
for (i = 0; i < db->backends.length; ++i) {
backend_internal *internal = git_vector_get(&db->backends, i);
git_odb_backend *b = internal->backend;
if (b->read_prefix != NULL) {
git_oid full_oid;
- error = b->read_prefix(&full_oid, &raw.data, &raw.len, &raw.type, b, short_id, len);
+ error = b->read_prefix(&full_oid, &raw.data, &raw.len, &raw.type, b, &key, len);
if (error == GIT_ENOTFOUND || error == GIT_PASSTHROUGH)
continue;
@@ -836,7 +846,7 @@ int git_odb_read_prefix(
}
if (!found)
- return git_odb__error_notfound("no match for prefix", short_id);
+ return git_odb__error_notfound("no match for prefix", &key);
if ((object = odb_object__alloc(&found_full_oid, &raw)) == NULL)
return -1;
diff --git a/tests/odb/loose.c b/tests/odb/loose.c
index ef7136e..c91927c 100644
--- a/tests/odb/loose.c
+++ b/tests/odb/loose.c
@@ -66,23 +66,25 @@ void test_odb_loose__cleanup(void)
void test_odb_loose__exists(void)
{
- git_oid id, id2;
+ git_oid id, id2;
git_odb *odb;
- write_object_files(&one);
+ write_object_files(&one);
cl_git_pass(git_odb_open(&odb, "test-objects"));
- cl_git_pass(git_oid_fromstr(&id, one.id));
+ cl_git_pass(git_oid_fromstr(&id, one.id));
+ cl_assert(git_odb_exists(odb, &id));
- cl_assert(git_odb_exists(odb, &id));
+ cl_git_pass(git_oid_fromstrp(&id, "8b137891"));
+ cl_git_pass(git_odb_exists_prefix(&id2, odb, &id, 8));
+ cl_assert_equal_i(0, git_oid_streq(&id2, one.id));
- cl_assert(git_odb_exists_prefix(&id2, odb, &id, 8));
- cl_assert(git_oid_equal(&id, &id2));
+ /* Test for a missing object */
+ cl_git_pass(git_oid_fromstr(&id, "8b137891791fe96927ad78e64b0aad7bded08baa"));
+ cl_assert(!git_odb_exists(odb, &id));
- /* Test for a non-existant object */
- cl_git_pass(git_oid_fromstr(&id2, "8b137891791fe96927ad78e64b0aad7bded08baa"));
- cl_assert(!git_odb_exists(odb, &id2));
- cl_assert_equal_i(GIT_ENOTFOUND, git_odb_exists_prefix(NULL, odb, &id2, 8));
+ cl_git_pass(git_oid_fromstrp(&id, "8b13789a"));
+ cl_assert_equal_i(GIT_ENOTFOUND, git_odb_exists_prefix(&id2, odb, &id, 8));
git_odb_free(odb);
}