diff --git a/src/commit.c b/src/commit.c index 710a14e92..e589dde54 100644 --- a/src/commit.c +++ b/src/commit.c @@ -23,10 +23,128 @@ * Boston, MA 02110-1301, USA. */ +#include + #include "common.h" #include "commit.h" +#include "revwalk.h" +#include "git/odb.h" const git_oid *git_commit_id(git_commit *c) { return &c->id; } + +git_commit *git_commit_lookup(git_revpool *pool, const git_oid *id) +{ + git_obj commit_obj; + git_commit *commit = NULL; + + /* + * TODO: check if the commit is already cached in the + * revpool instead of loading it from the odb + */ + + if (git_odb_read(&commit_obj, pool->db, id) < 0) + return NULL; + + if (commit_obj.type != GIT_OBJ_COMMIT) + goto error_cleanup; + + commit = git__malloc(sizeof(git_commit)); + memset(commit, 0x0, sizeof(git_commit)); + + git_oid_cpy(&commit->id, id); + commit->pool = pool; + + if (git_commit__parse_buffer(commit, commit_obj.data, commit_obj.len) < 0) + goto error_cleanup; + + return commit; + +error_cleanup: + git_obj_close(&commit_obj); + free(commit); + + return NULL; +} + +int git_commit__parse_time(time_t *commit_time, char *buffer, const char *buffer_end) +{ + if (memcmp(buffer, "author ", 7) != 0) + return -1; + + buffer = memchr(buffer, '\n', buffer_end - buffer); + if (buffer == 0 || buffer >= buffer_end) + return -1; + + if (memcmp(buffer, "committer ", 10) != 0) + return -1; + + buffer = memchr(buffer, '\n', buffer_end - buffer); + if (buffer == 0 || buffer >= buffer_end) + return -1; + + *commit_time = strtol(buffer, &buffer, 10); + + return (buffer < buffer_end) ? 0 : -1; +} + +int git_commit__parse_oid(git_oid *oid, char **buffer_out, const char *buffer_end, const char *header) +{ + size_t sha_len = GIT_OID_HEXSZ; + size_t header_len = strlen(header); + + char *buffer = *buffer_out; + + if (buffer + (header_len + sha_len + 1) > buffer_end) + return -1; + + if (memcmp(buffer, header, header_len) != 0) + return -1; + + if (buffer[header_len + sha_len] != '\n') + return -1; + + if (git_oid_mkstr(oid, buffer + header_len) < 0) + return -1; + + *buffer_out = buffer + (header_len + sha_len + 1); + + return 0; +} + +int git_commit__parse_buffer(git_commit *commit, void *data, size_t len) +{ + char *buffer = (char *)data; + const char *buffer_end = (char *)data + len; + + git_oid oid; + + if (commit->parsed) + return 0; + + if (git_commit__parse_oid(&oid, &buffer, buffer_end, "tree ") < 0) + return -1; + + /* + * TODO: load tree into commit object + * TODO: commit grafts! + */ + + while (git_commit__parse_oid(&oid, &buffer, buffer_end, "parent ") == 0) { + git_commit *parent; + + if ((parent = git_commit_lookup(commit->pool, &oid)) == NULL) + return -1; + + // TODO: push the new commit into the revpool + } + + if (git_commit__parse_time(&commit->commit_time, buffer, buffer_end) < 0) + return -1; + + commit->parsed = 1; + + return 0; +} diff --git a/src/commit.h b/src/commit.h index 05504cd34..1cdb9a4f4 100644 --- a/src/commit.h +++ b/src/commit.h @@ -5,11 +5,20 @@ #include +#define GIT_COMMIT_SEEN (1 << 0) +#define GIT_COMMIT_HIDE (1 << 1) +#define GIT_COMMIT_DELAY (1 << 2) + struct git_commit { git_oid id; time_t commit_time; + git_revpool *pool; unsigned parsed:1, flags:26; }; +int git_commit__parse_oid(git_oid *oid, char **buffer_out, const char *buffer_end, const char *header); +int git_commit__parse_buffer(git_commit *commit, void *data, size_t len); +int git_commit__parse_time(time_t *commit_time, char *buffer, const char *buffer_end); + #endif diff --git a/src/git/commit.h b/src/git/commit.h index 010f258ae..ea59a210d 100644 --- a/src/git/commit.h +++ b/src/git/commit.h @@ -17,7 +17,7 @@ GIT_BEGIN_DECL typedef struct git_commit git_commit; /** - * Parse (or lookup) a commit from a revision pool. + * Lookup a commit from a revision pool, and parse it if needed. * @param pool the pool to use when parsing/caching the commit. * @param id identity of the commit to locate. If the object is * an annotated tag it will be peeled back to the commit. @@ -25,7 +25,7 @@ typedef struct git_commit git_commit; * pool's git_odb, or if the commit is present but is * too malformed to be parsed successfully. */ -GIT_EXTERN(git_commit *) git_commit_parse(git_revpool *pool, const git_oid *id); +GIT_EXTERN(git_commit *) git_commit_lookup(git_revpool *pool, const git_oid *id); /** * Get the id of a commit.