summaryrefslogtreecommitdiff
path: root/src/odb_loose.c
diff options
context:
space:
mode:
authorMarc Pegon <pegon.marc@gmail.com>2011-05-29 18:00:35 +0200
committerVicent Marti <tanoku@gmail.com>2011-06-01 23:40:42 +0200
commitaea8a638f2593153b397a12d07c0751727e720f1 (patch)
treedf5aa4ac769c6b78cd02e93bbac1894935fe5063 /src/odb_loose.c
parent6c8ca697becd0cc8cc867f34f571b51247b142d7 (diff)
downloadlibgit2-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.c117
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)