mirror of
https://github.com/libgit2/libgit2.git
synced 2026-06-22 06:26:26 +00:00
commit: support custom user-specified headers
Some clients (eg GitButler) are storing additional information in custom user-specified commit headers. We should make this a first-class concept.
This commit is contained in:
@@ -363,6 +363,11 @@ GIT_EXTERN(int) git_commit_create_from_stage(
|
||||
const char *message,
|
||||
const git_commit_create_options *opts);
|
||||
|
||||
/** The field name and value for a custom commit header entry. */
|
||||
typedef struct {
|
||||
const char *field;
|
||||
const char *value;
|
||||
} git_commit_header;
|
||||
|
||||
typedef struct {
|
||||
unsigned int version;
|
||||
@@ -380,6 +385,13 @@ typedef struct {
|
||||
|
||||
/** Encoding for the commit message; leave NULL for default. */
|
||||
const char *message_encoding;
|
||||
|
||||
/**
|
||||
* Extra headers can be specified as an array of field name and
|
||||
* value pairs.
|
||||
*/
|
||||
const git_commit_header *extra_headers;
|
||||
size_t extra_headers_len; /**< Number of extra headers */
|
||||
} git_commit_create_ext_options;
|
||||
|
||||
/** Current version for the `git_commit_create_ext_options` structure */
|
||||
|
||||
@@ -42,6 +42,32 @@ void git_commit__free(void *_commit)
|
||||
git__free(commit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Append to 'out' properly marking continuations when there's a newline in 'content'
|
||||
*/
|
||||
static int format_header_field(git_str *out, const char *field, const char *content)
|
||||
{
|
||||
const char *lf;
|
||||
|
||||
GIT_ASSERT_ARG(out);
|
||||
GIT_ASSERT_ARG(field);
|
||||
GIT_ASSERT_ARG(content);
|
||||
|
||||
git_str_puts(out, field);
|
||||
git_str_putc(out, ' ');
|
||||
|
||||
while ((lf = strchr(content, '\n')) != NULL) {
|
||||
git_str_put(out, content, lf - content);
|
||||
git_str_puts(out, "\n ");
|
||||
content = lf + 1;
|
||||
}
|
||||
|
||||
git_str_puts(out, content);
|
||||
git_str_putc(out, '\n');
|
||||
|
||||
return git_str_oom(out) ? -1 : 0;
|
||||
}
|
||||
|
||||
static int git_commit__create_buffer_internal(
|
||||
git_str *out,
|
||||
const git_signature *author,
|
||||
@@ -69,8 +95,20 @@ static int git_commit__create_buffer_internal(
|
||||
git_signature__writebuf(out, "author ", author);
|
||||
git_signature__writebuf(out, "committer ", committer);
|
||||
|
||||
if (opts && opts->message_encoding != NULL)
|
||||
git_str_printf(out, "encoding %s\n", opts->message_encoding);
|
||||
if (opts && opts->message_encoding != NULL) {
|
||||
if (format_header_field(out, "encoding",
|
||||
opts->message_encoding) < 0)
|
||||
goto on_error;
|
||||
}
|
||||
|
||||
if (opts && opts->extra_headers_len) {
|
||||
for (i = 0; i < opts->extra_headers_len; i++) {
|
||||
if (format_header_field(out,
|
||||
opts->extra_headers[i].field,
|
||||
opts->extra_headers[i].value) < 0)
|
||||
goto on_error;
|
||||
}
|
||||
}
|
||||
|
||||
git_str_putc(out, '\n');
|
||||
|
||||
@@ -1026,38 +1064,11 @@ int git_commit__create_buffer(
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append to 'out' properly marking continuations when there's a newline in 'content'
|
||||
*/
|
||||
static int format_header_field(git_str *out, const char *field, const char *content)
|
||||
{
|
||||
const char *lf;
|
||||
|
||||
GIT_ASSERT_ARG(out);
|
||||
GIT_ASSERT_ARG(field);
|
||||
GIT_ASSERT_ARG(content);
|
||||
|
||||
git_str_puts(out, field);
|
||||
git_str_putc(out, ' ');
|
||||
|
||||
while ((lf = strchr(content, '\n')) != NULL) {
|
||||
git_str_put(out, content, lf - content);
|
||||
git_str_puts(out, "\n ");
|
||||
content = lf + 1;
|
||||
}
|
||||
|
||||
git_str_puts(out, content);
|
||||
git_str_putc(out, '\n');
|
||||
|
||||
return git_str_oom(out) ? -1 : 0;
|
||||
}
|
||||
|
||||
static const git_oid *commit_parent_from_commit(size_t n, void *payload)
|
||||
{
|
||||
const git_commit *commit = (const git_commit *) payload;
|
||||
|
||||
return git_array_get(commit->parent_ids, n);
|
||||
|
||||
}
|
||||
|
||||
int git_commit_create_with_signature(
|
||||
|
||||
@@ -422,3 +422,60 @@ a simple commit which works\n";
|
||||
git_odb_object_free(obj);
|
||||
git_odb_free(odb);
|
||||
}
|
||||
|
||||
void test_commit_write__can_add_extra_headers(void)
|
||||
{
|
||||
git_odb *odb;
|
||||
git_signature *author, *committer;
|
||||
git_oid tree_id, parent_id, commit_id;
|
||||
git_commit *parent;
|
||||
git_tree *tree;
|
||||
git_odb_object *obj;
|
||||
git_commit_create_ext_options opts = GIT_COMMIT_CREATE_EXT_OPTIONS_INIT;
|
||||
const git_commit_header extra_headers[] = {
|
||||
{ "line_one", "First extra header" },
|
||||
{ "line_two", "Second extra header" }
|
||||
};
|
||||
|
||||
const char *expected = "tree 1810dff58d8a660512d4832e740f692884338ccd\n\
|
||||
parent 8496071c1b46c854b31185ea97743be6a8774479\n\
|
||||
author Vicent Marti <vicent@github.com> 987654321 +0130\n\
|
||||
committer Vicent Marti <vicent@github.com> 123456789 +0100\n\
|
||||
line_one First extra header\n\
|
||||
line_two Second extra header\n\
|
||||
\n\
|
||||
This is a fun new commit.";
|
||||
|
||||
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));
|
||||
|
||||
/* this is a valid tree and parent */
|
||||
git_oid_from_string(&tree_id, tree_id_str, GIT_OID_SHA1);
|
||||
cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_id));
|
||||
|
||||
git_oid_from_string(&parent_id, parent_id_str, GIT_OID_SHA1);
|
||||
cl_git_pass(git_commit_lookup(&parent, g_repo, &parent_id));
|
||||
|
||||
opts.extra_headers = extra_headers;
|
||||
opts.extra_headers_len = 2;
|
||||
|
||||
cl_git_pass(git_commit_create_ext(
|
||||
&commit_id, g_repo, author, committer,
|
||||
"This is a fun new commit.",
|
||||
tree, 1, &parent, &opts));
|
||||
|
||||
cl_git_pass(git_repository_odb(&odb, g_repo));
|
||||
cl_git_pass(git_odb_read(&obj, odb, &commit_id));
|
||||
|
||||
cl_assert_equal_s(expected, git_odb_object_data(obj));
|
||||
|
||||
git_odb_object_free(obj);
|
||||
git_tree_free(tree);
|
||||
git_commit_free(parent);
|
||||
git_commit_free(commit);
|
||||
git_signature_free(committer);
|
||||
git_signature_free(author);
|
||||
git_odb_free(odb);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user