diff options
author | Marc Pegon <pegon.marc@gmail.com> | 2011-05-29 18:00:35 +0200 |
---|---|---|
committer | Vicent Marti <tanoku@gmail.com> | 2011-06-01 23:40:42 +0200 |
commit | aea8a638f2593153b397a12d07c0751727e720f1 (patch) | |
tree | df5aa4ac769c6b78cd02e93bbac1894935fe5063 /src/odb_loose.c | |
parent | 6c8ca697becd0cc8cc867f34f571b51247b142d7 (diff) | |
download | libgit2-aea8a638f2593153b397a12d07c0751727e720f1.tar.gz |
Implemented read_unique_short_oid method for loose backend.
Diffstat (limited to 'src/odb_loose.c')
-rw-r--r-- | src/odb_loose.c | 117 |
1 files changed, 115 insertions, 2 deletions
diff --git a/src/odb_loose.c b/src/odb_loose.c index 41b7f0b4d..603a43f22 100644 --- a/src/odb_loose.c +++ b/src/odb_loose.c @@ -54,6 +54,20 @@ typedef struct loose_backend { char *objects_dir; } loose_backend; +/* State structure for exploring directories, + * in order to locate objects matching a short oid. + */ +typedef struct { + size_t dir_len; + unsigned char short_oid[GIT_OID_HEXSZ]; /* hex formatted oid to match */ + unsigned int short_oid_len; + int found; /* number of matching + * objects already found */ + unsigned char res_oid[GIT_OID_HEXSZ]; /* hex formatted oid of + * the object found */ +} loose_locate_object_state; + + /*********************************************************** * @@ -465,6 +479,84 @@ static int locate_object(char *object_location, loose_backend *backend, const gi return gitfo_exists(object_location); } +/* Explore an entry of a directory and see if it matches a short oid */ +int fn_locate_object_short_oid(void *state, char *pathbuf) { + loose_locate_object_state *sstate = (loose_locate_object_state *)state; + + size_t pathbuf_len = strlen(pathbuf); + if (pathbuf_len - sstate->dir_len != GIT_OID_HEXSZ - 2) { + /* Entry cannot be an object. Continue to next entry */ + return GIT_SUCCESS; + } + + if (!gitfo_exists(pathbuf) && gitfo_isdir(pathbuf)) { + /* We are already in the directory matching the 2 first hex characters */ + if (!git_oid_match_hex(sstate->short_oid_len-2, sstate->short_oid+2, pathbuf+sstate->dir_len)) { + if (!sstate->found) { + sstate->res_oid[0] = sstate->short_oid[0]; + sstate->res_oid[1] = sstate->short_oid[1]; + memcpy(sstate->res_oid+2, pathbuf+sstate->dir_len, GIT_OID_HEXSZ-2); + } + sstate->found++; + } + } + if (sstate->found > 1) { + return git__throw(GIT_EAMBIGUOUSOIDPREFIX, "Ambiguous sha1 prefix within loose objects"); + } else { + return GIT_SUCCESS; + } +} + +/* Locate an object matching a given short oid */ +static int locate_object_short_oid(char *object_location, git_oid *res_oid, loose_backend *backend, const git_oid *short_oid, unsigned int len) +{ + char *objects_dir = backend->objects_dir; + size_t dir_len = strlen(objects_dir); + loose_locate_object_state state; + int error; + + if (dir_len+43 > GIT_PATH_MAX) + return git__throw(GIT_ERROR, "Failed to locate object from short oid. Object path too long"); + + strcpy(object_location, objects_dir); + + /* Add a separator if not already there */ + if (object_location[dir_len-1] != '/') + object_location[dir_len++] = '/'; + + /* Convert raw oid to hex formatted oid */ + git_oid_fmt(state.short_oid, short_oid); + /* Explore OBJ_DIR/xx/ where xx is the beginning of hex formatted short oid */ + sprintf(object_location+dir_len, "%.2s/", state.short_oid); + + /* Check that directory exists */ + if (gitfo_exists(object_location) || gitfo_isdir(object_location)) + return git__throw(GIT_ENOTFOUND, "Failed to locate object from short oid. Object not found"); + + state.dir_len = dir_len+3; + state.short_oid_len = len; + state.found = 0; + /* Explore directory to find a unique object matching short_oid */ + error = gitfo_dirent(object_location, GIT_PATH_MAX, fn_locate_object_short_oid, &state); + if (error) { + return git__rethrow(error, "Failed to locate object from short oid"); + } + if (!state.found) { + return git__throw(GIT_ENOTFOUND, "Failed to locate object from short oid. Object not found"); + } + + /* Convert obtained hex formatted oid to raw */ + error = git_oid_mkstr(res_oid, state.res_oid); + if (error) { + return git__rethrow(error, "Failed to locate object from short oid"); + } + + /* Update the location according to the oid obtained */ + git_oid_pathfmt(object_location+dir_len, res_oid); + + return GIT_SUCCESS; +} + @@ -527,15 +619,36 @@ int loose_backend__read(void **buffer_p, size_t *len_p, git_otype *type_p, git_o int loose_backend__read_unique_short_oid(git_oid *out_oid, void **buffer_p, size_t *len_p, git_otype *type_p, git_odb_backend *backend, const git_oid *short_oid, unsigned int len) { + if (len < GIT_OID_MINPREFIXLEN) + return git__throw(GIT_EAMBIGUOUSOIDPREFIX, "Failed to read loose backend. Prefix length is lower than %d.", GIT_OID_MINPREFIXLEN); + if (len >= GIT_OID_HEXSZ) { + /* We can fall back to regular read method */ int error = loose_backend__read(buffer_p, len_p, type_p, backend, short_oid); if (error == GIT_SUCCESS) git_oid_cpy(out_oid, short_oid); return error; - } else if (len < GIT_OID_HEXSZ) { - return git__throw(GIT_ENOTIMPLEMENTED, "Loose backend cannot search objects from short oid"); + } else { + char object_path[GIT_PATH_MAX]; + git_rawobj raw; + int error; + + assert(backend && short_oid); + + if ((error = locate_object_short_oid(object_path, out_oid, (loose_backend *)backend, short_oid, len)) < 0) { + return git__rethrow(error, "Failed to read loose backend"); + } + + if ((error = read_loose(&raw, object_path)) < GIT_SUCCESS) + return git__rethrow(error, "Failed to read loose backend"); + + *buffer_p = raw.data; + *len_p = raw.len; + *type_p = raw.type; } + + return GIT_SUCCESS; } int loose_backend__exists(git_odb_backend *backend, const git_oid *oid) |