diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/object.c | 43 | ||||
-rw-r--r-- | src/odb.c | 74 | ||||
-rw-r--r-- | src/odb_loose.c | 33 | ||||
-rw-r--r-- | src/odb_pack.c | 18 | ||||
-rw-r--r-- | src/path.c | 3 | ||||
-rw-r--r-- | src/refdb_fs.c | 12 | ||||
-rw-r--r-- | src/remote.c | 7 |
7 files changed, 170 insertions, 20 deletions
diff --git a/src/object.c b/src/object.c index 40cae184f..3847a0739 100644 --- a/src/object.c +++ b/src/object.c @@ -397,3 +397,46 @@ cleanup: git_tree_free(tree); return error; } + +int git_object_short_id(git_buf *out, const git_object *obj) +{ + git_repository *repo; + int len = GIT_ABBREV_DEFAULT, error; + git_oid id = {{0}}; + git_odb *odb; + + assert(out && obj); + + git_buf_sanitize(out); + repo = git_object_owner(obj); + + if ((error = git_repository__cvar(&len, repo, GIT_CVAR_ABBREV)) < 0) + return error; + + if ((error = git_repository_odb(&odb, repo)) < 0) + return error; + + while (len < GIT_OID_HEXSZ) { + /* set up short oid */ + memcpy(&id.id, &obj->cached.oid.id, (len + 1) / 2); + if (len & 1) + id.id[len / 2] &= 0xf0; + + error = git_odb_exists_prefix(NULL, odb, &id, len); + if (error != GIT_EAMBIGUOUS) + break; + + giterr_clear(); + len++; + } + + if (!error && !(error = git_buf_grow(out, len + 1))) { + git_oid_tostr(out->ptr, len + 1, &id); + out->size = len; + } + + git_odb_free(odb); + + return error; +} + @@ -635,6 +635,61 @@ int git_odb_exists(git_odb *db, const git_oid *id) 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 error; @@ -745,7 +800,6 @@ int git_odb_read_prefix( if (len < GIT_OID_MINPREFIXLEN) return git_odb__error_ambiguous("prefix length too short"); - if (len > GIT_OID_HEXSZ) len = GIT_OID_HEXSZ; @@ -862,7 +916,7 @@ int git_odb_open_wstream( { size_t i, writes = 0; int error = GIT_ERROR; - git_hash_ctx *ctx; + git_hash_ctx *ctx = NULL; assert(stream && db); @@ -883,22 +937,28 @@ int git_odb_open_wstream( } } - if (error == GIT_PASSTHROUGH) - error = 0; - if (error < 0 && !writes) - error = git_odb__error_unsupported_in_backend("write object"); + if (error < 0) { + if (error == GIT_PASSTHROUGH) + error = 0; + else if (!writes) + error = git_odb__error_unsupported_in_backend("write object"); + + goto done; + } ctx = git__malloc(sizeof(git_hash_ctx)); GITERR_CHECK_ALLOC(ctx); + if ((error = git_hash_ctx_init(ctx)) < 0) + goto done; - git_hash_ctx_init(ctx); hash_header(ctx, size, type); (*stream)->hash_ctx = ctx; (*stream)->declared_size = size; (*stream)->received_bytes = 0; +done: return error; } diff --git a/src/odb_loose.c b/src/odb_loose.c index fd4ffff1e..7b46a6652 100644 --- a/src/odb_loose.c +++ b/src/odb_loose.c @@ -519,11 +519,11 @@ static int locate_object_short_oid( loose_locate_object_state state; int error; - /* prealloc memory for OBJ_DIR/xx/ */ - if (git_buf_grow(object_location, dir_len + 5) < 0) + /* prealloc memory for OBJ_DIR/xx/xx..38x..xx */ + if (git_buf_grow(object_location, dir_len + 3 + GIT_OID_HEXSZ) < 0) return -1; - git_buf_sets(object_location, objects_dir); + 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 */ @@ -533,8 +533,9 @@ static int locate_object_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 */ - 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; + object_location->ptr[object_location->size - 1] = '/'; /* Check that directory exists */ if (git_path_isdir(object_location->ptr) == false) @@ -646,12 +647,9 @@ static int loose_backend__read_prefix( { int error = 0; - assert(len <= GIT_OID_HEXSZ); + assert(len >= GIT_OID_MINPREFIXLEN && len <= GIT_OID_HEXSZ); - if (len < GIT_OID_MINPREFIXLEN) - error = git_odb__error_ambiguous("prefix length too short"); - - else if (len == GIT_OID_HEXSZ) { + if (len == GIT_OID_HEXSZ) { /* We can fall back to regular read method */ error = loose_backend__read(buffer_p, len_p, type_p, backend, short_oid); if (!error) @@ -691,6 +689,22 @@ static int loose_backend__exists(git_odb_backend *backend, const git_oid *oid) 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 && len >= GIT_OID_MINPREFIXLEN); + + error = locate_object_short_oid( + &object_path, out, (loose_backend *)backend, short_id, len); + + git_buf_free(&object_path); + + return error; +} + struct foreach_state { size_t dir_len; git_odb_foreach_cb cb; @@ -939,6 +953,7 @@ int git_odb_backend_loose( backend->parent.read_header = &loose_backend__read_header; backend->parent.writestream = &loose_backend__stream; backend->parent.exists = &loose_backend__exists; + backend->parent.exists_prefix = &loose_backend__exists_prefix; backend->parent.foreach = &loose_backend__foreach; backend->parent.free = &loose_backend__free; diff --git a/src/odb_pack.c b/src/odb_pack.c index 903b00d26..9ab683882 100644 --- a/src/odb_pack.c +++ b/src/odb_pack.c @@ -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; } +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) { int error; @@ -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_header = &pack_backend__read_header; backend->parent.exists = &pack_backend__exists; + backend->parent.exists_prefix = &pack_backend__exists_prefix; backend->parent.refresh = &pack_backend__refresh; backend->parent.foreach = &pack_backend__foreach; backend->parent.writepack = &pack_backend__writepack; diff --git a/src/path.c b/src/path.c index 89409eae4..fa800b74c 100644 --- a/src/path.c +++ b/src/path.c @@ -853,6 +853,9 @@ int git_path_direach( if ((dir = opendir(path->ptr)) == NULL) { giterr_set(GITERR_OS, "Failed to open directory '%s'", path->ptr); + if (errno == ENOENT) + return GIT_ENOTFOUND; + return -1; } diff --git a/src/refdb_fs.c b/src/refdb_fs.c index 43682f40e..3219b0519 100644 --- a/src/refdb_fs.c +++ b/src/refdb_fs.c @@ -272,9 +272,17 @@ static int _dirent_loose_load(void *payload, git_buf *full_path) if (git__suffixcmp(full_path->ptr, ".lock") == 0) return 0; - if (git_path_isdir(full_path->ptr)) - return git_path_direach( + if (git_path_isdir(full_path->ptr)) { + int error = git_path_direach( full_path, backend->direach_flags, _dirent_loose_load, backend); + /* Race with the filesystem, ignore it */ + if (error == GIT_ENOTFOUND) { + giterr_clear(); + return 0; + } + + return error; + } file_path = full_path->ptr + strlen(backend->path); diff --git a/src/remote.c b/src/remote.c index f320f4a52..6f97d56a1 100644 --- a/src/remote.c +++ b/src/remote.c @@ -857,12 +857,15 @@ int git_remote_fetch( if ((error = git_remote_connect(remote, GIT_DIRECTION_FETCH)) != 0) return error; - if ((error = git_remote_download(remote)) != 0) - return error; + error = git_remote_download(remote); /* We don't need to be connected anymore */ git_remote_disconnect(remote); + /* If the download failed, return the error */ + if (error != 0) + return error; + /* Default reflog message */ if (reflog_message) git_buf_sets(&reflog_msg_buf, reflog_message); |