Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 47cb42d

Browse files
committed
commit: split creating the commit and writing it out
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.
1 parent 785d8c4 commit 47cb42d

File tree

4 files changed

+216
-47
lines changed

4 files changed

+216
-47
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ v0.24 + 1
55

66
### API additions
77

8+
* `git_commit_create_buffer()` creates a commit and writes it into a
9+
user-provided buffer instead of writing it into the object db.
10+
811
### API removals
912

1013
### Breaking API changes

include/git2/commit.h

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,52 @@ GIT_EXTERN(int) git_commit_amend(
394394
const char *message,
395395
const git_tree *tree);
396396

397+
/**
398+
* Create a commit and write it into a buffer
399+
*
400+
* Create a commit as with `git_commit_create()` but instead of
401+
* writing it to the objectdb, write the contents of the object into a
402+
* buffer.
403+
*
404+
* @param out the buffer into which to write the commit object content
405+
*
406+
* @param repo Repository where the referenced tree and parents live
407+
*
408+
* @param author Signature with author and author time of commit
409+
*
410+
* @param committer Signature with committer and * commit time of commit
411+
*
412+
* @param message_encoding The encoding for the message in the
413+
* commit, represented with a standard encoding name.
414+
* E.g. "UTF-8". If NULL, no encoding header is written and
415+
* UTF-8 is assumed.
416+
*
417+
* @param message Full message for this commit
418+
*
419+
* @param tree An instance of a `git_tree` object that will
420+
* be used as the tree for the commit. This tree object must
421+
* also be owned by the given `repo`.
422+
*
423+
* @param parent_count Number of parents for this commit
424+
*
425+
* @param parents Array of `parent_count` pointers to `git_commit`
426+
* objects that will be used as the parents for this commit. This
427+
* array may be NULL if `parent_count` is 0 (root commit). All the
428+
* given commits must be owned by the `repo`.
429+
*
430+
* @return 0 or an error code
431+
*/
432+
GIT_EXTERN(int) git_commit_create_buffer(
433+
git_buf *out,
434+
git_repository *repo,
435+
const git_signature *author,
436+
const git_signature *committer,
437+
const char *message_encoding,
438+
const char *message,
439+
const git_tree *tree,
440+
size_t parent_count,
441+
const git_commit *parents[]);
442+
397443
/** @} */
398444
GIT_END_DECL
399445
#endif

src/commit.c

Lines changed: 128 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "message.h"
1919
#include "refs.h"
2020
#include "object.h"
21+
#include "oidarray.h"
2122

2223
void git_commit__free(void *_commit)
2324
{
@@ -37,94 +38,143 @@ void git_commit__free(void *_commit)
3738
git__free(commit);
3839
}
3940

