Commit a3f42fe8e4cdae8c85ba5d7d7b4c9fd1247d5227

Carlos Martín Nieto 2015-06-22T15:32:29

commit: allow retrieving an arbitrary header field This allows the user to look up fields which we don't parse in libgit2, and allows them to access gpgsig or mergetag fields if they wish to check the signature.

diff --git a/CHANGELOG.md b/CHANGELOG.md
index a8e3e18..eb7ae84 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -140,6 +140,9 @@ support for HTTPS connections insead of OpenSSL.
 * `git_filter_list_contains` will indicate whether a particular
   filter will be run in the given filter list.
 
+* `git_commit_header_field()` has been added, which allows retrieving
+  the contents of an arbitrary header field.
+
 ### API removals
 
 * `git_remote_save()` and `git_remote_clear_refspecs()` have been
diff --git a/include/git2/commit.h b/include/git2/commit.h
index fb53a70..04711c1 100644
--- a/include/git2/commit.h
+++ b/include/git2/commit.h
@@ -240,6 +240,17 @@ GIT_EXTERN(int) git_commit_nth_gen_ancestor(
 	unsigned int n);
 
 /**
+ * Get an arbitrary header field
+ *
+ * @param out the buffer to fill
+ * @param commit the commit to look in
+ * @param field the header field to return
+ * @return 0 on succeess, GIT_ENOTFOUND if the field does not exist,
+ * or an error code
+ */
+GIT_EXTERN(int) git_commit_header_field(git_buf *out, const git_commit *commit, const char *field);
+
+/**
  * Create new commit in the repository from a list of `git_object` pointers
  *
  * The message will **not** be cleaned up automatically. You can do that
diff --git a/src/commit.c b/src/commit.c
index ce13bdb..616f947 100644
--- a/src/commit.c
+++ b/src/commit.c
@@ -518,3 +518,58 @@ int git_commit_nth_gen_ancestor(
 	*ancestor = parent;
 	return 0;
 }
+
+int git_commit_header_field(git_buf *out, const git_commit *commit, const char *field)
+{
+	const char *buf = commit->raw_header;
+	const char *h, *eol;
+
+	git_buf_sanitize(out);
+	while ((h = strchr(buf, '\n')) && h[1] != '\0' && h[1] != '\n') {
+		h++;
+		if (git__prefixcmp(h, field)) {
+			buf = h;
+			continue;
+		}
+
+		h += strlen(field);
+		eol = strchr(h, '\n');
+		if (h[0] != ' ') {
+			buf = h;
+			continue;
+		}
+		if (!eol)
+			goto malformed;
+
+		h++; /* skip the SP */
+
+		git_buf_put(out, h, eol - h);
+		if (git_buf_oom(out))
+			goto oom;
+
+		/* If the next line starts with SP, it's multi-line, we must continue */
+		while (eol[1] == ' ') {
+			git_buf_putc(out, '\n');
+			h = eol + 2;
+			eol = strchr(h, '\n');
+			if (!eol)
+				goto malformed;
+
+			git_buf_put(out, h, eol - h);
+		}
+
+		if (git_buf_oom(out))
+			goto oom;
+
+		return 0;
+	}
+
+	return GIT_ENOTFOUND;
+
+malformed:
+	giterr_set(GITERR_OBJECT, "malformed header");
+	return -1;
+oom:
+	giterr_set_oom();
+	return -1;
+}
diff --git a/tests/commit/parse.c b/tests/commit/parse.c
index fa079f4..388da07 100644
--- a/tests/commit/parse.c
+++ b/tests/commit/parse.c
@@ -418,3 +418,41 @@ committer Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
 	cl_assert_equal_s(raw_message, git_commit_message_raw(commit));
 	git_commit__free(commit);
 }
+
+void test_commit_parse__arbitrary_field(void)
+{
+	git_commit *commit;
+	git_buf buf = GIT_BUF_INIT;
+	const char *gpgsig = "-----BEGIN PGP SIGNATURE-----\n\
+Version: GnuPG v1.4.12 (Darwin)\n\
+\n\
+iQIcBAABAgAGBQJQ+FMIAAoJEH+LfPdZDSs1e3EQAJMjhqjWF+WkGLHju7pTw2al\n\
+o6IoMAhv0Z/LHlWhzBd9e7JeCnanRt12bAU7yvYp9+Z+z+dbwqLwDoFp8LVuigl8\n\
+JGLcnwiUW3rSvhjdCp9irdb4+bhKUnKUzSdsR2CK4/hC0N2i/HOvMYX+BRsvqweq\n\
+AsAkA6dAWh+gAfedrBUkCTGhlNYoetjdakWqlGL1TiKAefEZrtA1TpPkGn92vbLq\n\
+SphFRUY9hVn1ZBWrT3hEpvAIcZag3rTOiRVT1X1flj8B2vGCEr3RrcwOIZikpdaW\n\
+who/X3xh/DGbI2RbuxmmJpxxP/8dsVchRJJzBwG+yhwU/iN3MlV2c5D69tls/Dok\n\
+6VbyU4lm/ae0y3yR83D9dUlkycOnmmlBAHKIZ9qUts9X7mWJf0+yy2QxJVpjaTGG\n\
+cmnQKKPeNIhGJk2ENnnnzjEve7L7YJQF6itbx5VCOcsGh3Ocb3YR7DMdWjt7f8pu\n\
+c6j+q1rP7EpE2afUN/geSlp5i3x8aXZPDj67jImbVCE/Q1X9voCtyzGJH7MXR0N9\n\
+ZpRF8yzveRfMH8bwAJjSOGAFF5XkcR/RNY95o+J+QcgBLdX48h+ZdNmUf6jqlu3J\n\
+7KmTXXQcOVpN6dD3CmRFsbjq+x6RHwa8u1iGn+oIkX908r97ckfB/kHKH7ZdXIJc\n\
+cpxtDQQMGYFpXK/71stq\n\
+=ozeK\n\
+-----END PGP SIGNATURE-----";
+
+	cl_git_pass(parse_commit(&commit, passing_commit_cases[4]));
+
+	cl_git_pass(git_commit_header_field(&buf, commit, "parent"));
+	cl_assert_equal_s("34734e478d6cf50c27c9d69026d93974d052c454", buf.ptr);
+	git_buf_clear(&buf);
+
+	cl_git_pass(git_commit_header_field(&buf, commit, "gpgsig"));
+	cl_assert_equal_s(gpgsig, buf.ptr);
+
+	cl_git_fail_with(GIT_ENOTFOUND, git_commit_header_field(&buf, commit, "awesomeness"));
+	cl_git_fail_with(GIT_ENOTFOUND, git_commit_header_field(&buf, commit, "par"));
+
+	git_buf_free(&buf);
+	git_commit__free(commit);
+}