diff options
author | Carlos Martín Nieto <cmn@dwim.me> | 2016-03-03 22:56:02 +0100 |
---|---|---|
committer | Carlos Martín Nieto <cmn@dwim.me> | 2016-03-08 13:11:49 +0100 |
commit | 47cb42da5ad2e0af7946faf053c7ea4fd92ec6da (patch) | |
tree | de7df1005f9d9e1082ddd07182e88ea024700762 | |
parent | 785d8c48ea8725691da3c50e7dae8751523d4c30 (diff) | |
download | libgit2-cmn/commit-to-memory.tar.gz |
commit: split creating the commit and writing it outcmn/commit-to-memory
Sometimes you want to create a commit but not write it out to the
objectdb immediately. For these cases, provide a new function to
retrieve the buffer instead of having to go through the db.
-rw-r--r-- | CHANGELOG.md | 3 | ||||
-rw-r--r-- | include/git2/commit.h | 46 | ||||
-rw-r--r-- | src/commit.c | 175 | ||||
-rw-r--r-- | tests/commit/write.c | 39 |
4 files changed, 216 insertions, 47 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 43476b99a..21f972d2e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ v0.24 + 1 ### API additions +* `git_commit_create_buffer()` creates a commit and writes it into a + user-provided buffer instead of writing it into the object db. + ### API removals ### Breaking API changes diff --git a/include/git2/commit.h b/include/git2/commit.h index 3488c7440..44ea8882b 100644 --- a/include/git2/commit.h +++ b/include/git2/commit.h @@ -394,6 +394,52 @@ GIT_EXTERN(int) git_commit_amend( const char *message, const git_tree *tree); +/** + * Create a commit and write it into a buffer + * + * Create a commit as with `git_commit_create()` but instead of + * writing it to the objectdb, write the contents of the object into a + * buffer. + * + * @param out the buffer into which to write the commit object content + * + * @param repo Repository where the referenced tree and parents live + * + * @param author Signature with author and author time of commit + * + * @param committer Signature with committer and * commit time of commit + * + * @param message_encoding The encoding for the message in the + * commit, represented with a standard encoding name. + * E.g. "UTF-8". If NULL, no encoding header is written and + * UTF-8 is assumed. + * + * @param message Full message for this commit + * + * @param tree An instance of a `git_tree` object that will + * be used as the tree for the commit. This tree object must + * also be owned by the given `repo`. + * + * @param parent_count Number of parents for this commit + * + * @param parents Array of `parent_count` pointers to `git_commit` + * objects that will be used as the parents for this commit. This + * array may be NULL if `parent_count` is 0 (root commit). All the + * given commits must be owned by the `repo`. + * + * @return 0 or an error code + */ +GIT_EXTERN(int) git_commit_create_buffer( + git_buf *out, + git_repository *repo, + const git_signature *author, + const git_signature *committer, + const char *message_encoding, + const char *message, + const git_tree *tree, + size_t parent_count, + const git_commit *parents[]); + /** @} */ GIT_END_DECL #endif diff --git a/src/commit.c b/src/commit.c index 685c642aa..9d675ac97 100644 --- a/src/commit.c +++ b/src/commit.c @@ -18,6 +18,7 @@ #include "message.h" #include "refs.h" #include "object.h" +#include "oidarray.h" void git_commit__free(void *_commit) { @@ -37,94 +38,143 @@ void git_commit__free(void *_commit) git__free(commit); } -static int git_commit__create_internal( - git_oid *id, +static int git_commit__create_buffer_internal( + git_buf *out, git_repository *repo, - const char *update_ref, const git_signature *author, const git_signature *committer, const char *message_encoding, const char *message, const git_oid *tree, - git_commit_parent_callback parent_cb, - void *parent_payload, - bool validate) + git_array_oid_t *parents) { - git_reference *ref = NULL; - int error = 0, matched_parent = 0; - const git_oid *current_id = NULL; - git_buf commit = GIT_BUF_INIT; size_t i = 0; - git_odb *odb; const git_oid *parent; - assert(id && repo && tree && parent_cb); + assert(out && repo && tree); - if (validate && !git_object__is_valid(repo, tree, GIT_OBJ_TREE)) - return -1; + git_oid__writebuf(out, "tree ", tree); - if (update_ref) { - error = git_reference_lookup_resolved(&ref, repo, update_ref, 10); - if (error < 0 && error != GIT_ENOTFOUND) - return error; + for (i = 0; i < git_array_size(*parents); i++) { + parent = git_array_get(*parents, i); + git_oid__writebuf(out, "parent ", parent); } - giterr_clear(); - if (ref) - current_id = git_reference_target(ref); + git_signature__writebuf(out, "author ", author); + git_signature__writebuf(out, "committer ", committer); + + if (message_encoding != NULL) + git_buf_printf(out, "encoding %s\n", message_encoding); + + git_buf_putc(out, '\n'); - git_oid__writebuf(&commit, "tree ", tree); + if (git_buf_puts(out, message) < 0) + goto on_error; + + return 0; + +on_error: + git_buf_free(out); + return -1; +} +static int validate_tree_and_parents(git_array_oid_t *parents, git_repository *repo, const git_oid *tree, + git_commit_parent_callback parent_cb, void *parent_payload, + const git_oid *current_id, bool validate) +{ + size_t i; + int error; + git_oid *parent_cpy; + const git_oid *parent; + + if (validate && !git_object__is_valid(repo, tree, GIT_OBJ_TREE)) + return -1; + + i = 0; while ((parent = parent_cb(i, parent_payload)) != NULL) { if (validate && !git_object__is_valid(repo, parent, GIT_OBJ_COMMIT)) { error = -1; goto on_error; } - git_oid__writebuf(&commit, "parent ", parent); - if (i == 0 && current_id && git_oid_equal(current_id, parent)) - matched_parent = 1; + parent_cpy = git_array_alloc(*parents); + GITERR_CHECK_ALLOC(parent_cpy); + + git_oid_cpy(parent_cpy, parent); i++; } - if (ref && !matched_parent) { - git_reference_free(ref); - git_buf_free(&commit); + if (current_id && git_oid_cmp(current_id, git_array_get(*parents, 0))) { giterr_set(GITERR_OBJECT, "failed to create commit: current tip is not the first parent"); - return GIT_EMODIFIED; + error = GIT_EMODIFIED; + goto on_error; } - git_signature__writebuf(&commit, "author ", author); - git_signature__writebuf(&commit, "committer ", committer); + return 0; - if (message_encoding != NULL) - git_buf_printf(&commit, "encoding %s\n", message_encoding); +on_error: + git_array_clear(*parents); + return error; +} - git_buf_putc(&commit, '\n'); +static int git_commit__create_internal( + git_oid *id, + git_repository *repo, + const char *update_ref, + const git_signature *author, + const git_signature *committer, + const char *message_encoding, + const char *message, + const git_oid *tree, + git_commit_parent_callback parent_cb, + void *parent_payload, + bool validate) +{ + int error; + git_odb *odb; + git_reference *ref = NULL; + git_buf buf = GIT_BUF_INIT; + const git_oid *current_id = NULL; + git_array_oid_t parents = GIT_ARRAY_INIT; - if (git_buf_puts(&commit, message) < 0) - goto on_error; + if (update_ref) { + error = git_reference_lookup_resolved(&ref, repo, update_ref, 10); + if (error < 0 && error != GIT_ENOTFOUND) + return error; + } + giterr_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) + goto cleanup; + + error = git_commit__create_buffer_internal(&buf, repo, author, committer, + message_encoding, message, tree, + &parents); + + if (error < 0) + goto cleanup; if (git_repository_odb__weakptr(&odb, repo) < 0) - goto on_error; + goto cleanup; - if (git_odb_write(id, odb, commit.ptr, commit.size, GIT_OBJ_COMMIT) < 0) - goto on_error; + if (git_odb_write(id, odb, buf.ptr, buf.size, GIT_OBJ_COMMIT) < 0) + goto cleanup; - git_buf_free(&commit); if (update_ref != NULL) { error = git_reference__update_for_commit( repo, ref, update_ref, id, "commit"); - git_reference_free(ref); - return error; + goto cleanup; } - return 0; - -on_error: - git_buf_free(&commit); - return -1; +cleanup: + git_array_clear(parents); + git_reference_free(ref); + git_buf_free(&buf); + return error; } int git_commit_create_from_callback( @@ -739,3 +789,34 @@ cleanup: git_buf_clear(signed_data); return error; } + +int git_commit_create_buffer(git_buf *out, + git_repository *repo, + const git_signature *author, + const git_signature *committer, + const char *message_encoding, + const char *message, + const git_tree *tree, + size_t parent_count, + const git_commit *parents[]) +{ + int error; + commit_parent_data data = { parent_count, parents, repo }; + git_array_oid_t parents_arr = GIT_ARRAY_INIT; + const git_oid *tree_id; + + assert(tree && git_tree_owner(tree) == repo); + + tree_id = git_tree_id(tree); + + if ((error = validate_tree_and_parents(&parents_arr, repo, tree_id, commit_parent_from_array, &data, NULL, true)) < 0) + return error; + + error = git_commit__create_buffer_internal( + out, repo, author, committer, + message_encoding, message, tree_id, + &parents_arr); + + git_array_clear(parents_arr); + return error; +} diff --git a/tests/commit/write.c b/tests/commit/write.c index 96b7cc321..9d1ae78fb 100644 --- a/tests/commit/write.c +++ b/tests/commit/write.c @@ -98,6 +98,45 @@ void test_commit_write__from_memory(void) cl_assert_equal_s(commit_message, git_commit_message(commit)); } +void test_commit_write__into_buf(void) +{ + git_oid tree_id; + git_signature *author, *committer; + git_tree *tree; + git_commit *parent; + git_oid parent_id; + git_buf commit = GIT_BUF_INIT; + + git_oid_fromstr(&tree_id, tree_id_str); + cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_id)); + + /* create signatures */ + cl_git_pass(git_signature_new(&committer, committer_name, committer_email, 123456789, 60)); + cl_git_pass(git_signature_new(&author, committer_name, committer_email, 987654321, 90)); + + git_oid_fromstr(&parent_id, parent_id_str); + cl_git_pass(git_commit_lookup(&parent, g_repo, &parent_id)); + + cl_git_pass(git_commit_create_buffer(&commit, g_repo, author, committer, + NULL, root_commit_message, tree, 1, (const git_commit **) &parent)); + + cl_assert_equal_s(commit.ptr, + "tree 1810dff58d8a660512d4832e740f692884338ccd\n\ +parent 8496071c1b46c854b31185ea97743be6a8774479\n\ +author Vicent Marti <vicent@github.com> 987654321 +0130\n\ +committer Vicent Marti <vicent@github.com> 123456789 +0100\n\ +\n\ +This is a root commit\n\ + This is a root commit and should be the only one in this branch\n\ +"); + + git_buf_free(&commit); + git_tree_free(tree); + git_commit_free(parent); + git_signature_free(author); + git_signature_free(committer); +} + // create a root commit void test_commit_write__root(void) { |