Commit f094f9052fba43707cb5662a362511eeea4c4af5

Russell Belfer 2013-07-01T15:41:01

Add raw header access to commit API

diff --git a/include/git2/commit.h b/include/git2/commit.h
index 544d21d..fc0551b 100644
--- a/include/git2/commit.h
+++ b/include/git2/commit.h
@@ -130,6 +130,14 @@ GIT_EXTERN(const git_signature *) git_commit_committer(const git_commit *commit)
 GIT_EXTERN(const git_signature *) git_commit_author(const git_commit *commit);
 
 /**
+ * Get the full raw text of the commit header.
+ *
+ * @param commit a previously loaded commit
+ * @return the header text of the commit
+ */
+GIT_EXTERN(const char *) git_commit_raw_header(const git_commit *commit);
+
+/**
  * Get the tree pointed to by a commit.
  *
  * @param tree_out pointer where to store the tree object
diff --git a/src/commit.c b/src/commit.c
index 1ab9b34..cf50c2d 100644
--- a/src/commit.c
+++ b/src/commit.c
@@ -41,6 +41,7 @@ void git_commit__free(void *_commit)
 	git_signature_free(commit->author);
 	git_signature_free(commit->committer);
 
+	git__free(commit->raw_header);
 	git__free(commit->message);
 	git__free(commit->message_encoding);
 	git__free(commit);
@@ -171,11 +172,33 @@ int git_commit_create(
 int git_commit__parse(void *_commit, git_odb_object *odb_obj)
 {
 	git_commit *commit = _commit;
-	const char *buffer = git_odb_object_data(odb_obj);
-	const char *buffer_end = buffer + git_odb_object_size(odb_obj);
+	const char *buffer_start = git_odb_object_data(odb_obj), *buffer;
+	const char *buffer_end = buffer_start + git_odb_object_size(odb_obj);
 	git_oid parent_id;
+	size_t parent_count = 0, header_len;
 
-	if (git_vector_init(&commit->parent_ids, 4, NULL) < 0)
+	/* find end-of-header (counting parents as we go) */
+	for (buffer = buffer_start; buffer < buffer_end; ++buffer) {
+		if (!strncmp("\n\n", buffer, 2)) {
+			++buffer;
+			break;
+		}
+		if (!strncmp("\nparent ", buffer, strlen("\nparent ")))
+			++parent_count;
+	}
+
+	header_len = buffer - buffer_start;
+	commit->raw_header = git__strndup(buffer_start, header_len);
+	GITERR_CHECK_ALLOC(commit->raw_header);
+
+	/* point "buffer" to header data */
+	buffer = commit->raw_header;
+	buffer_end = commit->raw_header + header_len;
+
+	if (parent_count < 1)
+		parent_count = 1;
+
+	if (git_vector_init(&commit->parent_ids, parent_count, NULL) < 0)
 		return -1;
 
 	if (git_oid__parse(&commit->tree_id, &buffer, buffer_end, "tree ") < 0)
@@ -208,8 +231,8 @@ int git_commit__parse(void *_commit, git_odb_object *odb_obj)
 	if (git_signature__parse(commit->committer, &buffer, buffer_end, "committer ", '\n') < 0)
 		return -1;
 
-	/* Parse add'l header entries until blank line found */
-	while (buffer < buffer_end && *buffer != '\n') {
+	/* Parse add'l header entries */
+	while (buffer < buffer_end) {
 		const char *eoln = buffer;
 		while (eoln < buffer_end && *eoln != '\n')
 			++eoln;
@@ -223,15 +246,18 @@ int git_commit__parse(void *_commit, git_odb_object *odb_obj)
 
 		if (eoln < buffer_end && *eoln == '\n')
 			++eoln;
-
 		buffer = eoln;
 	}
 
-	/* buffer is now at the end of the header, double-check and move forward into the message */
-	if (buffer < buffer_end && *buffer == '\n')
-		buffer++;
+	/* point "buffer" to data after header */
+	buffer = git_odb_object_data(odb_obj);
+	buffer_end = buffer + git_odb_object_size(odb_obj);
+
+	buffer += header_len;
+	if (*buffer == '\n')
+		++buffer;
 
-	/* parse commit message */
+	/* extract commit message */
 	if (buffer <= buffer_end) {
 		commit->message = git__strndup(buffer, buffer_end - buffer);
 		GITERR_CHECK_ALLOC(commit->message);
@@ -255,6 +281,7 @@ GIT_COMMIT_GETTER(const git_signature *, author, commit->author)
 GIT_COMMIT_GETTER(const git_signature *, committer, commit->committer)
 GIT_COMMIT_GETTER(const char *, message, commit->message)
 GIT_COMMIT_GETTER(const char *, message_encoding, commit->message_encoding)
+GIT_COMMIT_GETTER(const char *, raw_header, commit->raw_header)
 GIT_COMMIT_GETTER(git_time_t, time, commit->committer->when.time)
 GIT_COMMIT_GETTER(int, time_offset, commit->committer->when.offset)
 GIT_COMMIT_GETTER(unsigned int, parentcount, (unsigned int)commit->parent_ids.length)
diff --git a/src/commit.h b/src/commit.h
index d0981b1..70d8fc6 100644
--- a/src/commit.h
+++ b/src/commit.h
@@ -25,6 +25,7 @@ struct git_commit {
 
 	char *message_encoding;
 	char *message;
+	char *raw_header;
 };
 
 void git_commit__free(void *commit);