Commit f5753999 by Russell Belfer

Add exists_prefix to ODB backend and ODB API

parent 0a62caf4
...@@ -159,6 +159,19 @@ GIT_EXTERN(int) git_odb_read_header(size_t *len_out, git_otype *type_out, git_od ...@@ -159,6 +159,19 @@ GIT_EXTERN(int) git_odb_read_header(size_t *len_out, git_otype *type_out, git_od
GIT_EXTERN(int) git_odb_exists(git_odb *db, const git_oid *id); GIT_EXTERN(int) git_odb_exists(git_odb *db, const git_oid *id);
/** /**
* Determine if objects can be found in the object database from a short OID.
*
* @param out The full OID of the found object if just one is found.
* @param db The database to be searched for the given object.
* @param short_id A prefix of the id of the object to read.
* @param len The length of the prefix.
* @return 0 if found, GIT_ENOTFOUND if not found, GIT_EAMBIGUOUS if multiple
* matches were found, other value < 0 if there was a read error.
*/
GIT_EXTERN(int) git_odb_exists_prefix(
git_oid *out, git_odb *db, const git_oid *short_id, size_t len);
/**
* Refresh the object database to load newly added files. * Refresh the object database to load newly added files.
* *
* If the object databases have changed on disk while the library * If the object databases have changed on disk while the library
......
...@@ -35,11 +35,8 @@ struct git_odb_backend { ...@@ -35,11 +35,8 @@ struct git_odb_backend {
int (* read)( int (* read)(
void **, size_t *, git_otype *, git_odb_backend *, const git_oid *); void **, size_t *, git_otype *, git_odb_backend *, const git_oid *);
/* To find a unique object given a prefix /* To find a unique object given a prefix of its oid. The oid given
* of its oid. * must be so that the remaining (GIT_OID_HEXSZ - len)*4 bits are 0s.
* The oid given must be so that the
* remaining (GIT_OID_HEXSZ - len)*4 bits
* are 0s.
*/ */
int (* read_prefix)( int (* read_prefix)(
git_oid *, void **, size_t *, git_otype *, git_oid *, void **, size_t *, git_otype *,
...@@ -64,6 +61,9 @@ struct git_odb_backend { ...@@ -64,6 +61,9 @@ struct git_odb_backend {
int (* exists)( int (* exists)(
git_odb_backend *, const git_oid *); git_odb_backend *, const git_oid *);
int (* exists_prefix)(
git_oid *, git_odb_backend *, const git_oid *, size_t);
/** /**
* If the backend implements a refreshing mechanism, it should be exposed * If the backend implements a refreshing mechanism, it should be exposed
* through this endpoint. Each call to `git_odb_refresh()` will invoke it. * through this endpoint. Each call to `git_odb_refresh()` will invoke it.
......
...@@ -635,6 +635,61 @@ int git_odb_exists(git_odb *db, const git_oid *id) ...@@ -635,6 +635,61 @@ int git_odb_exists(git_odb *db, const git_oid *id)
return (int)found; return (int)found;
} }
int git_odb_exists_prefix(
git_oid *out, git_odb *db, const git_oid *short_id, size_t len)
{
int error = GIT_ENOTFOUND, num_found = 0;
size_t i;
git_oid last_found = {{0}}, found;
assert(db && short_id);
if (len < GIT_OID_MINPREFIXLEN)
return git_odb__error_ambiguous("prefix length too short");
if (len > GIT_OID_HEXSZ)
len = GIT_OID_HEXSZ;
if (len == GIT_OID_HEXSZ) {
if (git_odb_exists(db, short_id)) {
if (out)
git_oid_cpy(out, short_id);
return 0;
} else {
return git_odb__error_notfound("no match for id prefix", short_id);
}
}
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->exists_prefix)
continue;
error = b->exists_prefix(&found, b, short_id, len);
if (error == GIT_ENOTFOUND || error == GIT_PASSTHROUGH)
continue;
if (error)
return error;
/* make sure found item doesn't introduce ambiguity */
if (num_found) {
if (git_oid__cmp(&last_found, &found))
return git_odb__error_ambiguous("multiple matches for prefix");
} else {
git_oid_cpy(&last_found, &found);
num_found++;
}
}
if (!num_found)
return git_odb__error_notfound("no match for id prefix", short_id);
if (out)
git_oid_cpy(out, &last_found);
return error;
}
int git_odb_read_header(size_t *len_p, git_otype *type_p, git_odb *db, const git_oid *id) int git_odb_read_header(size_t *len_p, git_otype *type_p, git_odb *db, const git_oid *id)
{ {
int error; int error;
......
...@@ -519,11 +519,11 @@ static int locate_object_short_oid( ...@@ -519,11 +519,11 @@ static int locate_object_short_oid(
loose_locate_object_state state; loose_locate_object_state state;
int error; int error;
/* prealloc memory for OBJ_DIR/xx/ */ /* prealloc memory for OBJ_DIR/xx/xx..38x..xx */
if (git_buf_grow(object_location, dir_len + 5) < 0) if (git_buf_grow(object_location, dir_len + 3 + GIT_OID_HEXSZ) < 0)
return -1; return -1;
git_buf_sets(object_location, objects_dir); git_buf_set(object_location, objects_dir, dir_len);
git_path_to_dir(object_location); git_path_to_dir(object_location);
/* save adjusted position at end of dir so it can be restored later */ /* save adjusted position at end of dir so it can be restored later */
...@@ -533,8 +533,9 @@ static int locate_object_short_oid( ...@@ -533,8 +533,9 @@ static int locate_object_short_oid(
git_oid_fmt((char *)state.short_oid, short_oid); git_oid_fmt((char *)state.short_oid, short_oid);
/* Explore OBJ_DIR/xx/ where xx is the beginning of hex formatted short oid */ /* Explore OBJ_DIR/xx/ where xx is the beginning of hex formatted short oid */
if (git_buf_printf(object_location, "%.2s/", state.short_oid) < 0) if (git_buf_put(object_location, (char *)state.short_oid, 3) < 0)
return -1; return -1;
object_location->ptr[object_location->size - 1] = '/';
/* Check that directory exists */ /* Check that directory exists */
if (git_path_isdir(object_location->ptr) == false) if (git_path_isdir(object_location->ptr) == false)
...@@ -691,6 +692,25 @@ static int loose_backend__exists(git_odb_backend *backend, const git_oid *oid) ...@@ -691,6 +692,25 @@ static int loose_backend__exists(git_odb_backend *backend, const git_oid *oid)
return !error; return !error;
} }
static int loose_backend__exists_prefix(
git_oid *out, git_odb_backend *backend, const git_oid *short_id, size_t len)
{
git_buf object_path = GIT_BUF_INIT;
int error;
assert(backend && out && short_id);
if (len < GIT_OID_MINPREFIXLEN)
return git_odb__error_ambiguous("prefix length too short");
error = locate_object_short_oid(
&object_path, out, (loose_backend *)backend, short_id, len);
git_buf_free(&object_path);
return error;
}
struct foreach_state { struct foreach_state {
size_t dir_len; size_t dir_len;
git_odb_foreach_cb cb; git_odb_foreach_cb cb;
...@@ -939,6 +959,7 @@ int git_odb_backend_loose( ...@@ -939,6 +959,7 @@ int git_odb_backend_loose(
backend->parent.read_header = &loose_backend__read_header; backend->parent.read_header = &loose_backend__read_header;
backend->parent.writestream = &loose_backend__stream; backend->parent.writestream = &loose_backend__stream;
backend->parent.exists = &loose_backend__exists; backend->parent.exists = &loose_backend__exists;
backend->parent.exists_prefix = &loose_backend__exists_prefix;
backend->parent.foreach = &loose_backend__foreach; backend->parent.foreach = &loose_backend__foreach;
backend->parent.free = &loose_backend__free; backend->parent.free = &loose_backend__free;
......
...@@ -493,6 +493,23 @@ static int pack_backend__exists(git_odb_backend *backend, const git_oid *oid) ...@@ -493,6 +493,23 @@ static int pack_backend__exists(git_odb_backend *backend, const git_oid *oid)
return pack_entry_find(&e, (struct pack_backend *)backend, oid) == 0; return pack_entry_find(&e, (struct pack_backend *)backend, oid) == 0;
} }
static int pack_backend__exists_prefix(
git_oid *out, git_odb_backend *backend, const git_oid *short_id, size_t len)
{
int error;
struct pack_backend *pb = (struct pack_backend *)backend;
struct git_pack_entry e = {0};
error = pack_entry_find_prefix(&e, pb, short_id, len);
if (error == GIT_ENOTFOUND && !(error = pack_backend__refresh(backend)))
error = pack_entry_find_prefix(&e, pb, short_id, len);
git_oid_cpy(out, &e.sha1);
return error;
}
static int pack_backend__foreach(git_odb_backend *_backend, git_odb_foreach_cb cb, void *data) static int pack_backend__foreach(git_odb_backend *_backend, git_odb_foreach_cb cb, void *data)
{ {
int error; int error;
...@@ -612,6 +629,7 @@ static int pack_backend__alloc(struct pack_backend **out, size_t initial_size) ...@@ -612,6 +629,7 @@ static int pack_backend__alloc(struct pack_backend **out, size_t initial_size)
backend->parent.read_prefix = &pack_backend__read_prefix; backend->parent.read_prefix = &pack_backend__read_prefix;
backend->parent.read_header = &pack_backend__read_header; backend->parent.read_header = &pack_backend__read_header;
backend->parent.exists = &pack_backend__exists; backend->parent.exists = &pack_backend__exists;
backend->parent.exists_prefix = &pack_backend__exists_prefix;
backend->parent.refresh = &pack_backend__refresh; backend->parent.refresh = &pack_backend__refresh;
backend->parent.foreach = &pack_backend__foreach; backend->parent.foreach = &pack_backend__foreach;
backend->parent.writepack = &pack_backend__writepack; backend->parent.writepack = &pack_backend__writepack;
......
...@@ -76,9 +76,13 @@ void test_odb_loose__exists(void) ...@@ -76,9 +76,13 @@ void test_odb_loose__exists(void)
cl_assert(git_odb_exists(odb, &id)); cl_assert(git_odb_exists(odb, &id));
cl_assert(git_odb_exists_prefix(&id2, odb, &id, 8));
cl_assert(git_oid_equal(&id, &id2));
/* Test for a non-existant object */ /* Test for a non-existant object */
cl_git_pass(git_oid_fromstr(&id2, "8b137891791fe96927ad78e64b0aad7bded08baa")); cl_git_pass(git_oid_fromstr(&id2, "8b137891791fe96927ad78e64b0aad7bded08baa"));
cl_assert(!git_odb_exists(odb, &id2)); cl_assert(!git_odb_exists(odb, &id2));
cl_assert_equal_i(GIT_ENOTFOUND, git_odb_exists_prefix(NULL, odb, &id2, 8));
git_odb_free(odb); git_odb_free(odb);
} }
......
...@@ -23,9 +23,14 @@ void test_odb_mixed__dup_oid(void) { ...@@ -23,9 +23,14 @@ void test_odb_mixed__dup_oid(void) {
cl_git_pass(git_oid_fromstr(&oid, hex)); cl_git_pass(git_oid_fromstr(&oid, hex));
cl_git_pass(git_odb_read_prefix(&obj, _odb, &oid, GIT_OID_HEXSZ)); cl_git_pass(git_odb_read_prefix(&obj, _odb, &oid, GIT_OID_HEXSZ));
git_odb_object_free(obj); git_odb_object_free(obj);
cl_git_pass(git_odb_exists_prefix(NULL, _odb, &oid, GIT_OID_HEXSZ));
cl_git_pass(git_oid_fromstrn(&oid, short_hex, sizeof(short_hex) - 1)); cl_git_pass(git_oid_fromstrn(&oid, short_hex, sizeof(short_hex) - 1));
cl_git_pass(git_odb_read_prefix(&obj, _odb, &oid, sizeof(short_hex) - 1)); cl_git_pass(git_odb_read_prefix(&obj, _odb, &oid, sizeof(short_hex) - 1));
git_odb_object_free(obj); git_odb_object_free(obj);
cl_git_pass(git_odb_exists_prefix(NULL, _odb, &oid, sizeof(short_hex) - 1));
} }
/* some known sha collisions of file content: /* some known sha collisions of file content:
...@@ -37,7 +42,7 @@ void test_odb_mixed__dup_oid(void) { ...@@ -37,7 +42,7 @@ void test_odb_mixed__dup_oid(void) {
void test_odb_mixed__dup_oid_prefix_0(void) { void test_odb_mixed__dup_oid_prefix_0(void) {
char hex[10]; char hex[10];
git_oid oid; git_oid oid, found;
git_odb_object *obj; git_odb_object *obj;
/* ambiguous in the same pack file */ /* ambiguous in the same pack file */
...@@ -46,10 +51,14 @@ void test_odb_mixed__dup_oid_prefix_0(void) { ...@@ -46,10 +51,14 @@ void test_odb_mixed__dup_oid_prefix_0(void) {
cl_git_pass(git_oid_fromstrn(&oid, hex, strlen(hex))); cl_git_pass(git_oid_fromstrn(&oid, hex, strlen(hex)));
cl_assert_equal_i( cl_assert_equal_i(
GIT_EAMBIGUOUS, git_odb_read_prefix(&obj, _odb, &oid, strlen(hex))); GIT_EAMBIGUOUS, git_odb_read_prefix(&obj, _odb, &oid, strlen(hex)));
cl_assert_equal_i(
GIT_EAMBIGUOUS, git_odb_exists_prefix(&found, _odb, &oid, strlen(hex)));
strncpy(hex, "dea509d09", sizeof(hex)); strncpy(hex, "dea509d09", sizeof(hex));
cl_git_pass(git_oid_fromstrn(&oid, hex, strlen(hex))); cl_git_pass(git_oid_fromstrn(&oid, hex, strlen(hex)));
cl_git_pass(git_odb_read_prefix(&obj, _odb, &oid, strlen(hex))); cl_git_pass(git_odb_read_prefix(&obj, _odb, &oid, strlen(hex)));
cl_git_pass(git_odb_exists_prefix(&found, _odb, &oid, strlen(hex)));
cl_assert(git_oid_equal(&found, git_odb_object_id(obj)));
git_odb_object_free(obj); git_odb_object_free(obj);
strncpy(hex, "dea509d0b", sizeof(hex)); strncpy(hex, "dea509d0b", sizeof(hex));
...@@ -63,10 +72,14 @@ void test_odb_mixed__dup_oid_prefix_0(void) { ...@@ -63,10 +72,14 @@ void test_odb_mixed__dup_oid_prefix_0(void) {
cl_git_pass(git_oid_fromstrn(&oid, hex, strlen(hex))); cl_git_pass(git_oid_fromstrn(&oid, hex, strlen(hex)));
cl_assert_equal_i( cl_assert_equal_i(
GIT_EAMBIGUOUS, git_odb_read_prefix(&obj, _odb, &oid, strlen(hex))); GIT_EAMBIGUOUS, git_odb_read_prefix(&obj, _odb, &oid, strlen(hex)));
cl_assert_equal_i(
GIT_EAMBIGUOUS, git_odb_exists_prefix(&found, _odb, &oid, strlen(hex)));
strncpy(hex, "81b5bff5b", sizeof(hex)); strncpy(hex, "81b5bff5b", sizeof(hex));
cl_git_pass(git_oid_fromstrn(&oid, hex, strlen(hex))); cl_git_pass(git_oid_fromstrn(&oid, hex, strlen(hex)));
cl_git_pass(git_odb_read_prefix(&obj, _odb, &oid, strlen(hex))); cl_git_pass(git_odb_read_prefix(&obj, _odb, &oid, strlen(hex)));
cl_git_pass(git_odb_exists_prefix(&found, _odb, &oid, strlen(hex)));
cl_assert(git_oid_equal(&found, git_odb_object_id(obj)));
git_odb_object_free(obj); git_odb_object_free(obj);
strncpy(hex, "81b5bff5f", sizeof(hex)); strncpy(hex, "81b5bff5f", sizeof(hex));
...@@ -80,10 +93,14 @@ void test_odb_mixed__dup_oid_prefix_0(void) { ...@@ -80,10 +93,14 @@ void test_odb_mixed__dup_oid_prefix_0(void) {
cl_git_pass(git_oid_fromstrn(&oid, hex, strlen(hex))); cl_git_pass(git_oid_fromstrn(&oid, hex, strlen(hex)));
cl_assert_equal_i( cl_assert_equal_i(
GIT_EAMBIGUOUS, git_odb_read_prefix(&obj, _odb, &oid, strlen(hex))); GIT_EAMBIGUOUS, git_odb_read_prefix(&obj, _odb, &oid, strlen(hex)));
cl_assert_equal_i(
GIT_EAMBIGUOUS, git_odb_exists_prefix(&found, _odb, &oid, strlen(hex)));
strncpy(hex, "0ddeaded9", sizeof(hex)); strncpy(hex, "0ddeaded9", sizeof(hex));
cl_git_pass(git_oid_fromstrn(&oid, hex, strlen(hex))); cl_git_pass(git_oid_fromstrn(&oid, hex, strlen(hex)));
cl_git_pass(git_odb_read_prefix(&obj, _odb, &oid, strlen(hex))); cl_git_pass(git_odb_read_prefix(&obj, _odb, &oid, strlen(hex)));
cl_git_pass(git_odb_exists_prefix(&found, _odb, &oid, strlen(hex)));
cl_assert(git_oid_equal(&found, git_odb_object_id(obj)));
git_odb_object_free(obj); git_odb_object_free(obj);
strncpy(hex, "0ddeadede", sizeof(hex)); strncpy(hex, "0ddeadede", sizeof(hex));
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment