diff --git a/include/git2/odb.h b/include/git2/odb.h index ef16e1e42..d0c369055 100644 --- a/include/git2/odb.h +++ b/include/git2/odb.h @@ -281,6 +281,19 @@ GIT_EXTERN(int) git_odb_open_rstream(git_odb_stream **stream, git_odb *db, const */ GIT_EXTERN(int) git_odb_hash(git_oid *id, const void *data, size_t len, git_otype type); +/** + * Read a file from disk and fill a git_oid with the object id + * that the file would have if it were written to the Object + * Database as an object of the given type. Similar functionality + * to git.git's `git hash-object` without the `-w` flag. + * + * @param out oid structure the result is written into. + * @param path file to read and determine object id for + * @param type the type of the object that will be hashed + * @return GIT_SUCCESS if valid; error code otherwise + */ +GIT_EXTERN(int) git_odb_hashfile(git_oid *out, const char *path, git_otype type); + /** * Close an ODB object * diff --git a/include/git2/status.h b/include/git2/status.h index b8d473447..7946cc1f3 100644 --- a/include/git2/status.h +++ b/include/git2/status.h @@ -51,18 +51,6 @@ GIT_BEGIN_DECL // TODO Ignored files not handled yet #define GIT_STATUS_IGNORED (1 << 6) -/** - * Read a file from disk and fill a git_oid with the object id - * that the file would have if it were written to the Object - * Database as a loose blob. Similar functionality to git.git's - * `git hash-object` without the `-w` flag. - * - * @param out oid structure the result is written into. - * @param path file to read and determine object id for - * @return GIT_SUCCESS if valid; error code otherwise - */ -GIT_EXTERN(int) git_status_hashfile(git_oid *out, const char *path); - /** * Gather file statuses and run a callback for each one. * diff --git a/src/odb.c b/src/odb.c index caa4e1bef..52546e7c7 100644 --- a/src/odb.c +++ b/src/odb.c @@ -46,10 +46,10 @@ typedef struct int is_alternate; } backend_internal; -static int format_object_header(char *hdr, size_t n, git_rawobj *obj) +static int format_object_header(char *hdr, size_t n, size_t obj_len, git_otype obj_type) { - const char *type_str = git_object_type2string(obj->type); - int len = snprintf(hdr, n, "%s %"PRIuZ, type_str, obj->len); + const char *type_str = git_object_type2string(obj_type); + int len = snprintf(hdr, n, "%s %"PRIuZ, type_str, obj_len); assert(len > 0); /* otherwise snprintf() is broken */ assert(((size_t) len) < n); /* otherwise the caller is broken! */ @@ -72,7 +72,7 @@ int git_odb__hash_obj(git_oid *id, char *hdr, size_t n, int *len, git_rawobj *ob if (!obj->data && obj->len != 0) return git__throw(GIT_ERROR, "Failed to hash object. No data given"); - if ((hdrlen = format_object_header(hdr, n, obj)) < 0) + if ((hdrlen = format_object_header(hdr, n, obj->len, obj->type)) < 0) return git__rethrow(hdrlen, "Failed to hash object"); *len = hdrlen; @@ -134,6 +134,52 @@ void git_odb_object_close(git_odb_object *object) git_cached_obj_decref((git_cached_obj *)object, &free_odb_object); } +int git_odb_hashfile(git_oid *out, const char *path, git_otype type) +{ + int fd, hdr_len; + char hdr[64], buffer[2048]; + git_off_t size; + git_hash_ctx *ctx; + + if ((fd = p_open(path, O_RDONLY)) < 0) + return git__throw(GIT_ENOTFOUND, "Could not open '%s'", path); + + if ((size = git_futils_filesize(fd)) < 0 || !git__is_sizet(size)) { + p_close(fd); + return git__throw(GIT_EOSERR, "'%s' appears to be corrupted", path); + } + + hdr_len = format_object_header(hdr, sizeof(hdr), size, type); + if (hdr_len < 0) + return git__throw(GIT_ERROR, "Failed to format blob header. Length is out of bounds"); + + ctx = git_hash_new_ctx(); + + git_hash_update(ctx, hdr, hdr_len); + + while (size > 0) { + ssize_t read_len; + + read_len = read(fd, buffer, sizeof(buffer)); + + if (read_len < 0) { + p_close(fd); + git_hash_free_ctx(ctx); + return git__throw(GIT_EOSERR, "Can't read full file '%s'", path); + } + + git_hash_update(ctx, buffer, read_len); + size -= read_len; + } + + p_close(fd); + + git_hash_final(out, ctx); + git_hash_free_ctx(ctx); + + return GIT_SUCCESS; +} + int git_odb_hash(git_oid *id, const void *data, size_t len, git_otype type) { char hdr[64]; diff --git a/src/status.c b/src/status.c index 9f826e0bc..e25e100f5 100644 --- a/src/status.c +++ b/src/status.c @@ -31,54 +31,6 @@ #include "tree.h" #include "git2/status.h" -int git_status_hashfile(git_oid *out, const char *path) -{ - int fd, len; - char hdr[64], buffer[2048]; - git_off_t size; - git_hash_ctx *ctx; - - if ((fd = p_open(path, O_RDONLY)) < 0) - return git__throw(GIT_ENOTFOUND, "Could not open '%s'", path); - - if ((size = git_futils_filesize(fd)) < 0 || !git__is_sizet(size)) { - p_close(fd); - return git__throw(GIT_EOSERR, "'%s' appears to be corrupted", path); - } - - ctx = git_hash_new_ctx(); - - len = snprintf(hdr, sizeof(hdr), "blob %"PRIuZ, (size_t)size); - assert(len > 0); - assert(((size_t) len) < sizeof(hdr)); - if (len < 0 || ((size_t) len) >= sizeof(hdr)) - return git__throw(GIT_ERROR, "Failed to format blob header. Length is out of bounds"); - - git_hash_update(ctx, hdr, len+1); - - while (size > 0) { - ssize_t read_len; - - read_len = read(fd, buffer, sizeof(buffer)); - - if (read_len < 0) { - p_close(fd); - git_hash_free_ctx(ctx); - return git__throw(GIT_EOSERR, "Can't read full file '%s'", path); - } - - git_hash_update(ctx, buffer, read_len); - size -= read_len; - } - - p_close(fd); - - git_hash_final(out, ctx); - git_hash_free_ctx(ctx); - - return GIT_SUCCESS; -} - struct status_entry { char path[GIT_PATH_MAX]; @@ -181,13 +133,21 @@ static void recurse_tree_entry(git_tree *tree, struct status_entry *e, const cha git_tree_close(tree); } -static int workdir_path_len; +struct status_st { + union { + git_vector *vector; + struct status_entry *e; + } entry; + + int workdir_path_len; +}; + static int dirent_cb(void *state, char *full_path) { int idx; struct status_entry *e; - git_vector *entries = (git_vector *)state; - char *file_path = full_path + workdir_path_len; + struct status_st *st = (struct status_st *)state; + char *file_path = full_path + st->workdir_path_len; struct stat filest; git_oid oid; @@ -197,8 +157,8 @@ static int dirent_cb(void *state, char *full_path) if (git_futils_isdir(full_path) == GIT_SUCCESS) return git_futils_direach(full_path, GIT_PATH_MAX, dirent_cb, state); - if ((idx = find_status_entry(entries, file_path)) != GIT_ENOTFOUND) { - e = (struct status_entry *)git_vector_get(entries, idx); + if ((idx = find_status_entry(st->entry.vector, file_path)) != GIT_ENOTFOUND) { + e = (struct status_entry *)git_vector_get(st->entry.vector, idx); if (p_stat(full_path, &filest) < 0) return git__throw(GIT_EOSERR, "Failed to read file %s", full_path); @@ -208,10 +168,10 @@ static int dirent_cb(void *state, char *full_path) return 0; } } else { - e = new_status_entry(entries, file_path); + e = new_status_entry(st->entry.vector, file_path); } - git_status_hashfile(&oid, full_path); + git_odb_hashfile(&oid, full_path, GIT_OBJ_BLOB); git_oid_cpy(&e->wt_oid, &oid); return 0; @@ -219,8 +179,10 @@ static int dirent_cb(void *state, char *full_path) static int single_dirent_cb(void *state, char *full_path) { - struct status_entry *e = *(struct status_entry **)(state); - char *file_path = full_path + workdir_path_len; + struct status_st *st = (struct status_st *)state; + struct status_entry *e = st->entry.e; + + char *file_path = full_path + st->workdir_path_len; struct stat filest; git_oid oid; @@ -239,7 +201,7 @@ static int single_dirent_cb(void *state, char *full_path) return 1; } - git_status_hashfile(&oid, full_path); + git_odb_hashfile(&oid, full_path, GIT_OBJ_BLOB); git_oid_cpy(&e->wt_oid, &oid); return 1; } @@ -289,6 +251,7 @@ int git_status_foreach(git_repository *repo, int (*callback)(const char *, unsig git_oid zero; int error; git_tree *tree; + struct status_st dirent_st; git_reference *head_ref, *resolved_head_ref; git_commit *head_commit; @@ -314,9 +277,10 @@ int git_status_foreach(git_repository *repo, int (*callback)(const char *, unsig git_commit_tree(&tree, head_commit); recurse_tree_entries(tree, &entries, ""); - workdir_path_len = strlen(repo->path_workdir); + dirent_st.workdir_path_len = strlen(repo->path_workdir); + dirent_st.entry.vector = &entries; strcpy(temp_path, repo->path_workdir); - git_futils_direach(temp_path, GIT_PATH_MAX, dirent_cb, &entries); + git_futils_direach(temp_path, GIT_PATH_MAX, dirent_cb, &dirent_st); memset(&zero, 0x0, sizeof(git_oid)); for (i = 0; i < entries.length; ++i) { @@ -352,6 +316,7 @@ int git_status_file(unsigned int *status_flags, git_repository *repo, const char git_tree *tree; git_reference *head_ref, *resolved_head_ref; git_commit *head_commit; + struct status_st dirent_st; assert(status_flags); @@ -376,9 +341,10 @@ int git_status_file(unsigned int *status_flags, git_repository *repo, const char recurse_tree_entry(tree, e, path); // Find file in Workdir - workdir_path_len = strlen(repo->path_workdir); + dirent_st.workdir_path_len = strlen(repo->path_workdir); + dirent_st.entry.e = e; strcpy(temp_path, repo->path_workdir); - git_futils_direach(temp_path, GIT_PATH_MAX, single_dirent_cb, &e); + git_futils_direach(temp_path, GIT_PATH_MAX, single_dirent_cb, &dirent_st); if ((error = set_status_flags(e)) < GIT_SUCCESS) return git__throw(error, "Nonexistent file"); diff --git a/tests/t18-status.c b/tests/t18-status.c index a20d97b8f..3fdcce9c9 100644 --- a/tests/t18-status.c +++ b/tests/t18-status.c @@ -68,7 +68,7 @@ BEGIN_TEST(file0, "test retrieving OID from a file apart from the ODB") must_pass(git_futils_exists(temp_path)); git_oid_fromstr(&expected_id, test_blob_oid); - must_pass(git_status_hashfile(&actual_id, temp_path)); + must_pass(git_odb_hashfile(&actual_id, temp_path, GIT_OBJ_BLOB)); must_be_true(git_oid_cmp(&expected_id, &actual_id) == 0);