Commit 7f8fe1d45e086adc9e7f3f0c33b624eeb3774033

Patrick Steinhardt 2015-12-01T10:03:56

commit: introduce `git_commit_body` It is already possible to get a commit's summary with the `git_commit_summary` function. It is not possible to get the remaining part of the commit message, that is the commit message's body. Fix this by introducing a new function `git_commit_body`.

diff --git a/include/git2/commit.h b/include/git2/commit.h
index 04711c1..34d29ed 100644
--- a/include/git2/commit.h
+++ b/include/git2/commit.h
@@ -128,6 +128,19 @@ GIT_EXTERN(const char *) git_commit_message_raw(const git_commit *commit);
 GIT_EXTERN(const char *) git_commit_summary(git_commit *commit);
 
 /**
+ * Get the long "body" of the git commit message.
+ *
+ * The returned message is the body of the commit, comprising
+ * everything but the first paragraph of the message. Leading and
+ * trailing whitespaces are trimmed.
+ *
+ * @param commit a previously loaded commit.
+ * @return the body of a commit or NULL when no the message only
+ *   consists of a summary
+ */
+GIT_EXTERN(const char *) git_commit_body(git_commit *commit);
+
+/**
  * Get the commit time (i.e. committer time) of a commit.
  *
  * @param commit a previously loaded commit.
diff --git a/src/commit.c b/src/commit.c
index b42f9de..81aae48 100644
--- a/src/commit.c
+++ b/src/commit.c
@@ -31,6 +31,7 @@ void git_commit__free(void *_commit)
 	git__free(commit->raw_message);
 	git__free(commit->message_encoding);
 	git__free(commit->summary);
+	git__free(commit->body);
 
 	git__free(commit);
 }
@@ -472,6 +473,33 @@ const char *git_commit_summary(git_commit *commit)
 	return commit->summary;
 }
 
+const char *git_commit_body(git_commit *commit)
+{
+	const char *msg, *end;
+
+	assert(commit);
+
+	if (!commit->body) {
+		/* search for end of summary */
+		for (msg = git_commit_message(commit); *msg; ++msg)
+			if (msg[0] == '\n' && (!msg[1] || msg[1] == '\n'))
+				break;
+
+		/* trim leading and trailing whitespace */
+		for (; *msg; ++msg)
+			if (!git__isspace(*msg))
+				break;
+		for (end = msg + strlen(msg) - 1; msg <= end; --end)
+			if (!git__isspace(*end))
+				break;
+
+		if (*msg)
+			    commit->body = git__strndup(msg, end - msg + 1);
+	}
+
+	return commit->body;
+}
+
 int git_commit_tree(git_tree **tree_out, const git_commit *commit)
 {
 	assert(commit);
diff --git a/src/commit.h b/src/commit.h
index efb080b..d01ac2b 100644
--- a/src/commit.h
+++ b/src/commit.h
@@ -28,6 +28,7 @@ struct git_commit {
 	char *raw_header;
 
 	char *summary;
+	char *body;
 };
 
 void git_commit__free(void *commit);
diff --git a/tests/commit/commit.c b/tests/commit/commit.c
index 81aaf80..c82971f 100644
--- a/tests/commit/commit.c
+++ b/tests/commit/commit.c
@@ -63,6 +63,18 @@ void assert_commit_summary(const char *expected, const char *given)
 	git_commit__free(dummy);
 }
 
+void assert_commit_body(const char *expected, const char *given)
+{
+	git_commit *dummy;
+
+	cl_assert(dummy = git__calloc(1, sizeof(struct git_commit)));
+
+	dummy->raw_message = git__strdup(given);
+	cl_assert_equal_s(expected, git_commit_body(dummy));
+
+	git_commit_free(dummy);
+}
+
 void test_commit_commit__summary(void)
 {
 	assert_commit_summary("One-liner with no trailing newline", "One-liner with no trailing newline");
@@ -80,8 +92,35 @@ void test_commit_commit__summary(void)
 	assert_commit_summary("  Spaces after newlines are collapsed", "\n  Spaces after newlines\n  are\n  collapsed\n  "); /* newlines at the very beginning are ignored and not collapsed */
 	assert_commit_summary(" Spaces before newlines are collapsed", "  \nSpaces before newlines  \nare  \ncollapsed  \n");
 	assert_commit_summary(" Spaces around newlines are collapsed", "  \n  Spaces around newlines  \n  are  \n  collapsed  \n  ");
+	assert_commit_summary(" Trailing newlines are" , "  \n  Trailing newlines  \n  are  \n\n  collapsed  \n  ");
+	assert_commit_summary(" Trailing spaces are stripped", "  \n  Trailing spaces \n  are stripped \n\n  \n \t ");
 	assert_commit_summary("", "");
 	assert_commit_summary("", " ");
 	assert_commit_summary("", "\n");
 	assert_commit_summary("", "\n \n");
 }
+
+void test_commit_commit__body(void)
+{
+	assert_commit_body(NULL, "One-liner with no trailing newline");
+	assert_commit_body(NULL, "One-liner with trailing newline\n");
+	assert_commit_body(NULL, "\n\nTrimmed leading&trailing newlines\n\n");
+	assert_commit_body("(There are more!)", "\nFirst paragraph only\n\n(There are more!)");
+	assert_commit_body("(Yes, unwrapped!)", "\nFirst paragraph\nwith  unwrapped\ntrailing\tlines\n\n(Yes, unwrapped!)");
+	assert_commit_body("are preserved", "\tLeading\n\ttabs\n\nare preserved"); /* tabs around newlines are collapsed down to a single space */
+	assert_commit_body("are preserved", " Leading\n Spaces\n\nare preserved"); /* spaces around newlines are collapsed down to a single space */
+	assert_commit_body(NULL, "Trailing tabs\tare removed\t\t");
+	assert_commit_body(NULL, "Trailing spaces  are removed  ");
+	assert_commit_body("are removed", "Trailing tabs\t\n\nare removed");
+	assert_commit_body("are removed", "Trailing spaces \n\nare removed");
+	assert_commit_body(NULL,"Newlines\nare\nreplaced by spaces\n");
+	assert_commit_body(NULL , "\n  Spaces after newlines\n  are\n  collapsed\n  "); /* newlines at the very beginning are ignored and not collapsed */
+	assert_commit_body(NULL , "  \nSpaces before newlines  \nare  \ncollapsed  \n");
+	assert_commit_body(NULL , "  \n  Spaces around newlines  \n  are  \n  collapsed  \n  ");
+	assert_commit_body("collapsed" , "  \n  Trailing newlines  \n  are  \n\n  collapsed  \n  ");
+	assert_commit_body(NULL, "  \n  Trailing spaces \n  are stripped \n\n  \n \t ");
+	assert_commit_body(NULL , "");
+	assert_commit_body(NULL , " ");
+	assert_commit_body(NULL , "\n");
+	assert_commit_body(NULL , "\n \n");
+}