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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
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);
+}