commit: add headers and signature to create_from

The `create_from` and `amend_from` APIs use a slightly different options
structure. They should both allow extra headers and signature callbacks.
This commit is contained in:
Edward Thomson
2026-05-25 16:29:09 +01:00
parent 05c4b5cf95
commit f8823e4e11
2 changed files with 88 additions and 61 deletions

View File

@@ -318,52 +318,6 @@ GIT_EXTERN(int) git_commit_header_field(
GIT_EXTERN(int) git_commit_extract_signature(git_buf *signature, git_buf *signed_data, git_repository *repo, git_oid *commit_id, const char *field);
typedef struct {
unsigned int version;
/**
* Flags for creating the commit.
*
* If `allow_empty_commit` is specified, a commit with no changes
* from the prior commit (an "empty" commit) is allowed. Otherwise,
* commit creation will be stopped.
*/
unsigned int allow_empty_commit : 1;
/** The commit author, or NULL for the default. */
const git_signature *author;
/** The committer, or NULL for the default. */
const git_signature *committer;
/** Encoding for the commit message; leave NULL for default. */
const char *message_encoding;
} git_commit_create_options;
/** Current version for the `git_commit_create_options` structure */
#define GIT_COMMIT_CREATE_OPTIONS_VERSION 1
/** Static constructor for `git_commit_create_options` */
#define GIT_COMMIT_CREATE_OPTIONS_INIT { GIT_COMMIT_CREATE_OPTIONS_VERSION }
/**
* Commits the staged changes in the repository; this is a near analog to
* `git commit -m message`.
*
* By default, empty commits are not allowed.
*
* @param id pointer to store the new commit's object id
* @param repo repository to commit changes in
* @param message the commit message
* @param opts options for creating the commit
* @return 0 on success, GIT_EUNCHANGED if there were no changes to commit, or an error code
*/
GIT_EXTERN(int) git_commit_create_from_stage(
git_oid *id,
git_repository *repo,
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;
@@ -406,6 +360,65 @@ typedef int GIT_CALLBACK(git_commit_signature_cb)(
const char *commit_content,
void *payload);
typedef struct {
unsigned int version;
/**
* Flags for creating the commit.
*
* If `allow_empty_commit` is specified, a commit with no changes
* from the prior commit (an "empty" commit) is allowed. Otherwise,
* commit creation will be stopped.
*/
unsigned int allow_empty_commit : 1;
/** The commit author, or NULL for the default. */
const git_signature *author;
/** The committer, or NULL for the default. */
const git_signature *committer;
/** 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 */
/** Signing callback; leave NULL for no commit signing. */
git_commit_signature_cb sign;
/** Callback payload (optional) */
void *payload;
} git_commit_create_options;
/** Current version for the `git_commit_create_options` structure */
#define GIT_COMMIT_CREATE_OPTIONS_VERSION 1
/** Static constructor for `git_commit_create_options` */
#define GIT_COMMIT_CREATE_OPTIONS_INIT { GIT_COMMIT_CREATE_OPTIONS_VERSION }
/**
* Commits the staged changes in the repository; this is a near analog to
* `git commit -m message`.
*
* By default, empty commits are not allowed.
*
* @param id pointer to store the new commit's object id
* @param repo repository to commit changes in
* @param message the commit message
* @param opts options for creating the commit
* @return 0 on success, GIT_EUNCHANGED if there were no changes to commit, or an error code
*/
GIT_EXTERN(int) git_commit_create_from_stage(
git_oid *id,
git_repository *repo,
const char *message,
const git_commit_create_options *opts);
typedef struct {
unsigned int version;

View File

@@ -1230,6 +1230,20 @@ done:
return error;
}
static void init_ext_options(
git_commit_create_ext_options *ext_opts,
const git_commit_create_options *create_opts)
{
if (!create_opts)
return;
ext_opts->message_encoding = create_opts->message_encoding;
ext_opts->extra_headers = create_opts->extra_headers;
ext_opts->extra_headers_len = create_opts->extra_headers_len;
ext_opts->sign = create_opts->sign;
ext_opts->payload = create_opts->payload;
}
static int create_from_tree(
git_oid *out,
git_repository *repo,
@@ -1240,10 +1254,14 @@ static int create_from_tree(
git_signature *default_signature = NULL;
const git_signature *author, *committer;
git_commitarray parents = { 0 };
git_commit_create_ext_options ext_opts = GIT_COMMIT_CREATE_EXT_OPTIONS_INIT;
int error = -1;
author = opts->author;
committer = opts->committer;
init_ext_options(&ext_opts, opts);
ext_opts.update_ref = "HEAD";
author = opts ? opts->author : NULL;
committer = opts ? opts->committer : NULL;
if (!author || !committer) {
if (git_signature_default(&default_signature, repo) < 0)
@@ -1259,10 +1277,9 @@ static int create_from_tree(
if (git_repository_commit_parents(&parents, repo) < 0)
goto done;
error = git_commit_create(out, repo, "HEAD", author, committer,
opts->message_encoding, message,
tree, parents.count,
(const git_commit **)parents.commits);
error = git_commit_create_ext(out, repo, author, committer,
message, tree, parents.count,
parents.commits, &ext_opts);
done:
git_commitarray_dispose(&parents);
@@ -1363,9 +1380,8 @@ int git_commit_amend_from_tree(
git_repository *repo,
const git_tree *tree,
const char *given_message,
const git_commit_create_options *given_opts)
const git_commit_create_options *opts)
{
git_commit_create_options opts = GIT_COMMIT_CREATE_OPTIONS_INIT;
git_commit_create_ext_options ext_opts = GIT_COMMIT_CREATE_EXT_OPTIONS_INIT;
git_reference *head_ref = NULL;
git_commit *head_commit = NULL;
@@ -1376,21 +1392,20 @@ int git_commit_amend_from_tree(
GIT_ASSERT_ARG(out && repo && tree);
if (given_opts)
memcpy(&opts, given_opts, sizeof(git_commit_create_options));
init_ext_options(&ext_opts, opts);
if ((error = git_repository_head(&head_ref, repo)) < 0 ||
(error = git_reference_peel((git_object **)&head_commit, head_ref, GIT_OBJECT_COMMIT)) < 0)
goto done;
if (opts.author) {
author = opts.author;
if (opts && opts->author) {
author = opts->author;
} else {
author = git_commit_author(head_commit);
}
if (opts.committer) {
committer = opts.committer;
if (opts && opts->committer) {
committer = opts->committer;
} else {
if ((error = git_signature_default(&new_committer, repo)) < 0)
goto done;
@@ -1400,7 +1415,6 @@ int git_commit_amend_from_tree(
if (given_message) {
message = given_message;
ext_opts.message_encoding = opts.message_encoding;
} else {
message = git_commit_message(head_commit);
ext_opts.message_encoding = git_commit_message_encoding(head_commit);