diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/commit.c | 243 | ||||
-rw-r--r-- | src/commit.h | 27 | ||||
-rw-r--r-- | src/git/commit.h | 52 | ||||
-rw-r--r-- | src/revwalk.c | 9 |
4 files changed, 283 insertions, 48 deletions
diff --git a/src/commit.c b/src/commit.c index a1a5bf7f5..6b0b66190 100644 --- a/src/commit.c +++ b/src/commit.c @@ -29,14 +29,22 @@ #include "git/odb.h" #define COMMIT_PRINT(commit) {\ - char oid[41]; oid[40] = 0;\ - git_oid_fmt(oid, &commit->object.id);\ - printf("Oid: %s | In degree: %d | Time: %u\n", oid, commit->in_degree, commit->commit_time);\ + char oid[41]; oid[40] = 0;\ + git_oid_fmt(oid, &commit->object.id);\ + printf("Oid: %s | In degree: %d | Time: %u\n", oid, commit->in_degree, commit->commit_time);\ } void git_commit__free(git_commit *commit) { git_commit_list_clear(&commit->parents, 0); + + if (commit->odb_open) + git_obj_close(&commit->odb_object); + + free(commit->author); + free(commit->committer); + free(commit->message); + free(commit->message_short); free(commit); } @@ -69,7 +77,7 @@ git_commit *git_commit_parse(git_revpool *pool, const git_oid *id) if ((commit = git_commit_lookup(pool, id)) == NULL) return NULL; - if (git_commit_parse_existing(commit) < 0) + if (git_commit__parse_basic(commit) < 0) goto error_cleanup; return commit; @@ -79,30 +87,51 @@ error_cleanup: return NULL; } -int git_commit_parse_existing(git_commit *commit) +int git_commit__parse(git_commit *commit, unsigned int parse_flags, int close_db_object) { int error = 0; - git_obj commit_obj; - if (commit->parsed) - return 0; + if (!commit->odb_open) { + error = git_odb_read(&commit->odb_object, commit->object.pool->db, &commit->object.id); + if (error < 0) + return error; - error = git_odb_read(&commit_obj, commit->object.pool->db, &commit->object.id); - if (error < 0) - return error; + if (commit->odb_object.type != GIT_OBJ_COMMIT) { + git_obj_close(&commit->odb_object); + return GIT_EOBJTYPE; + } - if (commit_obj.type != GIT_OBJ_COMMIT) { - error = GIT_EOBJTYPE; - goto cleanup; + commit->odb_open = 1; } - error = git_commit__parse_buffer(commit, commit_obj.data, commit_obj.len); + error = git_commit__parse_buffer(commit, + commit->odb_object.data, commit->odb_object.len, parse_flags); + + if (close_db_object) { + git_obj_close(&commit->odb_object); + commit->odb_open = 0; + } -cleanup: - git_obj_close(&commit_obj); return error; } +int git_commit__parse_basic(git_commit *commit) +{ + int error; + + if (commit->basic_parse) + return 0; + + error = git_commit__parse(commit, + (GIT_COMMIT_TREE | GIT_COMMIT_PARENTS | GIT_COMMIT_TIME), 1); + + if (error < 0) + return error; + + commit->basic_parse = 1; + return 0; +} + git_commit *git_commit_lookup(git_revpool *pool, const git_oid *id) { git_commit *commit = NULL; @@ -131,35 +160,67 @@ git_commit *git_commit_lookup(git_revpool *pool, const git_oid *id) return commit; } -int git_commit__parse_time(time_t *commit_time, char *buffer, const char *buffer_end) +int git_commit__parse_person(git_commit_person *person, char **buffer_out, + const char *buffer_end, const char *header) { - if (memcmp(buffer, "author ", 7) != 0) + const size_t header_len = strlen(header); + + int i; + char *buffer = *buffer_out; + char *line_end, *name, *email; + + line_end = memchr(buffer, '\n', buffer_end - buffer); + if (!line_end) return GIT_EOBJCORRUPTED; - buffer = memchr(buffer, '\n', buffer_end - buffer); - if (!buffer || ++buffer >= buffer_end) + if (buffer + (header_len + 1) > line_end) return GIT_EOBJCORRUPTED; - if (memcmp(buffer, "committer ", 10) != 0) + if (memcmp(buffer, header, header_len) != 0) return GIT_EOBJCORRUPTED; - buffer = memchr(buffer, '>', buffer_end - buffer); - if (!buffer || ++buffer >= buffer_end) + buffer += header_len; + + + /* Parse name field */ + for (i = 0, name = person->name; + i < 64 && buffer < line_end && *buffer != '<'; + ++i) + *name++ = *buffer++; + + *(name - 1) = 0; + + while (buffer < line_end && *buffer != '<') + buffer++; + + if (++buffer >= line_end) return GIT_EOBJCORRUPTED; - *commit_time = strtol(buffer, &buffer, 10); + /* Parse email field */ + for (i = 0, email = person->email; + i < 64 && buffer < line_end && *buffer != '>'; + ++i) + *email++ = *buffer++; + + *email = 0; + + while (buffer < line_end && *buffer != '>') + buffer++; - if (*commit_time == 0) + if (++buffer >= line_end) return GIT_EOBJCORRUPTED; - buffer = memchr(buffer, '\n', buffer_end - buffer); - if (!buffer || ++buffer >= buffer_end) + person->time = strtol(buffer, &buffer, 10); + + if (person->time == 0) return GIT_EOBJCORRUPTED; - return (buffer < buffer_end) ? 0 : -1; + *buffer_out = (line_end + 1); + return 0; } -int git_commit__parse_oid(git_oid *oid, char **buffer_out, const char *buffer_end, const char *header) +int git_commit__parse_oid(git_oid *oid, char **buffer_out, + const char *buffer_end, const char *header) { const size_t sha_len = GIT_OID_HEXSZ; const size_t header_len = strlen(header); @@ -183,28 +244,33 @@ int git_commit__parse_oid(git_oid *oid, char **buffer_out, const char *buffer_en return 0; } -int git_commit__parse_buffer(git_commit *commit, void *data, size_t len) +int git_commit__parse_buffer(git_commit *commit, void *data, size_t len, unsigned int parse_flags) { char *buffer = (char *)data; const char *buffer_end = (char *)data + len; git_oid oid; - - if (commit->parsed) - return 0; + git_commit_person person; if (git_commit__parse_oid(&oid, &buffer, buffer_end, "tree ") < 0) return GIT_EOBJCORRUPTED; - commit->tree = git_tree_lookup(commit->object.pool, &oid); + if (parse_flags & GIT_COMMIT_TREE) + commit->tree = git_tree_lookup(commit->object.pool, &oid); /* * TODO: commit grafts! */ + if (parse_flags & GIT_COMMIT_PARENTS) + git_commit_list_clear(&commit->parents, 0); + while (git_commit__parse_oid(&oid, &buffer, buffer_end, "parent ") == 0) { git_commit *parent; + if ((parse_flags & GIT_COMMIT_PARENTS) == 0) + continue; + if ((parent = git_commit_lookup(commit->object.pool, &oid)) == NULL) return GIT_ENOTFOUND; @@ -216,14 +282,117 @@ int git_commit__parse_buffer(git_commit *commit, void *data, size_t len) return GIT_ENOMEM; } - if (git_commit__parse_time(&commit->commit_time, buffer, buffer_end) < 0) + if (git_commit__parse_person(&person, &buffer, buffer_end, "author ") < 0) return GIT_EOBJCORRUPTED; - commit->parsed = 1; + if (parse_flags & GIT_COMMIT_AUTHOR) { + if (commit->author) + free(commit->author); + + commit->author = git__malloc(sizeof(git_commit_person)); + memcpy(commit->author, &person, sizeof(git_commit_person)); + } + + if (git_commit__parse_person(&person, &buffer, buffer_end, "committer ") < 0) + return GIT_EOBJCORRUPTED; + + if (parse_flags & GIT_COMMIT_TIME) + commit->commit_time = person.time; + + if (parse_flags & GIT_COMMIT_COMMITTER) { + if (commit->committer) + free(commit->committer); + + commit->committer = git__malloc(sizeof(git_commit_person)); + memcpy(commit->committer, &person, sizeof(git_commit_person)); + } + + /* parse commit message */ + while (buffer <= buffer_end && *buffer == '\n') + buffer++; + + if (buffer < buffer_end) + { + if (parse_flags & GIT_COMMIT_MESSAGE) { + size_t message_len = buffer_end - buffer; + + commit->message = git__malloc(message_len + 1); + memcpy(commit->message, buffer, message_len); + commit->message[message_len] = 0; + } + + if (parse_flags & GIT_COMMIT_MESSAGE_SHORT) { + char *line_end; + size_t message_len; + + line_end = memchr(buffer, '\n', buffer_end - buffer); + message_len = line_end - buffer; + + commit->message_short = git__malloc(message_len + 1); + memcpy(commit->message_short, buffer, message_len); + commit->message_short[message_len] = 0; + } + } return 0; } +const git_tree *git_commit_tree(git_commit *commit) +{ + if (commit->tree) + return commit->tree; + + git_commit__parse(commit, GIT_COMMIT_TREE, 0); + return commit->tree; +} + +const git_commit_person *git_commit_author(git_commit *commit) +{ + if (commit->author) + return commit->author; + + git_commit__parse(commit, GIT_COMMIT_AUTHOR, 0); + return commit->author; +} + +const git_commit_person *git_commit_committer(git_commit *commit) +{ + if (commit->committer) + return commit->committer; + + git_commit__parse(commit, GIT_COMMIT_COMMITTER, 0); + return commit->committer; +} + +time_t git_commit_time(git_commit *commit) +{ + if (commit->commit_time) + return commit->commit_time; + + git_commit__parse(commit, GIT_COMMIT_TIME, 0); + return commit->commit_time; +} + +const char *git_commit_message(git_commit *commit) +{ + if (commit->message) + return commit->message; + + git_commit__parse(commit, GIT_COMMIT_MESSAGE, 0); + return commit->message; +} + +const char *git_commit_message_short(git_commit *commit) +{ + if (commit->message_short) + return commit->message_short; + + git_commit__parse(commit, GIT_COMMIT_MESSAGE_SHORT, 0); + return commit->message_short; +} + + + int git_commit_list_push_back(git_commit_list *list, git_commit *commit) { git_commit_node *node = NULL; diff --git a/src/commit.h b/src/commit.h index 524570f2c..36fd65cf2 100644 --- a/src/commit.h +++ b/src/commit.h @@ -23,31 +23,46 @@ struct git_commit_list { typedef struct git_commit_list git_commit_list; typedef struct git_commit_node git_commit_node; +#define GIT_COMMIT_TREE (1 << 1) +#define GIT_COMMIT_PARENTS (1 << 2) +#define GIT_COMMIT_AUTHOR (1 << 3) +#define GIT_COMMIT_COMMITTER (1 << 4) +#define GIT_COMMIT_TIME (1 << 5) +#define GIT_COMMIT_MESSAGE (1 << 6) +#define GIT_COMMIT_MESSAGE_SHORT (1 << 7) +#define GIT_COMMIT_FOOTERS (1 << 8) struct git_commit { git_revpool_object object; + git_obj odb_object; time_t commit_time; git_commit_list parents; git_tree *tree; + git_commit_person *author; + git_commit_person *committer; + + char *message; + char *message_short; unsigned short in_degree; - unsigned parsed:1, + unsigned basic_parse:1, + odb_open:1, seen:1, uninteresting:1, topo_delay:1, - flags:26; + flags:25; }; void git_commit__free(git_commit *c); +int git_commit__parse(git_commit *commit, unsigned int flags, int close_odb); +int git_commit__parse_basic(git_commit *commit); 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); +int git_commit__parse_buffer(git_commit *commit, void *data, size_t len, unsigned int parse_flags); +int git_commit__parse_person(git_commit_person *person, char **buffer_out, const char *buffer_end, const char *header); void git_commit__mark_uninteresting(git_commit *commit); -int git_commit_parse_existing(git_commit *commit); - int git_commit_list_push_back(git_commit_list *list, git_commit *commit); int git_commit_list_push_front(git_commit_list *list, git_commit *commit); diff --git a/src/git/commit.h b/src/git/commit.h index 0ccf3c246..6d34f01ce 100644 --- a/src/git/commit.h +++ b/src/git/commit.h @@ -3,6 +3,7 @@ #include "common.h" #include "oid.h" +#include "tree.h" /** * @file git/commit.h @@ -16,6 +17,13 @@ GIT_BEGIN_DECL /** Parsed representation of a commit object. */ typedef struct git_commit git_commit; +/** Parsed representation of an author/committer of a commit */ +typedef struct git_commit_person { + char name[64]; /**< Full name */ + char email[64]; /**< Email address */ + time_t time; /**< Time when this person commited the change */ +} git_commit_person; + /** * Locate a reference to a commit without loading it. * The generated commit object is owned by the revision @@ -45,11 +53,53 @@ GIT_EXTERN(git_commit *) git_commit_parse(git_revpool *pool, const git_oid *id); /** * Get the id of a commit. - * @param commit a previously parsed commit. + * @param commit a previously loaded commit. * @return object identity for the commit. */ GIT_EXTERN(const git_oid *) git_commit_id(git_commit *commit); +/** + * Get the short (one line) message of a commit. + * @param commit a previously loaded commit. + * @return the short message of a commit + */ +GIT_EXTERN(const char *) git_commit_message_short(git_commit *commit); + +/** + * Get the full message of a commit. + * @param commit a previously loaded commit. + * @return the message of a commit + */ +GIT_EXTERN(const char *) git_commit_message(git_commit *commit); + +/** + * Get the commit time (i.e. committer time) of a commit. + * @param commit a previously loaded commit. + * @return the time of a commit + */ +GIT_EXTERN(time_t) git_commit_time(git_commit *commit); + +/** + * Get the committer of a commit. + * @param commit a previously loaded commit. + * @return the committer of a commit + */ +GIT_EXTERN(const git_commit_person *) git_commit_committer(git_commit *commit); + +/** + * Get the author of a commit. + * @param commit a previously loaded commit. + * @return the author of a commit + */ +GIT_EXTERN(const git_commit_person *) git_commit_author(git_commit *commit); + +/** + * Get the tree pointed to by a commit. + * @param commit a previously loaded commit. + * @return the tree of a commit + */ +GIT_EXTERN(const git_tree *) git_commit_tree(git_commit *commit); + /** @} */ GIT_END_DECL #endif diff --git a/src/revwalk.c b/src/revwalk.c index 422ef9df8..d4627ae2e 100644 --- a/src/revwalk.c +++ b/src/revwalk.c @@ -92,8 +92,9 @@ int gitrp_push(git_revpool *pool, git_commit *commit) if (commit->object.pool != pool || pool->walking) return GIT_ERROR; - if (!commit->parsed) { - int error = git_commit_parse_existing(commit); + if (!commit->basic_parse) { + int error = git_commit__parse_basic(commit); + if (error < 0) return error; } @@ -129,8 +130,8 @@ int gitrp__enroot(git_revpool *pool, git_commit *commit) if (commit->seen) return 0; - if (commit->parsed == 0) { - error = git_commit_parse_existing(commit); + if (!commit->basic_parse) { + error = git_commit__parse_basic(commit); if (error < 0) return error; } |