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 key = {{0}}, 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); } } /* 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 = (backend_internal *) git_vector_get(&db->backends, i); git_odb_backend *b = internal->backend; if (!b->exists_prefix) continue; error = b->exists_prefix(&found, b, &key, 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", &key); if (out) git_oid_cpy(out, &last_found); return 0; }
static int pack_entry_find_prefix( struct git_pack_entry *e, struct pack_backend *backend, const git_oid *short_oid, size_t len) { int error; size_t i; git_oid found_full_oid = {{0}}; bool found = false; struct git_pack_file *last_found = backend->last_found; if (last_found) { error = git_pack_entry_find(e, last_found, short_oid, len); if (error == GIT_EAMBIGUOUS) return error; if (!error) { git_oid_cpy(&found_full_oid, &e->sha1); found = true; } } for (i = 0; i < backend->packs.length; ++i) { struct git_pack_file *p; p = (git_pack_file*) git_vector_get(&backend->packs, i); if (p == last_found) continue; error = git_pack_entry_find(e, p, short_oid, len); if (error == GIT_EAMBIGUOUS) return error; if (!error) { if (found && git_oid_cmp(&e->sha1, &found_full_oid)) return git_odb__error_ambiguous("found multiple pack entries"); git_oid_cpy(&found_full_oid, &e->sha1); found = true; backend->last_found = p; } } if (!found) return git_odb__error_notfound("no matching pack entry for prefix", short_oid); else return 0; }
static int pack_backend__read_prefix_internal( git_oid *out_oid, void **buffer_p, size_t *len_p, git_otype *type_p, git_odb_backend *backend, const git_oid *short_oid, size_t len) { int error = 0; if (len < GIT_OID_MINPREFIXLEN) error = git_odb__error_ambiguous("prefix length too short"); else if (len >= GIT_OID_HEXSZ) { /* We can fall back to regular read method */ error = pack_backend__read(buffer_p, len_p, type_p, backend, short_oid); if (!error) git_oid_cpy(out_oid, short_oid); } else { struct git_pack_entry e; git_rawobj raw; if ((error = pack_entry_find_prefix( &e, (struct pack_backend *)backend, short_oid, len)) == 0 && (error = git_packfile_unpack(&raw, e.p, &e.offset)) == 0) { *buffer_p = raw.data; *len_p = raw.len; *type_p = raw.type; git_oid_cpy(out_oid, &e.sha1); } } return error; }
/* Locate an object matching a given short oid */ static int locate_object_short_oid( git_buf *object_location, git_oid *res_oid, loose_backend *backend, const git_oid *short_oid, size_t len) { char *objects_dir = backend->objects_dir; size_t dir_len = strlen(objects_dir), alloc_len; loose_locate_object_state state; int error; /* prealloc memory for OBJ_DIR/xx/xx..38x..xx */ GITERR_CHECK_ALLOC_ADD(&alloc_len, dir_len, GIT_OID_HEXSZ); GITERR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 3); if (git_buf_grow(object_location, alloc_len) < 0) return -1; git_buf_set(object_location, objects_dir, dir_len); git_path_to_dir(object_location); /* save adjusted position at end of dir so it can be restored later */ dir_len = git_buf_len(object_location); /* Convert raw oid to hex formatted oid */ git_oid_fmt((char *)state.short_oid, short_oid); /* Explore OBJ_DIR/xx/ where xx is the beginning of hex formatted short oid */ if (git_buf_put(object_location, (char *)state.short_oid, 3) < 0) return -1; object_location->ptr[object_location->size - 1] = '/'; /* Check that directory exists */ if (git_path_isdir(object_location->ptr) == false) return git_odb__error_notfound("no matching loose object for prefix", short_oid, len); state.dir_len = git_buf_len(object_location); state.short_oid_len = len; state.found = 0; /* Explore directory to find a unique object matching short_oid */ error = git_path_direach( object_location, 0, fn_locate_object_short_oid, &state); if (error < 0 && error != GIT_EAMBIGUOUS) return error; if (!state.found) return git_odb__error_notfound("no matching loose object for prefix", short_oid, len); if (state.found > 1) return git_odb__error_ambiguous("multiple matches in loose objects"); /* Convert obtained hex formatted oid to raw */ error = git_oid_fromstr(res_oid, (char *)state.res_oid); if (error) return error; /* Update the location according to the oid obtained */ GITERR_CHECK_ALLOC_ADD(&alloc_len, dir_len, GIT_OID_HEXSZ); GITERR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 2); git_buf_truncate(object_location, dir_len); if (git_buf_grow(object_location, alloc_len) < 0) return -1; git_oid_pathfmt(object_location->ptr + dir_len, res_oid); object_location->size += GIT_OID_HEXSZ + 1; object_location->ptr[object_location->size] = '\0'; return 0; }
int git_odb_read_prefix( git_odb_object **out, git_odb *db, const git_oid *short_id, size_t len) { size_t i; int error = GIT_ENOTFOUND; git_oid key = {{0}}, found_full_oid = {{0}}; git_rawobj raw; void *data = NULL; bool found = false; git_odb_object *object; assert(out && db); 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) { *out = git_cache_get_raw(odb_cache(db), short_id); if (*out != NULL) 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 = (backend_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, &key, len); if (error == GIT_ENOTFOUND || error == GIT_PASSTHROUGH) continue; if (error) return error; git__free(data); data = raw.data; if (found && git_oid__cmp(&full_oid, &found_full_oid)) { git__free(raw.data); return git_odb__error_ambiguous("multiple matches for prefix"); } found_full_oid = full_oid; found = true; } } if (!found) return git_odb__error_notfound("no match for prefix", &key); if ((object = odb_object__alloc(&found_full_oid, &raw)) == NULL) return -1; *out = (git_odb_object*) git_cache_store_raw(odb_cache(db), object); return 0; }