summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/commit.c243
-rw-r--r--src/commit.h27
-rw-r--r--src/git/commit.h52
-rw-r--r--src/revwalk.c9
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;
}