summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarlos Martín Nieto <cmn@dwim.me>2014-02-10 23:50:41 +0100
committerCarlos Martín Nieto <cmn@dwim.me>2014-02-10 23:50:41 +0100
commit97645461d8bc74f95080697c1b111894698360c8 (patch)
tree19f8ae3f922c593336e96740436d5416630b62f1
parentb362fbf363f1793fe9b190f57ecb04afd26b09ce (diff)
downloadlibgit2-cmn/safe-commit.tar.gz
commit: add a function to create a commit on top of a refcmn/safe-commit
Add a way to safely create a commit which builds on top of the current commit in a given reference.
-rw-r--r--include/git2/commit.h19
-rw-r--r--src/commit.c72
-rw-r--r--tests/commit/write.c27
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 <stdarg.h>
@@ -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);
+}