mirror of
https://github.com/libgit2/libgit2.git
synced 2026-06-22 06:26:26 +00:00
commit: introduce commit creation options
Introduce `git_commit_create_ext` and an options structure for extensibility. Non-default arguments (message encoding, reference updates) are in the options structure. This simplifies the complex self-service commit creation APIs and allows for future sustainable improvements.
This commit is contained in:
@@ -316,6 +316,115 @@ GIT_EXTERN(int) git_commit_header_field(git_buf *out, const git_commit *commit,
|
|||||||
*/
|
*/
|
||||||
GIT_EXTERN(int) git_commit_extract_signature(git_buf *signature, git_buf *signed_data, git_repository *repo, git_oid *commit_id, const char *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);
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
unsigned int version;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If not NULL, the name of the reference that will be updated
|
||||||
|
* to point to this commit. If the reference is not direct, it
|
||||||
|
* will be resolved to a direct reference. Use `HEAD` to update
|
||||||
|
* the `HEAD` of the current branch and make it point to this
|
||||||
|
* commit. If the reference doesn't exist yet, it will be created.
|
||||||
|
* If it does exist, the first parent must be the tip of this
|
||||||
|
* branch.
|
||||||
|
*/
|
||||||
|
const char *update_ref;
|
||||||
|
|
||||||
|
/** Encoding for the commit message; leave NULL for default. */
|
||||||
|
const char *message_encoding;
|
||||||
|
} git_commit_create_ext_options;
|
||||||
|
|
||||||
|
/** Current version for the `git_commit_create_ext_options` structure */
|
||||||
|
#define GIT_COMMIT_CREATE_EXT_OPTIONS_VERSION 1
|
||||||
|
|
||||||
|
/** Static constructor for `git_commit_create_ext_options` */
|
||||||
|
#define GIT_COMMIT_CREATE_EXT_OPTIONS_INIT { GIT_COMMIT_CREATE_EXT_OPTIONS_VERSION }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new commit object and write it to the object database.
|
||||||
|
*
|
||||||
|
* Note that the message will _not_ be cleaned up automatically. You
|
||||||
|
* may wish to use the `git_message_prettify` API to clean it up.
|
||||||
|
*
|
||||||
|
* This API provides the most control for generating commit objects;
|
||||||
|
* this is useful if (for example) you have a tree object already.
|
||||||
|
* If you have staged changes, and just want to emulate `git commit`
|
||||||
|
* on the command line, then `git_commit_create_from_stage` may be a
|
||||||
|
* simpler option.
|
||||||
|
*
|
||||||
|
* @param id_out pointer to store the new commit's object id
|
||||||
|
* @param repo repository to store the commit in
|
||||||
|
* @param author The name of the author and time of authorship
|
||||||
|
* @param committer The name of the committer and time of commit
|
||||||
|
* @param message Full message for this commit
|
||||||
|
* @param tree The `git_tree` that will be used as the tree for this commit
|
||||||
|
* @param parent_count Number of parents for this commit
|
||||||
|
* @param parents Array pointers to `git_commit` objects that will be used
|
||||||
|
* as parents for this commit.
|
||||||
|
* @param opts Additional options for creating this commit,
|
||||||
|
* or `NULL` for defaults
|
||||||
|
* @return 0 on success or an error code
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(int) git_commit_create_ext(
|
||||||
|
git_oid *id_out,
|
||||||
|
git_repository *repo,
|
||||||
|
const git_signature *author,
|
||||||
|
const git_signature *committer,
|
||||||
|
const char *message,
|
||||||
|
const git_tree *tree,
|
||||||
|
size_t parent_count,
|
||||||
|
git_commit * const parents[],
|
||||||
|
const git_commit_create_ext_options *opts);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create new commit in the repository from a list of `git_object` pointers
|
* Create new commit in the repository from a list of `git_object` pointers
|
||||||
*
|
*
|
||||||
@@ -429,52 +538,6 @@ GIT_EXTERN(int) git_commit_create_v(
|
|||||||
size_t parent_count,
|
size_t parent_count,
|
||||||
...);
|
...);
|
||||||
|
|
||||||
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 (and "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);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Amend an existing commit by replacing only non-NULL values.
|
* Amend an existing commit by replacing only non-NULL values.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -46,10 +46,10 @@ static int git_commit__create_buffer_internal(
|
|||||||
git_str *out,
|
git_str *out,
|
||||||
const git_signature *author,
|
const git_signature *author,
|
||||||
const git_signature *committer,
|
const git_signature *committer,
|
||||||
const char *message_encoding,
|
|
||||||
const char *message,
|
const char *message,
|
||||||
const git_oid *tree,
|
const git_oid *tree,
|
||||||
git_array_oid_t *parents)
|
git_array_oid_t *parents,
|
||||||
|
const git_commit_create_ext_options *opts)
|
||||||
{
|
{
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
const git_oid *parent;
|
const git_oid *parent;
|
||||||
@@ -69,8 +69,8 @@ static int git_commit__create_buffer_internal(
|
|||||||
git_signature__writebuf(out, "author ", author);
|
git_signature__writebuf(out, "author ", author);
|
||||||
git_signature__writebuf(out, "committer ", committer);
|
git_signature__writebuf(out, "committer ", committer);
|
||||||
|
|
||||||
if (message_encoding != NULL)
|
if (opts && opts->message_encoding != NULL)
|
||||||
git_str_printf(out, "encoding %s\n", message_encoding);
|
git_str_printf(out, "encoding %s\n", opts->message_encoding);
|
||||||
|
|
||||||
git_str_putc(out, '\n');
|
git_str_putc(out, '\n');
|
||||||
|
|
||||||
@@ -126,14 +126,13 @@ on_error:
|
|||||||
static int git_commit__create_internal(
|
static int git_commit__create_internal(
|
||||||
git_oid *id,
|
git_oid *id,
|
||||||
git_repository *repo,
|
git_repository *repo,
|
||||||
const char *update_ref,
|
|
||||||
const git_signature *author,
|
const git_signature *author,
|
||||||
const git_signature *committer,
|
const git_signature *committer,
|
||||||
const char *message_encoding,
|
|
||||||
const char *message,
|
const char *message,
|
||||||
const git_oid *tree,
|
const git_oid *tree,
|
||||||
git_commit_parent_callback parent_cb,
|
git_commit_parent_callback parent_cb,
|
||||||
void *parent_payload,
|
void *parent_payload,
|
||||||
|
const git_commit_create_ext_options *opts,
|
||||||
bool validate)
|
bool validate)
|
||||||
{
|
{
|
||||||
int error;
|
int error;
|
||||||
@@ -143,8 +142,10 @@ static int git_commit__create_internal(
|
|||||||
const git_oid *current_id = NULL;
|
const git_oid *current_id = NULL;
|
||||||
git_array_oid_t parents = GIT_ARRAY_INIT;
|
git_array_oid_t parents = GIT_ARRAY_INIT;
|
||||||
|
|
||||||
if (update_ref) {
|
if (opts && opts->update_ref) {
|
||||||
error = git_reference_lookup_resolved(&ref, repo, update_ref, 10);
|
error = git_reference_lookup_resolved(&ref,
|
||||||
|
repo, opts->update_ref, 10);
|
||||||
|
|
||||||
if (error < 0 && error != GIT_ENOTFOUND)
|
if (error < 0 && error != GIT_ENOTFOUND)
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
@@ -156,9 +157,8 @@ static int git_commit__create_internal(
|
|||||||
if ((error = validate_tree_and_parents(&parents, repo, tree, parent_cb, parent_payload, current_id, validate)) < 0)
|
if ((error = validate_tree_and_parents(&parents, repo, tree, parent_cb, parent_payload, current_id, validate)) < 0)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
error = git_commit__create_buffer_internal(&buf, author, committer,
|
error = git_commit__create_buffer_internal(&buf,
|
||||||
message_encoding, message, tree,
|
author, committer, message, tree, &parents, opts);
|
||||||
&parents);
|
|
||||||
|
|
||||||
if (error < 0)
|
if (error < 0)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
@@ -173,9 +173,9 @@ static int git_commit__create_internal(
|
|||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
|
|
||||||
if (update_ref != NULL) {
|
if (opts && opts->update_ref != NULL) {
|
||||||
error = git_reference__update_for_commit(
|
error = git_reference__update_for_commit(
|
||||||
repo, ref, update_ref, id, "commit");
|
repo, ref, opts->update_ref, id, "commit");
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -198,9 +198,14 @@ int git_commit_create_from_callback(
|
|||||||
git_commit_parent_callback parent_cb,
|
git_commit_parent_callback parent_cb,
|
||||||
void *parent_payload)
|
void *parent_payload)
|
||||||
{
|
{
|
||||||
|
git_commit_create_ext_options opts = GIT_COMMIT_CREATE_EXT_OPTIONS_INIT;
|
||||||
|
|
||||||
|
opts.update_ref = update_ref;
|
||||||
|
opts.message_encoding = message_encoding;
|
||||||
|
|
||||||
return git_commit__create_internal(
|
return git_commit__create_internal(
|
||||||
id, repo, update_ref, author, committer, message_encoding, message,
|
id, repo, author, committer, message,
|
||||||
tree, parent_cb, parent_payload, true);
|
tree, parent_cb, parent_payload, &opts, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -230,8 +235,9 @@ int git_commit_create_v(
|
|||||||
size_t parent_count,
|
size_t parent_count,
|
||||||
...)
|
...)
|
||||||
{
|
{
|
||||||
int error = 0;
|
git_commit_create_ext_options opts = GIT_COMMIT_CREATE_EXT_OPTIONS_INIT;
|
||||||
commit_parent_varargs data;
|
commit_parent_varargs data;
|
||||||
|
int error = 0;
|
||||||
|
|
||||||
GIT_ASSERT_ARG(tree);
|
GIT_ASSERT_ARG(tree);
|
||||||
GIT_ASSERT_ARG(git_tree_owner(tree) == repo);
|
GIT_ASSERT_ARG(git_tree_owner(tree) == repo);
|
||||||
@@ -239,10 +245,12 @@ int git_commit_create_v(
|
|||||||
data.total = parent_count;
|
data.total = parent_count;
|
||||||
va_start(data.args, parent_count);
|
va_start(data.args, parent_count);
|
||||||
|
|
||||||
|
opts.update_ref = update_ref;
|
||||||
|
opts.message_encoding = message_encoding;
|
||||||
|
|
||||||
error = git_commit__create_internal(
|
error = git_commit__create_internal(
|
||||||
id, repo, update_ref, author, committer,
|
id, repo, author, committer, message, git_tree_id(tree),
|
||||||
message_encoding, message, git_tree_id(tree),
|
commit_parent_from_varargs, &data, &opts, false);
|
||||||
commit_parent_from_varargs, &data, false);
|
|
||||||
|
|
||||||
va_end(data.args);
|
va_end(data.args);
|
||||||
return error;
|
return error;
|
||||||
@@ -271,17 +279,20 @@ int git_commit_create_from_ids(
|
|||||||
size_t parent_count,
|
size_t parent_count,
|
||||||
const git_oid *parents[])
|
const git_oid *parents[])
|
||||||
{
|
{
|
||||||
|
git_commit_create_ext_options opts = GIT_COMMIT_CREATE_EXT_OPTIONS_INIT;
|
||||||
commit_parent_oids data = { parent_count, parents };
|
commit_parent_oids data = { parent_count, parents };
|
||||||
|
|
||||||
|
opts.update_ref = update_ref;
|
||||||
|
opts.message_encoding = message_encoding;
|
||||||
|
|
||||||
return git_commit__create_internal(
|
return git_commit__create_internal(
|
||||||
id, repo, update_ref, author, committer,
|
id, repo, author, committer, message, tree,
|
||||||
message_encoding, message, tree,
|
commit_parent_from_ids, &data, &opts, true);
|
||||||
commit_parent_from_ids, &data, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
size_t total;
|
size_t total;
|
||||||
const git_commit **parents;
|
git_commit * const *parents;
|
||||||
git_repository *repo;
|
git_repository *repo;
|
||||||
} commit_parent_data;
|
} commit_parent_data;
|
||||||
|
|
||||||
@@ -297,6 +308,27 @@ static const git_oid *commit_parent_from_array(size_t curr, void *payload)
|
|||||||
return git_commit_id(commit);
|
return git_commit_id(commit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int git_commit_create_ext(
|
||||||
|
git_oid *id,
|
||||||
|
git_repository *repo,
|
||||||
|
const git_signature *author,
|
||||||
|
const git_signature *committer,
|
||||||
|
const char *message,
|
||||||
|
const git_tree *tree,
|
||||||
|
size_t parent_count,
|
||||||
|
git_commit * const parents[],
|
||||||
|
const git_commit_create_ext_options *opts)
|
||||||
|
{
|
||||||
|
commit_parent_data data = { parent_count, parents, repo };
|
||||||
|
|
||||||
|
GIT_ASSERT_ARG(tree);
|
||||||
|
GIT_ASSERT_ARG(git_tree_owner(tree) == repo);
|
||||||
|
|
||||||
|
return git_commit__create_internal(
|
||||||
|
id, repo, author, committer, message, git_tree_id(tree),
|
||||||
|
commit_parent_from_array, &data, opts, false);
|
||||||
|
}
|
||||||
|
|
||||||
int git_commit_create(
|
int git_commit_create(
|
||||||
git_oid *id,
|
git_oid *id,
|
||||||
git_repository *repo,
|
git_repository *repo,
|
||||||
@@ -309,15 +341,18 @@ int git_commit_create(
|
|||||||
size_t parent_count,
|
size_t parent_count,
|
||||||
const git_commit *parents[])
|
const git_commit *parents[])
|
||||||
{
|
{
|
||||||
commit_parent_data data = { parent_count, parents, repo };
|
git_commit_create_ext_options opts = GIT_COMMIT_CREATE_EXT_OPTIONS_INIT;
|
||||||
|
commit_parent_data data = { parent_count, (git_commit * const *)parents, repo };
|
||||||
|
|
||||||
GIT_ASSERT_ARG(tree);
|
GIT_ASSERT_ARG(tree);
|
||||||
GIT_ASSERT_ARG(git_tree_owner(tree) == repo);
|
GIT_ASSERT_ARG(git_tree_owner(tree) == repo);
|
||||||
|
|
||||||
|
opts.update_ref = update_ref;
|
||||||
|
opts.message_encoding = message_encoding;
|
||||||
|
|
||||||
return git_commit__create_internal(
|
return git_commit__create_internal(
|
||||||
id, repo, update_ref, author, committer,
|
id, repo, author, committer, message, git_tree_id(tree),
|
||||||
message_encoding, message, git_tree_id(tree),
|
commit_parent_from_array, &data, &opts, false);
|
||||||
commit_parent_from_array, &data, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const git_oid *commit_parent_for_amend(size_t curr, void *payload)
|
static const git_oid *commit_parent_for_amend(size_t curr, void *payload)
|
||||||
@@ -341,6 +376,7 @@ int git_commit_amend(
|
|||||||
git_repository *repo;
|
git_repository *repo;
|
||||||
git_oid tree_id;
|
git_oid tree_id;
|
||||||
git_reference *ref = NULL;
|
git_reference *ref = NULL;
|
||||||
|
git_commit_create_ext_options opts = GIT_COMMIT_CREATE_EXT_OPTIONS_INIT;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
GIT_ASSERT_ARG(id);
|
GIT_ASSERT_ARG(id);
|
||||||
@@ -378,9 +414,12 @@ int git_commit_amend(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
opts.message_encoding = message_encoding;
|
||||||
|
|
||||||
error = git_commit__create_internal(
|
error = git_commit__create_internal(
|
||||||
id, repo, NULL, author, committer, message_encoding, message,
|
id, repo, author, committer, message, &tree_id,
|
||||||
&tree_id, commit_parent_for_amend, (void *)commit_to_amend, false);
|
commit_parent_for_amend, (void *)commit_to_amend,
|
||||||
|
&opts, false);
|
||||||
|
|
||||||
if (!error && update_ref) {
|
if (!error && update_ref) {
|
||||||
error = git_reference__update_for_commit(
|
error = git_reference__update_for_commit(
|
||||||
@@ -964,9 +1003,10 @@ int git_commit__create_buffer(
|
|||||||
const git_commit *parents[])
|
const git_commit *parents[])
|
||||||
{
|
{
|
||||||
int error;
|
int error;
|
||||||
commit_parent_data data = { parent_count, parents, repo };
|
commit_parent_data data = { parent_count, (git_commit * const *)parents, repo };
|
||||||
git_array_oid_t parents_arr = GIT_ARRAY_INIT;
|
git_array_oid_t parents_arr = GIT_ARRAY_INIT;
|
||||||
const git_oid *tree_id;
|
const git_oid *tree_id;
|
||||||
|
git_commit_create_ext_options opts = GIT_COMMIT_CREATE_EXT_OPTIONS_INIT;
|
||||||
|
|
||||||
GIT_ASSERT_ARG(tree);
|
GIT_ASSERT_ARG(tree);
|
||||||
GIT_ASSERT_ARG(git_tree_owner(tree) == repo);
|
GIT_ASSERT_ARG(git_tree_owner(tree) == repo);
|
||||||
@@ -976,10 +1016,11 @@ int git_commit__create_buffer(
|
|||||||
if ((error = validate_tree_and_parents(&parents_arr, repo, tree_id, commit_parent_from_array, &data, NULL, true)) < 0)
|
if ((error = validate_tree_and_parents(&parents_arr, repo, tree_id, commit_parent_from_array, &data, NULL, true)) < 0)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
|
opts.message_encoding = message_encoding;
|
||||||
|
|
||||||
error = git_commit__create_buffer_internal(
|
error = git_commit__create_buffer_internal(
|
||||||
out, author, committer,
|
out, author, committer, message, tree_id,
|
||||||
message_encoding, message, tree_id,
|
&parents_arr, &opts);
|
||||||
&parents_arr);
|
|
||||||
|
|
||||||
git_array_clear(parents_arr);
|
git_array_clear(parents_arr);
|
||||||
return error;
|
return error;
|
||||||
|
|||||||
Reference in New Issue
Block a user