From 97645461d8bc74f95080697c1b111894698360c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 10 Feb 2014 23:50:41 +0100 Subject: commit: add a function to create a commit on top of a ref Add a way to safely create a commit which builds on top of the current commit in a given reference. --- include/git2/commit.h | 19 ++++++++++++++ src/commit.c | 72 ++++++++++++++++++++++++++++++++++++++++++++------- tests/commit/write.c | 27 +++++++++++++++++++ 3 files changed, 109 insertions(+), 9 deletions(-) diff --git a/include/git2/commit.h b/include/git2/commit.h index 834330b5d..78deb6de0 100644 --- a/include/git2/commit.h +++ b/include/git2/commit.h @@ -351,6 +351,25 @@ GIT_EXTERN(int) git_commit_amend( const char *message, const git_tree *tree); +/** + * Create a commit on top of the tip of a given branch + * + * All parameters have the same meaning as in `git_commit_create()` + * with the exception that `update_ref` is required and the new + * commit's single parent will be the value of that ref at the time of + * reading. + */ +int git_commit_append( + 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_tree *tree); + + /** @} */ GIT_END_DECL #endif diff --git a/src/commit.c b/src/commit.c index de50e772e..6eb4f443b 100644 --- a/src/commit.c +++ b/src/commit.c @@ -16,6 +16,7 @@ #include "commit.h" #include "signature.h" #include "message.h" +#include "refs.h" #include @@ -36,6 +37,25 @@ void git_commit__free(void *_commit) git__free(commit); } +static int commit_reflog_message(git_buf *buf, git_repository *repo, const git_oid *id) +{ + git_commit *c; + const char *shortmsg; + + git_buf_clear(buf); + + if (git_commit_lookup(&c, repo, id) < 0) + return -1; + + shortmsg = git_commit_summary(c); + git_buf_printf(buf, "commit%s: %s", + git_commit_parentcount(c) == 0 ? " (initial)" : "", + shortmsg); + git_commit_free(c); + + return 0; +} + int git_commit_create_from_callback( git_oid *id, git_repository *repo, @@ -81,19 +101,11 @@ int git_commit_create_from_callback( if (update_ref != NULL) { int error; - git_commit *c; - const char *shortmsg; git_buf reflog_msg = GIT_BUF_INIT; - if (git_commit_lookup(&c, repo, id) < 0) + if ((error = commit_reflog_message(&reflog_msg, repo, id)) < 0) goto on_error; - shortmsg = git_commit_summary(c); - git_buf_printf(&reflog_msg, "commit%s: %s", - git_commit_parentcount(c) == 0 ? " (initial)" : "", - shortmsg); - git_commit_free(c); - error = git_reference__update_terminal(repo, update_ref, id, committer, git_buf_cstr(&reflog_msg)); @@ -273,6 +285,48 @@ int git_commit_amend( &tree_id, commit_parent_for_amend, (void *)commit_to_amend); } +int git_commit_append( + 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_tree *tree) +{ + git_buf reflog_msg = GIT_BUF_INIT; + git_reference *ref, *ref2; + const git_oid *parent_id = NULL; + git_commit *parent; + int error; + + assert(repo && update_ref && author && committer && message); + + if ((error = git_reference_lookup_resolved(&ref, repo, update_ref, 5)) < 0) + return error; + + parent_id = git_reference_target(ref); + if ((error = git_commit_lookup(&parent, repo, parent_id)) < 0) + goto out; + + error = git_commit_create_v(id, repo, NULL, author, committer, message_encoding, message, tree, 1, parent_id); + if (error < 0) + goto out; + + if ((error = commit_reflog_message(&reflog_msg, repo, parent_id)) < 0) + goto out; + + error = git_reference_set_target(&ref2, ref, id, committer, git_buf_cstr(&reflog_msg)); + git_buf_free(&reflog_msg); + +out: + git_commit_free(parent); + git_reference_free(ref); + git_reference_free(ref2); + return error; +} + int git_commit__parse(void *_commit, git_odb_object *odb_obj) { git_commit *commit = _commit; diff --git a/tests/commit/write.c b/tests/commit/write.c index b1cdf4485..84cc99133 100644 --- a/tests/commit/write.c +++ b/tests/commit/write.c @@ -158,3 +158,30 @@ void test_commit_write__root(void) git_signature_free(committer); git_reflog_free(log); } + +void test_commit_write__append(void) +{ + git_tree *tree; + git_oid tree_id, commit_id; + git_signature *author, *committer; + + git_oid_fromstr(&tree_id, tree_oid); + cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_id)); + + cl_git_pass(git_signature_new(&author, committer_name, committer_email, 987654321, 90)); + cl_git_pass(git_signature_new(&committer, committer_name, committer_email, 123456789, 60)); + + cl_git_pass(git_commit_append( + &commit_id, /* out id */ + g_repo, + "HEAD", + author, + committer, + NULL, + root_commit_message, + tree)); + + git_tree_free(tree); + git_signature_free(committer); + git_signature_free(author); +} -- cgit v1.2.1