summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRussell Belfer <rb@github.com>2014-03-10 10:53:39 -0700
committerRussell Belfer <rb@github.com>2014-03-10 11:34:50 -0700
commit894990788771bef6ca20398e46b217817b8e0db8 (patch)
tree70f93107a116e6a81cee68abb4b2b526fc6c760c
parent9af14886a94ca4d08c7af1ba84b21cba40fce34c (diff)
downloadlibgit2-894990788771bef6ca20398e46b217817b8e0db8.tar.gz
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.
-rw-r--r--src/odb.c24
-rw-r--r--tests/odb/loose.c22
2 files changed, 29 insertions, 17 deletions
diff --git a/src/odb.c b/src/odb.c
index 085eda594..550951fd3 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 ef7136e36..c91927c4a 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);
}