diff --git a/include/git2/commit.h b/include/git2/commit.h index 5d47dd191..060e0085b 100644 --- a/include/git2/commit.h +++ b/include/git2/commit.h @@ -294,7 +294,8 @@ GIT_EXTERN(int) git_commit_nth_gen_ancestor( * @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); +GIT_EXTERN(int) git_commit_header_field( + git_buf *out, const git_commit *commit, const char *field); /** * Extract the signature from a commit @@ -369,6 +370,42 @@ typedef struct { const char *value; } git_commit_header; +/** An accessor object for setting commit signature data. */ +typedef struct git_commitbuilder git_commitbuilder; + +/** + * Add a header on a commit being created. + * + * @param builder in-progress commitbuilder object + * @param field the header field to add + * @param value the header value to add + * @return 0 on success or an error code + */ +GIT_EXTERN(int) git_commitbuilder_add_header( + git_commitbuilder *builder, + const char *field, + const char *value); + +/** + * Commit signing callback: used when a function is going to write + * a commit (for example, in `git_commit_create_ext`) to allow callers + * to sign the commit. + * + * @param builder commit builder object to populate with the signature + * @param repo the repository being committed in + * @param commit_content the raw contents of the commit (to be signed) + * @param payload the caller-specified callback payload + * @return 0 if this callback has created a signature and populated the + * field and signature buffers, `GIT_PASSTHROUGH` if the callback + * does not want to sign the commit, any other value to stop and + * return a failure + */ +typedef int GIT_CALLBACK(git_commit_signature_cb)( + git_commitbuilder *builder, + git_repository *repo, + const char *commit_content, + void *payload); + typedef struct { unsigned int version; @@ -392,6 +429,12 @@ typedef struct { */ const git_commit_header *extra_headers; size_t extra_headers_len; /**< Number of extra headers */ + + /** Signing callback; leave NULL for no commit signing. */ + git_commit_signature_cb sign; + + /** Callback payload (optional) */ + void *payload; } git_commit_create_ext_options; /** Current version for the `git_commit_create_ext_options` structure */ diff --git a/src/libgit2/commit.c b/src/libgit2/commit.c index 0e40af458..3516af884 100644 --- a/src/libgit2/commit.c +++ b/src/libgit2/commit.c @@ -161,6 +161,10 @@ on_error: return error; } +struct git_commitbuilder { + git_str *contents; +}; + static int git_commit__create_internal( git_oid *id, git_repository *repo, @@ -173,12 +177,12 @@ static int git_commit__create_internal( const git_commit_create_ext_options *opts, bool validate) { - int error; git_odb *odb; git_reference *ref = NULL; git_str buf = GIT_STR_INIT; const git_oid *current_id = NULL; git_array_oid_t parents = GIT_ARRAY_INIT; + int error = 0; if (opts && opts->update_ref) { error = git_reference_lookup_resolved(&ref, @@ -187,27 +191,31 @@ static int git_commit__create_internal( if (error < 0 && error != GIT_ENOTFOUND) return error; } + git_error_clear(); if (ref) current_id = git_reference_target(ref); - if ((error = validate_tree_and_parents(&parents, repo, tree, parent_cb, parent_payload, current_id, validate)) < 0) + if ((error = validate_tree_and_parents(&parents, repo, tree, + parent_cb, parent_payload, current_id, validate)) < 0) goto cleanup; - error = git_commit__create_buffer_internal(&buf, - author, committer, message, tree, &parents, opts); - - if (error < 0) + if ((error = git_commit__create_buffer_internal(&buf, + author, committer, message, tree, &parents, opts)) < 0) goto cleanup; - if (git_repository_odb__weakptr(&odb, repo) < 0) - goto cleanup; + if (opts && opts->sign) { + git_commitbuilder builder = { &buf }; - if (git_odb__freshen(odb, tree) < 0) - goto cleanup; + if ((error = opts->sign(&builder, repo, buf.ptr, + opts->payload)) < 0) + goto cleanup; + } - if (git_odb_write(id, odb, buf.ptr, buf.size, GIT_OBJECT_COMMIT) < 0) + if ((error = git_repository_odb__weakptr(&odb, repo)) < 0 || + (error = git_odb__freshen(odb, tree)) < 0 || + (error = git_odb_write(id, odb, buf.ptr, buf.size, GIT_OBJECT_COMMIT)) < 0) goto cleanup; @@ -367,6 +375,57 @@ int git_commit_create_ext( commit_parent_from_array, &data, opts, false); } +static int append_header( + git_str *out, + const char *raw_content, + const char *name, + const char *value) +{ + const char *header_end; + + /* Identifying the end of the commit header area */ + header_end = strstr(raw_content, "\n\n"); + + if (!header_end) { + git_error_set(GIT_ERROR_INVALID, "malformed commit contents"); + return -1; + } + + /* The header ends after the first LF */ + header_end++; + + git_str_put(out, raw_content, header_end - raw_content); + + if (format_header_field(out, name, value) < 0) + return -1; + + git_str_puts(out, header_end); + + if (git_str_oom(out)) + return -1; + + return 0; +} + +int git_commitbuilder_add_header( + git_commitbuilder *builder, + const char *field, + const char *value) +{ + git_str signed_data = GIT_BUF_INIT; + int error; + + if ((error = append_header(&signed_data, builder->contents->ptr, + field, value)) < 0) + goto done; + + git_str_swap(builder->contents, &signed_data); + +done: + git_str_dispose(&signed_data); + return error; +} + int git_commit_create( git_oid *id, git_repository *repo, @@ -1079,20 +1138,29 @@ int git_commit_create_with_signature( const char *signature_field) { git_odb *odb; - int error = 0; - const char *field; - const char *header_end; - git_str commit = GIT_STR_INIT; + git_str signed_content = GIT_STR_INIT; git_commit *parsed; git_array_oid_t parents = GIT_ARRAY_INIT; git_commit__parse_options parse_opts = {0}; + size_t commit_content_len; + int error = 0; + + GIT_ASSERT_ARG(out); + GIT_ASSERT_ARG(repo); + GIT_ASSERT_ARG(commit_content); + + if (!signature_field) + signature_field = "gpgsig"; + + commit_content_len = strlen(commit_content);; parse_opts.oid_type = repo->oid_type; /* The first step is to verify that all the tree and parents exist */ parsed = git__calloc(1, sizeof(git_commit)); GIT_ERROR_CHECK_ALLOC(parsed); - if (commit_parse(parsed, commit_content, strlen(commit_content), &parse_opts) < 0) { + + if (commit_parse(parsed, commit_content, commit_content_len, &parse_opts) < 0) { error = -1; goto cleanup; } @@ -1102,39 +1170,25 @@ int git_commit_create_with_signature( git_array_clear(parents); - /* Then we start appending by identifying the end of the commit header */ - header_end = strstr(commit_content, "\n\n"); - if (!header_end) { - git_error_set(GIT_ERROR_INVALID, "malformed commit contents"); - error = -1; - goto cleanup; - } - - /* The header ends after the first LF */ - header_end++; - git_str_put(&commit, commit_content, header_end - commit_content); - - if (signature != NULL) { - field = signature_field ? signature_field : "gpgsig"; - - if ((error = format_header_field(&commit, field, signature)) < 0) + if (signature) { + if ((error = append_header(&signed_content, + commit_content, signature_field, signature)) < 0) goto cleanup; + + commit_content = signed_content.ptr; + commit_content_len = signed_content.size; } - git_str_puts(&commit, header_end); - - if (git_str_oom(&commit)) - return -1; - if ((error = git_repository_odb__weakptr(&odb, repo)) < 0) goto cleanup; - if ((error = git_odb_write(out, odb, commit.ptr, commit.size, GIT_OBJECT_COMMIT)) < 0) + if ((error = git_odb_write(out, odb, commit_content, + commit_content_len, GIT_OBJECT_COMMIT)) < 0) goto cleanup; cleanup: git_commit__free(parsed); - git_str_dispose(&commit); + git_str_dispose(&signed_content); return error; } diff --git a/tests/libgit2/commit/write.c b/tests/libgit2/commit/write.c index 52a2bb9d2..87e2d9987 100644 --- a/tests/libgit2/commit/write.c +++ b/tests/libgit2/commit/write.c @@ -299,130 +299,6 @@ void test_commit_write__can_validate_objects(void) cl_git_fail(create_commit_from_ids(&commit_id, &tree_id, &parent_id)); } -void test_commit_write__attach_signature_checks_objects(void) -{ - const char *sig = "magic word: pretty please"; - const char *badtree = "tree 6b79e22d69bf46e289df0345a14ca059dfc9bdf6\n\ -parent 34734e478d6cf50c27c9d69026d93974d052c454\n\ -author Ben Burkert 1358451456 -0800\n\ -committer Ben Burkert 1358451456 -0800\n\ -\n\ -a simple commit which does not work\n"; - - const char *badparent = "tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904\n\ -parent 34734e478d6cf50c27c9d69026d93974d052c454\n\ -author Ben Burkert 1358451456 -0800\n\ -committer Ben Burkert 1358451456 -0800\n\ -\n\ -a simple commit which does not work\n"; - - git_oid id; - - cl_git_fail_with(-1, git_commit_create_with_signature(&id, g_repo, badtree, sig, "magicsig")); - cl_git_fail_with(-1, git_commit_create_with_signature(&id, g_repo, badparent, sig, "magicsig")); - -} - -void test_commit_write__attach_singleline_signature(void) -{ - const char *sig = "magic word: pretty please"; - - const char *data = "tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904\n\ -parent 8496071c1b46c854b31185ea97743be6a8774479\n\ -author Ben Burkert 1358451456 -0800\n\ -committer Ben Burkert 1358451456 -0800\n\ -\n\ -a simple commit which works\n"; - - const char *complete = "tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904\n\ -parent 8496071c1b46c854b31185ea97743be6a8774479\n\ -author Ben Burkert 1358451456 -0800\n\ -committer Ben Burkert 1358451456 -0800\n\ -magicsig magic word: pretty please\n\ -\n\ -a simple commit which works\n"; - - git_oid id; - git_odb *odb; - git_odb_object *obj; - - cl_git_pass(git_commit_create_with_signature(&id, g_repo, data, sig, "magicsig")); - - cl_git_pass(git_repository_odb(&odb, g_repo)); - cl_git_pass(git_odb_read(&obj, odb, &id)); - cl_assert_equal_s(complete, git_odb_object_data(obj)); - - git_odb_object_free(obj); - git_odb_free(odb); -} - -void test_commit_write__attach_multiline_signature(void) -{ - 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-----"; - - const char *data = "tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904\n\ -parent 8496071c1b46c854b31185ea97743be6a8774479\n\ -author Ben Burkert 1358451456 -0800\n\ -committer Ben Burkert 1358451456 -0800\n\ -\n\ -a simple commit which works\n"; - -const char *complete = "tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904\n\ -parent 8496071c1b46c854b31185ea97743be6a8774479\n\ -author Ben Burkert 1358451456 -0800\n\ -committer Ben Burkert 1358451456 -0800\n\ -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-----\n\ -\n\ -a simple commit which works\n"; - - git_oid one, two; - git_odb *odb; - git_odb_object *obj; - - cl_git_pass(git_commit_create_with_signature(&one, g_repo, data, gpgsig, "gpgsig")); - cl_git_pass(git_commit_create_with_signature(&two, g_repo, data, gpgsig, NULL)); - - cl_assert(!git_oid_cmp(&one, &two)); - cl_git_pass(git_repository_odb(&odb, g_repo)); - cl_git_pass(git_odb_read(&obj, odb, &one)); - cl_assert_equal_s(complete, git_odb_object_data(obj)); - - git_odb_object_free(obj); - git_odb_free(odb); -} - void test_commit_write__can_add_extra_headers(void) { git_odb *odb; @@ -479,3 +355,169 @@ This is a fun new commit."; git_signature_free(author); git_odb_free(odb); } + +static const char *unsigned_data = +"tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904\n\ +parent 8496071c1b46c854b31185ea97743be6a8774479\n\ +author Ben Burkert 1358451456 -0800\n\ +committer Ben Burkert 1358451456 -0800\n\ +\n\ +a simple commit which works\n"; + +static 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-----"; + +static const char *signed_data = +"tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904\n\ +parent 8496071c1b46c854b31185ea97743be6a8774479\n\ +author Ben Burkert 1358451456 -0800\n\ +committer Ben Burkert 1358451456 -0800\n\ +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-----\n\ +\n\ +a simple commit which works\n"; + +void test_commit_write__attach_signature_checks_objects(void) +{ + const char *sig = "magic word: pretty please"; + const char *badtree = "tree 6b79e22d69bf46e289df0345a14ca059dfc9bdf6\n\ +parent 34734e478d6cf50c27c9d69026d93974d052c454\n\ +author Ben Burkert 1358451456 -0800\n\ +committer Ben Burkert 1358451456 -0800\n\ +\n\ +a simple commit which does not work\n"; + + const char *badparent = "tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904\n\ +parent 34734e478d6cf50c27c9d69026d93974d052c454\n\ +author Ben Burkert 1358451456 -0800\n\ +committer Ben Burkert 1358451456 -0800\n\ +\n\ +a simple commit which does not work\n"; + + git_oid id; + + cl_git_fail_with(-1, git_commit_create_with_signature(&id, g_repo, badtree, sig, "magicsig")); + cl_git_fail_with(-1, git_commit_create_with_signature(&id, g_repo, badparent, sig, "magicsig")); + +} + +void test_commit_write__attach_singleline_signature(void) +{ + const char *sig = "magic word: pretty please"; + + const char *complete = "tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904\n\ +parent 8496071c1b46c854b31185ea97743be6a8774479\n\ +author Ben Burkert 1358451456 -0800\n\ +committer Ben Burkert 1358451456 -0800\n\ +magicsig magic word: pretty please\n\ +\n\ +a simple commit which works\n"; + + git_oid id; + git_odb *odb; + git_odb_object *obj; + + cl_git_pass(git_commit_create_with_signature(&id, g_repo, unsigned_data, sig, "magicsig")); + + cl_git_pass(git_repository_odb(&odb, g_repo)); + cl_git_pass(git_odb_read(&obj, odb, &id)); + cl_assert_equal_s(complete, git_odb_object_data(obj)); + + git_odb_object_free(obj); + git_odb_free(odb); +} + +void test_commit_write__attach_multiline_signature(void) +{ + git_oid one, two; + git_odb *odb; + git_odb_object *obj; + + cl_git_pass(git_commit_create_with_signature(&one, g_repo, unsigned_data, gpgsig, "gpgsig")); + cl_git_pass(git_commit_create_with_signature(&two, g_repo, unsigned_data, gpgsig, NULL)); + + cl_assert(!git_oid_cmp(&one, &two)); + cl_git_pass(git_repository_odb(&odb, g_repo)); + cl_git_pass(git_odb_read(&obj, odb, &one)); + cl_assert_equal_s(signed_data, git_odb_object_data(obj)); + + git_odb_object_free(obj); + git_odb_free(odb); +} + +static int sign_cb( + git_commitbuilder *builder, git_repository *repo, + const char *commit_buffer, void *payload) +{ + GIT_UNUSED(repo); + GIT_UNUSED(commit_buffer); + GIT_UNUSED(payload); + + return git_commitbuilder_add_header(builder, + "gpgsig", gpgsig); +} + +void test_commit_write__signature_callback(void) +{ + git_oid result_id, tree_id, parent_id; + git_odb *odb; + git_odb_object *obj; + git_signature *committer; + git_tree *tree; + git_commit *parent; + git_commit_create_ext_options opts = GIT_COMMIT_CREATE_EXT_OPTIONS_INIT; + + cl_git_pass(git_oid_from_string(&tree_id, "4b825dc642cb6eb9a060e54bf8d69288fbee4904", GIT_OID_SHA1)); + cl_git_pass(git_oid_from_string(&parent_id, "8496071c1b46c854b31185ea97743be6a8774479", GIT_OID_SHA1)); + + cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_id)); + cl_git_pass(git_commit_lookup(&parent, g_repo, &parent_id)); + cl_git_pass(git_signature_new(&committer, "Ben Burkert", "ben@benburkert.com", 1358451456, -480)); + + opts.sign = sign_cb; + + cl_git_pass(git_commit_create_ext(&result_id, g_repo, committer, + committer, "a simple commit which works\n", tree, + 1, &parent, &opts)); + + cl_git_pass(git_repository_odb(&odb, g_repo)); + cl_git_pass(git_odb_read(&obj, odb, &result_id)); + cl_assert_equal_s(signed_data, git_odb_object_data(obj)); + + git_tree_free(tree); + git_commit_free(parent); + git_signature_free(committer); + git_odb_object_free(obj); + git_odb_free(odb); +}