mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-06 15:29:22 +00:00
Merge pull request #3652 from libgit2/cmn/commit-to-memory
commit: split creating the commit and writing it out
This commit is contained in:
commit
534ca8882d
@ -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
|
||||
|
@ -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
|
||||
|
183
src/commit.c
183
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,6 +38,85 @@ void git_commit__free(void *_commit)
|
||||
git__free(commit);
|
||||
}
|
||||
|
||||
static int git_commit__create_buffer_internal(
|
||||
git_buf *out,
|
||||
git_repository *repo,
|
||||
const git_signature *author,
|
||||
const git_signature *committer,
|
||||
const char *message_encoding,
|
||||
const char *message,
|
||||
const git_oid *tree,
|
||||
git_array_oid_t *parents)
|
||||
{
|
||||
size_t i = 0;
|
||||
const git_oid *parent;
|
||||
|
||||
assert(out && repo && tree);
|
||||
|
||||
git_oid__writebuf(out, "tree ", tree);
|
||||
|
||||
for (i = 0; i < git_array_size(*parents); i++) {
|
||||
parent = git_array_get(*parents, i);
|
||||
git_oid__writebuf(out, "parent ", parent);
|
||||
}
|
||||
|
||||
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');
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
parent_cpy = git_array_alloc(*parents);
|
||||
GITERR_CHECK_ALLOC(parent_cpy);
|
||||
|
||||
git_oid_cpy(parent_cpy, parent);
|
||||
i++;
|
||||
}
|
||||
|
||||
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");
|
||||
error = GIT_EMODIFIED;
|
||||
goto on_error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
on_error:
|
||||
git_array_clear(*parents);
|
||||
return error;
|
||||
}
|
||||
|
||||
static int git_commit__create_internal(
|
||||
git_oid *id,
|
||||
git_repository *repo,
|
||||
@ -50,18 +130,12 @@ static int git_commit__create_internal(
|
||||
void *parent_payload,
|
||||
bool validate)
|
||||
{
|
||||
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;
|
||||
int error;
|
||||
git_odb *odb;
|
||||
const git_oid *parent;
|
||||
|
||||
assert(id && repo && tree && parent_cb);
|
||||
|
||||
if (validate && !git_object__is_valid(repo, tree, GIT_OBJ_TREE))
|
||||
return -1;
|
||||
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 (update_ref) {
|
||||
error = git_reference_lookup_resolved(&ref, repo, update_ref, 10);
|
||||
@ -73,58 +147,34 @@ static int git_commit__create_internal(
|
||||
if (ref)
|
||||
current_id = git_reference_target(ref);
|
||||
|
||||
git_oid__writebuf(&commit, "tree ", tree);
|
||||
if ((error = validate_tree_and_parents(&parents, repo, tree, parent_cb, parent_payload, current_id, validate)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
while ((parent = parent_cb(i, parent_payload)) != NULL) {
|
||||
if (validate && !git_object__is_valid(repo, parent, GIT_OBJ_COMMIT)) {
|
||||
error = -1;
|
||||
goto on_error;
|
||||
}
|
||||
error = git_commit__create_buffer_internal(&buf, repo, author, committer,
|
||||
message_encoding, message, tree,
|
||||
&parents);
|
||||
|
||||
git_oid__writebuf(&commit, "parent ", parent);
|
||||
if (i == 0 && current_id && git_oid_equal(current_id, parent))
|
||||
matched_parent = 1;
|
||||
i++;
|
||||
}
|
||||
|
||||
if (ref && !matched_parent) {
|
||||
git_reference_free(ref);
|
||||
git_buf_free(&commit);
|
||||
giterr_set(GITERR_OBJECT, "failed to create commit: current tip is not the first parent");
|
||||
return GIT_EMODIFIED;
|
||||
}
|
||||
|
||||
git_signature__writebuf(&commit, "author ", author);
|
||||
git_signature__writebuf(&commit, "committer ", committer);
|
||||
|
||||
if (message_encoding != NULL)
|
||||
git_buf_printf(&commit, "encoding %s\n", message_encoding);
|
||||
|
||||
git_buf_putc(&commit, '\n');
|
||||
|
||||
if (git_buf_puts(&commit, message) < 0)
|
||||
goto on_error;
|
||||
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;
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user