40-
static int git_commit__create_internal(
41-
git_oid *id,
41+
static int git_commit__create_buffer_internal(
42+
git_buf *out,
4243
git_repository *repo,
43-
const char *update_ref,
4444
const git_signature *author,
4545
const git_signature *committer,
4646
const char *message_encoding,
4747
const char *message,
4848
const git_oid *tree,
49-
git_commit_parent_callback parent_cb,
50-
void *parent_payload,
51-
bool validate)
49+
git_array_oid_t *parents)
5250
{
53-
git_reference *ref = NULL;
54-
int error = 0, matched_parent = 0;
55-
const git_oid *current_id = NULL;
56-
git_buf commit = GIT_BUF_INIT;
5751
size_t i = 0;
58-
git_odb *odb;
5952
const git_oid *parent;
6053

61-
assert(id && repo && tree && parent_cb);
54+
assert(out && repo && tree);
6255

63-
if (validate && !git_object__is_valid(repo, tree, GIT_OBJ_TREE))
64-
return -1;
56+
git_oid__writebuf(out, "tree ", tree);
6557

66-
if (update_ref) {
67-
error = git_reference_lookup_resolved(&ref, repo, update_ref, 10);
68-
if (error < 0 && error != GIT_ENOTFOUND)
69-
return error;
58+
for (i = 0; i < git_array_size(*parents); i++) {
59+
parent = git_array_get(*parents, i);
60+
git_oid__writebuf(out, "parent ", parent);
7061
}
71-
giterr_clear();
7262

73-
if (ref)
74-
current_id = git_reference_target(ref);
63+
git_signature__writebuf(out, "author ", author);
64+
git_signature__writebuf(out, "committer ", committer);
65+
66+
if (message_encoding != NULL)
67+
git_buf_printf(out, "encoding %s\n", message_encoding);
68+
69+
git_buf_putc(out, '\n');
7570

76-
git_oid__writebuf(&commit, "tree ", tree);
71+
if (git_buf_puts(out, message) < 0)
72+
goto on_error;
73+
74+
return 0;
75+
76+
on_error:
77+
git_buf_free(out);
78+
return -1;
79+
}
7780

81+
static int validate_tree_and_parents(git_array_oid_t *parents, git_repository *repo, const git_oid *tree,
82+
git_commit_parent_callback parent_cb, void *parent_payload,
83+
const git_oid *current_id, bool validate)
84+
{
85+
size_t i;
86+
int error;
87+
git_oid *parent_cpy;
88+
const git_oid *parent;
89+
90+
if (validate && !git_object__is_valid(repo, tree, GIT_OBJ_TREE))
91+
return -1;
92+
93+
i = 0;
7894
while ((parent = parent_cb(i, parent_payload)) != NULL) {
7995
if (validate && !git_object__is_valid(repo, parent, GIT_OBJ_COMMIT)) {
8096
error = -1;
8197
goto on_error;
8298
}
8399

84-
git_oid__writebuf(&commit, "parent ", parent);
85-
if (i == 0 && current_id && git_oid_equal(current_id, parent))
86-
matched_parent = 1;
100+
parent_cpy = git_array_alloc(*parents);
101+
GITERR_CHECK_ALLOC(parent_cpy);
102+
103+
git_oid_cpy(parent_cpy, parent);
87104
i++;
88105
}
89106

90-
if (ref && !matched_parent) {
91-
git_reference_free(ref);
92-
git_buf_free(&commit);
107+
if (current_id && git_oid_cmp(current_id, git_array_get(*parents, 0))) {
93108
giterr_set(GITERR_OBJECT, "failed to create commit: current tip is not the first parent");
94-
return GIT_EMODIFIED;
109+
error = GIT_EMODIFIED;
110+
goto on_error;
95111
}
96112

97-
git_signature__writebuf(&commit, "author ", author);
98-
git_signature__writebuf(&commit, "committer ", committer);
113+
return 0;
99114

100-
if (message_encoding != NULL)
101-
git_buf_printf(&commit, "encoding %s\n", message_encoding);
115+
on_error:
116+
git_array_clear(*parents);
117+
return error;
118+
}
102119

103-
git_buf_putc(&commit, '\n');
120+
static int git_commit__create_internal(
121+
git_oid *id,
122+
git_repository *repo,
123+
const char *update_ref,
124+
const git_signature *author,
125+
const git_signature *committer,
126+
const char *message_encoding,
127+
const char *message,
128+
const git_oid *tree,
129+
git_commit_parent_callback parent_cb,
130+
void *parent_payload,
131+
bool validate)
132+
{
133+
int error;
134+
git_odb *odb;
135+
git_reference *ref = NULL;
136+
git_buf buf = GIT_BUF_INIT;
137+
const git_oid *current_id = NULL;
138+
git_array_oid_t parents = GIT_ARRAY_INIT;
104139

105-
if (git_buf_puts(&commit, message) < 0)
106-
goto on_error;
140+
if (update_ref) {
141+
error = git_reference_lookup_resolved(&ref, repo, update_ref, 10);
142+
if (error < 0 && error != GIT_ENOTFOUND)
143+
return error;
144+
}
145+
giterr_clear();
146+
147+
if (ref)
148+
current_id = git_reference_target(ref);
149+
150+
if ((error = validate_tree_and_parents(&parents, repo, tree, parent_cb, parent_payload, current_id, validate)) < 0)
151+
goto cleanup;
152+
153+
error = git_commit__create_buffer_internal(&buf, repo, author, committer,
154+
message_encoding, message, tree,
155+
&parents);
156+
157+
if (error < 0)
158+
goto cleanup;
107159

108160
if (git_repository_odb__weakptr(&odb, repo) < 0)
109-
goto on_error;
161+
goto cleanup;
110162

111-
if (git_odb_write(id, odb, commit.ptr, commit.size, GIT_OBJ_COMMIT) < 0)
112-
goto on_error;
163+
if (git_odb_write(id, odb, buf.ptr, buf.size, GIT_OBJ_COMMIT) < 0)
164+
goto cleanup;
113165

114-
git_buf_free(&commit);
115166

116167
if (update_ref != NULL) {
117168
error = git_reference__update_for_commit(
118169
repo, ref, update_ref, id, "commit");
119-
git_reference_free(ref);
120-
return error;
170+
goto cleanup;
121171
}
122172

123-
return 0;
124-
125-
on_error:
126-
git_buf_free(&commit);
127-
return -1;
173+
cleanup:
174+
git_array_clear(parents);
175+
git_reference_free(ref);
176+
git_buf_free(&buf);
177+
return error;
128178
}
129179

130180
int git_commit_create_from_callback(
@@ -739,3 +789,34 @@ int git_commit_extract_signature(git_buf *signature, git_buf *signed_data, git_r
739789
git_buf_clear(signed_data);
740790
return error;
741791
}
792+
793+
int git_commit_create_buffer(git_buf *out,
794+
git_repository *repo,
795+
const git_signature *author,
796+
const git_signature *committer,
797+
const char *message_encoding,
798+
const char *message,
799+
const git_tree *tree,
800+
size_t parent_count,
801+
const git_commit *parents[])
802+
{
803+
int error;
804+
commit_parent_data data = { parent_count, parents, repo };
805+
git_array_oid_t parents_arr = GIT_ARRAY_INIT;
806+
const git_oid *tree_id;
807+
808+
assert(tree && git_tree_owner(tree) == repo);
809+
810+
tree_id = git_tree_id(tree);
811+
812+
if ((error = validate_tree_and_parents(&parents_arr, repo, tree_id, commit_parent_from_array, &data, NULL, true)) < 0)
813+
return error;
814+
815+
error = git_commit__create_buffer_internal(
816+
out, repo, author, committer,
817+
message_encoding, message, tree_id,
818+
&parents_arr);
819+
820+
git_array_clear(parents_arr);
821+
return error;
822+
}

tests/commit/write.c

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,45 @@ void test_commit_write__from_memory(void)
9898
cl_assert_equal_s(commit_message, git_commit_message(commit));
9999
}
100100

101+
void test_commit_write__into_buf(void)
102+
{
103+
git_oid tree_id;
104+
git_signature *author, *committer;
105+
git_tree *tree;
106+
git_commit *parent;
107+
git_oid parent_id;
108+
git_buf commit = GIT_BUF_INIT;
109+
110+
git_oid_fromstr(&tree_id, tree_id_str);
111+
cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_id));
112+
113+
/* create signatures */
114+
cl_git_pass(git_signature_new(&committer, committer_name, committer_email, 123456789, 60));
115+
cl_git_pass(git_signature_new(&author, committer_name, committer_email, 987654321, 90));
116+
117+
git_oid_fromstr(&parent_id, parent_id_str);
118+
cl_git_pass(git_commit_lookup(&parent, g_repo, &parent_id));
119+
120+
cl_git_pass(git_commit_create_buffer(&commit, g_repo, author, committer,
121+
NULL, root_commit_message, tree, 1, (const git_commit **) &parent));
122+
123+
cl_assert_equal_s(commit.ptr,
124+
"tree 1810dff58d8a660512d4832e740f692884338ccd\n\
125+
parent 8496071c1b46c854b31185ea97743be6a8774479\n\
126+
author Vicent Marti <[email protected]> 987654321 +0130\n\
127+
committer Vicent Marti <[email protected]> 123456789 +0100\n\
128+
\n\
129+
This is a root commit\n\
130+
This is a root commit and should be the only one in this branch\n\
131+
");
132+
133+
git_buf_free(&commit);
134+
git_tree_free(tree);
135+
git_commit_free(parent);
136+
git_signature_free(author);
137+
git_signature_free(committer);
138+
}
139+
101140
// create a root commit
102141
void test_commit_write__root(void)
103142
{

0 commit comments

Comments
 (0)