Merge branch 'pw/sequencer-in-process-commit'
The sequencer infrastructure is shared across "git cherry-pick", "git rebase -i", etc., and has always spawned "git commit" when it needs to create a commit. It has been taught to do so internally, when able, by reusing the codepath "git commit" itself uses, which gives performance boost for a few tens of percents in some sample scenarios. * pw/sequencer-in-process-commit: sequencer: run 'prepare-commit-msg' hook t7505: add tests for cherry-pick and rebase -i/-p t7505: style fixes sequencer: assign only free()able strings to gpg_sign sequencer: improve config handling t3512/t3513: remove KNOWN_FAILURE_CHERRY_PICK_SEES_EMPTY_COMMIT=1 sequencer: try to commit without forking 'git commit' sequencer: load commit related config sequencer: simplify adding Signed-off-by: trailer commit: move print_commit_summary() to libgit commit: move post-rewrite code to libgit Add a function to update HEAD after creating a commit commit: move empty message checks to libgit t3404: check intermediate squash messages
This commit is contained in:
538
sequencer.c
538
sequencer.c
@ -1,10 +1,10 @@
|
||||
#include "cache.h"
|
||||
#include "config.h"
|
||||
#include "lockfile.h"
|
||||
#include "sequencer.h"
|
||||
#include "dir.h"
|
||||
#include "object.h"
|
||||
#include "commit.h"
|
||||
#include "sequencer.h"
|
||||
#include "tag.h"
|
||||
#include "run-command.h"
|
||||
#include "exec_cmd.h"
|
||||
@ -21,12 +21,16 @@
|
||||
#include "log-tree.h"
|
||||
#include "wt-status.h"
|
||||
#include "hashmap.h"
|
||||
#include "notes-utils.h"
|
||||
#include "sigchain.h"
|
||||
|
||||
#define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
|
||||
|
||||
const char sign_off_header[] = "Signed-off-by: ";
|
||||
static const char cherry_picked_prefix[] = "(cherry picked from commit ";
|
||||
|
||||
GIT_PATH_FUNC(git_path_commit_editmsg, "COMMIT_EDITMSG")
|
||||
|
||||
GIT_PATH_FUNC(git_path_seq_dir, "sequencer")
|
||||
|
||||
static GIT_PATH_FUNC(git_path_todo_file, "sequencer/todo")
|
||||
@ -130,6 +134,51 @@ static GIT_PATH_FUNC(rebase_path_strategy, "rebase-merge/strategy")
|
||||
static GIT_PATH_FUNC(rebase_path_strategy_opts, "rebase-merge/strategy_opts")
|
||||
static GIT_PATH_FUNC(rebase_path_allow_rerere_autoupdate, "rebase-merge/allow_rerere_autoupdate")
|
||||
|
||||
static int git_sequencer_config(const char *k, const char *v, void *cb)
|
||||
{
|
||||
struct replay_opts *opts = cb;
|
||||
int status;
|
||||
|
||||
if (!strcmp(k, "commit.cleanup")) {
|
||||
const char *s;
|
||||
|
||||
status = git_config_string(&s, k, v);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
if (!strcmp(s, "verbatim"))
|
||||
opts->default_msg_cleanup = COMMIT_MSG_CLEANUP_NONE;
|
||||
else if (!strcmp(s, "whitespace"))
|
||||
opts->default_msg_cleanup = COMMIT_MSG_CLEANUP_SPACE;
|
||||
else if (!strcmp(s, "strip"))
|
||||
opts->default_msg_cleanup = COMMIT_MSG_CLEANUP_ALL;
|
||||
else if (!strcmp(s, "scissors"))
|
||||
opts->default_msg_cleanup = COMMIT_MSG_CLEANUP_SPACE;
|
||||
else
|
||||
warning(_("invalid commit message cleanup mode '%s'"),
|
||||
s);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
if (!strcmp(k, "commit.gpgsign")) {
|
||||
opts->gpg_sign = git_config_bool(k, v) ? xstrdup("") : NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
status = git_gpg_config(k, v, NULL);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
return git_diff_basic_config(k, v, NULL);
|
||||
}
|
||||
|
||||
void sequencer_init_config(struct replay_opts *opts)
|
||||
{
|
||||
opts->default_msg_cleanup = COMMIT_MSG_CLEANUP_NONE;
|
||||
git_config(git_sequencer_config, opts);
|
||||
}
|
||||
|
||||
static inline int is_rebase_i(const struct replay_opts *opts)
|
||||
{
|
||||
return opts->action == REPLAY_INTERACTIVE_REBASE;
|
||||
@ -478,9 +527,6 @@ static int do_recursive_merge(struct commit *base, struct commit *next,
|
||||
_(action_name(opts)));
|
||||
rollback_lock_file(&index_lock);
|
||||
|
||||
if (opts->signoff)
|
||||
append_signoff(msgbuf, 0, 0);
|
||||
|
||||
if (!clean)
|
||||
append_conflicts_hint(msgbuf);
|
||||
|
||||
@ -596,6 +642,18 @@ static int read_env_script(struct argv_array *env)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char *get_author(const char *message)
|
||||
{
|
||||
size_t len;
|
||||
const char *a;
|
||||
|
||||
a = find_commit_header(message, "author", &len);
|
||||
if (a)
|
||||
return xmemdupz(a, len);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char staged_changes_advice[] =
|
||||
N_("you have staged changes in your working tree\n"
|
||||
"If these changes are meant to be squashed into the previous commit, run:\n"
|
||||
@ -658,8 +716,6 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,
|
||||
argv_array_push(&cmd.args, "--amend");
|
||||
if (opts->gpg_sign)
|
||||
argv_array_pushf(&cmd.args, "-S%s", opts->gpg_sign);
|
||||
if (opts->signoff)
|
||||
argv_array_push(&cmd.args, "-s");
|
||||
if (defmsg)
|
||||
argv_array_pushl(&cmd.args, "-F", defmsg, NULL);
|
||||
if ((flags & CLEANUP_MSG))
|
||||
@ -694,6 +750,461 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,
|
||||
return run_command(&cmd);
|
||||
}
|
||||
|
||||
static int rest_is_empty(const struct strbuf *sb, int start)
|
||||
{
|
||||
int i, eol;
|
||||
const char *nl;
|
||||
|
||||
/* Check if the rest is just whitespace and Signed-off-by's. */
|
||||
for (i = start; i < sb->len; i++) {
|
||||
nl = memchr(sb->buf + i, '\n', sb->len - i);
|
||||
if (nl)
|
||||
eol = nl - sb->buf;
|
||||
else
|
||||
eol = sb->len;
|
||||
|
||||
if (strlen(sign_off_header) <= eol - i &&
|
||||
starts_with(sb->buf + i, sign_off_header)) {
|
||||
i = eol;
|
||||
continue;
|
||||
}
|
||||
while (i < eol)
|
||||
if (!isspace(sb->buf[i++]))
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find out if the message in the strbuf contains only whitespace and
|
||||
* Signed-off-by lines.
|
||||
*/
|
||||
int message_is_empty(const struct strbuf *sb,
|
||||
enum commit_msg_cleanup_mode cleanup_mode)
|
||||
{
|
||||
if (cleanup_mode == COMMIT_MSG_CLEANUP_NONE && sb->len)
|
||||
return 0;
|
||||
return rest_is_empty(sb, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* See if the user edited the message in the editor or left what
|
||||
* was in the template intact
|
||||
*/
|
||||
int template_untouched(const struct strbuf *sb, const char *template_file,
|
||||
enum commit_msg_cleanup_mode cleanup_mode)
|
||||
{
|
||||
struct strbuf tmpl = STRBUF_INIT;
|
||||
const char *start;
|
||||
|
||||
if (cleanup_mode == COMMIT_MSG_CLEANUP_NONE && sb->len)
|
||||
return 0;
|
||||
|
||||
if (!template_file || strbuf_read_file(&tmpl, template_file, 0) <= 0)
|
||||
return 0;
|
||||
|
||||
strbuf_stripspace(&tmpl, cleanup_mode == COMMIT_MSG_CLEANUP_ALL);
|
||||
if (!skip_prefix(sb->buf, tmpl.buf, &start))
|
||||
start = sb->buf;
|
||||
strbuf_release(&tmpl);
|
||||
return rest_is_empty(sb, start - sb->buf);
|
||||
}
|
||||
|
||||
int update_head_with_reflog(const struct commit *old_head,
|
||||
const struct object_id *new_head,
|
||||
const char *action, const struct strbuf *msg,
|
||||
struct strbuf *err)
|
||||
{
|
||||
struct ref_transaction *transaction;
|
||||
struct strbuf sb = STRBUF_INIT;
|
||||
const char *nl;
|
||||
int ret = 0;
|
||||
|
||||
if (action) {
|
||||
strbuf_addstr(&sb, action);
|
||||
strbuf_addstr(&sb, ": ");
|
||||
}
|
||||
|
||||
nl = strchr(msg->buf, '\n');
|
||||
if (nl) {
|
||||
strbuf_add(&sb, msg->buf, nl + 1 - msg->buf);
|
||||
} else {
|
||||
strbuf_addbuf(&sb, msg);
|
||||
strbuf_addch(&sb, '\n');
|
||||
}
|
||||
|
||||
transaction = ref_transaction_begin(err);
|
||||
if (!transaction ||
|
||||
ref_transaction_update(transaction, "HEAD", new_head,
|
||||
old_head ? &old_head->object.oid : &null_oid,
|
||||
0, sb.buf, err) ||
|
||||
ref_transaction_commit(transaction, err)) {
|
||||
ret = -1;
|
||||
}
|
||||
ref_transaction_free(transaction);
|
||||
strbuf_release(&sb);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int run_rewrite_hook(const struct object_id *oldoid,
|
||||
const struct object_id *newoid)
|
||||
{
|
||||
struct child_process proc = CHILD_PROCESS_INIT;
|
||||
const char *argv[3];
|
||||
int code;
|
||||
struct strbuf sb = STRBUF_INIT;
|
||||
|
||||
argv[0] = find_hook("post-rewrite");
|
||||
if (!argv[0])
|
||||
return 0;
|
||||
|
||||
argv[1] = "amend";
|
||||
argv[2] = NULL;
|
||||
|
||||
proc.argv = argv;
|
||||
proc.in = -1;
|
||||
proc.stdout_to_stderr = 1;
|
||||
|
||||
code = start_command(&proc);
|
||||
if (code)
|
||||
return code;
|
||||
strbuf_addf(&sb, "%s %s\n", oid_to_hex(oldoid), oid_to_hex(newoid));
|
||||
sigchain_push(SIGPIPE, SIG_IGN);
|
||||
write_in_full(proc.in, sb.buf, sb.len);
|
||||
close(proc.in);
|
||||
strbuf_release(&sb);
|
||||
sigchain_pop(SIGPIPE);
|
||||
return finish_command(&proc);
|
||||
}
|
||||
|
||||
void commit_post_rewrite(const struct commit *old_head,
|
||||
const struct object_id *new_head)
|
||||
{
|
||||
struct notes_rewrite_cfg *cfg;
|
||||
|
||||
cfg = init_copy_notes_for_rewrite("amend");
|
||||
if (cfg) {
|
||||
/* we are amending, so old_head is not NULL */
|
||||
copy_note_for_rewrite(cfg, &old_head->object.oid, new_head);
|
||||
finish_copy_notes_for_rewrite(cfg, "Notes added by 'git commit --amend'");
|
||||
}
|
||||
run_rewrite_hook(&old_head->object.oid, new_head);
|
||||
}
|
||||
|
||||
static int run_prepare_commit_msg_hook(struct strbuf *msg, const char *commit)
|
||||
{
|
||||
struct argv_array hook_env = ARGV_ARRAY_INIT;
|
||||
int ret;
|
||||
const char *name;
|
||||
|
||||
name = git_path_commit_editmsg();
|
||||
if (write_message(msg->buf, msg->len, name, 0))
|
||||
return -1;
|
||||
|
||||
argv_array_pushf(&hook_env, "GIT_INDEX_FILE=%s", get_index_file());
|
||||
argv_array_push(&hook_env, "GIT_EDITOR=:");
|
||||
if (commit)
|
||||
ret = run_hook_le(hook_env.argv, "prepare-commit-msg", name,
|
||||
"commit", commit, NULL);
|
||||
else
|
||||
ret = run_hook_le(hook_env.argv, "prepare-commit-msg", name,
|
||||
"message", NULL);
|
||||
if (ret)
|
||||
ret = error(_("'prepare-commit-msg' hook failed"));
|
||||
argv_array_clear(&hook_env);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const char implicit_ident_advice_noconfig[] =
|
||||
N_("Your name and email address were configured automatically based\n"
|
||||
"on your username and hostname. Please check that they are accurate.\n"
|
||||
"You can suppress this message by setting them explicitly. Run the\n"
|
||||
"following command and follow the instructions in your editor to edit\n"
|
||||
"your configuration file:\n"
|
||||
"\n"
|
||||
" git config --global --edit\n"
|
||||
"\n"
|
||||
"After doing this, you may fix the identity used for this commit with:\n"
|
||||
"\n"
|
||||
" git commit --amend --reset-author\n");
|
||||
|
||||
static const char implicit_ident_advice_config[] =
|
||||
N_("Your name and email address were configured automatically based\n"
|
||||
"on your username and hostname. Please check that they are accurate.\n"
|
||||
"You can suppress this message by setting them explicitly:\n"
|
||||
"\n"
|
||||
" git config --global user.name \"Your Name\"\n"
|
||||
" git config --global user.email you@example.com\n"
|
||||
"\n"
|
||||
"After doing this, you may fix the identity used for this commit with:\n"
|
||||
"\n"
|
||||
" git commit --amend --reset-author\n");
|
||||
|
||||
static const char *implicit_ident_advice(void)
|
||||
{
|
||||
char *user_config = expand_user_path("~/.gitconfig", 0);
|
||||
char *xdg_config = xdg_config_home("config");
|
||||
int config_exists = file_exists(user_config) || file_exists(xdg_config);
|
||||
|
||||
free(user_config);
|
||||
free(xdg_config);
|
||||
|
||||
if (config_exists)
|
||||
return _(implicit_ident_advice_config);
|
||||
else
|
||||
return _(implicit_ident_advice_noconfig);
|
||||
|
||||
}
|
||||
|
||||
void print_commit_summary(const char *prefix, const struct object_id *oid,
|
||||
unsigned int flags)
|
||||
{
|
||||
struct rev_info rev;
|
||||
struct commit *commit;
|
||||
struct strbuf format = STRBUF_INIT;
|
||||
const char *head;
|
||||
struct pretty_print_context pctx = {0};
|
||||
struct strbuf author_ident = STRBUF_INIT;
|
||||
struct strbuf committer_ident = STRBUF_INIT;
|
||||
|
||||
commit = lookup_commit(oid);
|
||||
if (!commit)
|
||||
die(_("couldn't look up newly created commit"));
|
||||
if (parse_commit(commit))
|
||||
die(_("could not parse newly created commit"));
|
||||
|
||||
strbuf_addstr(&format, "format:%h] %s");
|
||||
|
||||
format_commit_message(commit, "%an <%ae>", &author_ident, &pctx);
|
||||
format_commit_message(commit, "%cn <%ce>", &committer_ident, &pctx);
|
||||
if (strbuf_cmp(&author_ident, &committer_ident)) {
|
||||
strbuf_addstr(&format, "\n Author: ");
|
||||
strbuf_addbuf_percentquote(&format, &author_ident);
|
||||
}
|
||||
if (flags & SUMMARY_SHOW_AUTHOR_DATE) {
|
||||
struct strbuf date = STRBUF_INIT;
|
||||
|
||||
format_commit_message(commit, "%ad", &date, &pctx);
|
||||
strbuf_addstr(&format, "\n Date: ");
|
||||
strbuf_addbuf_percentquote(&format, &date);
|
||||
strbuf_release(&date);
|
||||
}
|
||||
if (!committer_ident_sufficiently_given()) {
|
||||
strbuf_addstr(&format, "\n Committer: ");
|
||||
strbuf_addbuf_percentquote(&format, &committer_ident);
|
||||
if (advice_implicit_identity) {
|
||||
strbuf_addch(&format, '\n');
|
||||
strbuf_addstr(&format, implicit_ident_advice());
|
||||
}
|
||||
}
|
||||
strbuf_release(&author_ident);
|
||||
strbuf_release(&committer_ident);
|
||||
|
||||
init_revisions(&rev, prefix);
|
||||
setup_revisions(0, NULL, &rev, NULL);
|
||||
|
||||
rev.diff = 1;
|
||||
rev.diffopt.output_format =
|
||||
DIFF_FORMAT_SHORTSTAT | DIFF_FORMAT_SUMMARY;
|
||||
|
||||
rev.verbose_header = 1;
|
||||
rev.show_root_diff = 1;
|
||||
get_commit_format(format.buf, &rev);
|
||||
rev.always_show_header = 0;
|
||||
rev.diffopt.detect_rename = DIFF_DETECT_RENAME;
|
||||
rev.diffopt.break_opt = 0;
|
||||
diff_setup_done(&rev.diffopt);
|
||||
|
||||
head = resolve_ref_unsafe("HEAD", 0, NULL, NULL);
|
||||
if (!head)
|
||||
die_errno(_("unable to resolve HEAD after creating commit"));
|
||||
if (!strcmp(head, "HEAD"))
|
||||
head = _("detached HEAD");
|
||||
else
|
||||
skip_prefix(head, "refs/heads/", &head);
|
||||
printf("[%s%s ", head, (flags & SUMMARY_INITIAL_COMMIT) ?
|
||||
_(" (root-commit)") : "");
|
||||
|
||||
if (!log_tree_commit(&rev, commit)) {
|
||||
rev.always_show_header = 1;
|
||||
rev.use_terminator = 1;
|
||||
log_tree_commit(&rev, commit);
|
||||
}
|
||||
|
||||
strbuf_release(&format);
|
||||
}
|
||||
|
||||
static int parse_head(struct commit **head)
|
||||
{
|
||||
struct commit *current_head;
|
||||
struct object_id oid;
|
||||
|
||||
if (get_oid("HEAD", &oid)) {
|
||||
current_head = NULL;
|
||||
} else {
|
||||
current_head = lookup_commit_reference(&oid);
|
||||
if (!current_head)
|
||||
return error(_("could not parse HEAD"));
|
||||
if (oidcmp(&oid, ¤t_head->object.oid)) {
|
||||
warning(_("HEAD %s is not a commit!"),
|
||||
oid_to_hex(&oid));
|
||||
}
|
||||
if (parse_commit(current_head))
|
||||
return error(_("could not parse HEAD commit"));
|
||||
}
|
||||
*head = current_head;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to commit without forking 'git commit'. In some cases we need
|
||||
* to run 'git commit' to display an error message
|
||||
*
|
||||
* Returns:
|
||||
* -1 - error unable to commit
|
||||
* 0 - success
|
||||
* 1 - run 'git commit'
|
||||
*/
|
||||
static int try_to_commit(struct strbuf *msg, const char *author,
|
||||
struct replay_opts *opts, unsigned int flags,
|
||||
struct object_id *oid)
|
||||
{
|
||||
struct object_id tree;
|
||||
struct commit *current_head;
|
||||
struct commit_list *parents = NULL;
|
||||
struct commit_extra_header *extra = NULL;
|
||||
struct strbuf err = STRBUF_INIT;
|
||||
struct strbuf commit_msg = STRBUF_INIT;
|
||||
char *amend_author = NULL;
|
||||
const char *hook_commit = NULL;
|
||||
enum commit_msg_cleanup_mode cleanup;
|
||||
int res = 0;
|
||||
|
||||
if (parse_head(¤t_head))
|
||||
return -1;
|
||||
|
||||
if (flags & AMEND_MSG) {
|
||||
const char *exclude_gpgsig[] = { "gpgsig", NULL };
|
||||
const char *out_enc = get_commit_output_encoding();
|
||||
const char *message = logmsg_reencode(current_head, NULL,
|
||||
out_enc);
|
||||
|
||||
if (!msg) {
|
||||
const char *orig_message = NULL;
|
||||
|
||||
find_commit_subject(message, &orig_message);
|
||||
msg = &commit_msg;
|
||||
strbuf_addstr(msg, orig_message);
|
||||
hook_commit = "HEAD";
|
||||
}
|
||||
author = amend_author = get_author(message);
|
||||
unuse_commit_buffer(current_head, message);
|
||||
if (!author) {
|
||||
res = error(_("unable to parse commit author"));
|
||||
goto out;
|
||||
}
|
||||
parents = copy_commit_list(current_head->parents);
|
||||
extra = read_commit_extra_headers(current_head, exclude_gpgsig);
|
||||
} else if (current_head) {
|
||||
commit_list_insert(current_head, &parents);
|
||||
}
|
||||
|
||||
if (write_cache_as_tree(tree.hash, 0, NULL)) {
|
||||
res = error(_("git write-tree failed to write a tree"));
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!(flags & ALLOW_EMPTY) && !oidcmp(current_head ?
|
||||
¤t_head->tree->object.oid :
|
||||
&empty_tree_oid, &tree)) {
|
||||
res = 1; /* run 'git commit' to display error message */
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (find_hook("prepare-commit-msg")) {
|
||||
res = run_prepare_commit_msg_hook(msg, hook_commit);
|
||||
if (res)
|
||||
goto out;
|
||||
if (strbuf_read_file(&commit_msg, git_path_commit_editmsg(),
|
||||
2048) < 0) {
|
||||
res = error_errno(_("unable to read commit message "
|
||||
"from '%s'"),
|
||||
git_path_commit_editmsg());
|
||||
goto out;
|
||||
}
|
||||
msg = &commit_msg;
|
||||
}
|
||||
|
||||
cleanup = (flags & CLEANUP_MSG) ? COMMIT_MSG_CLEANUP_ALL :
|
||||
opts->default_msg_cleanup;
|
||||
|
||||
if (cleanup != COMMIT_MSG_CLEANUP_NONE)
|
||||
strbuf_stripspace(msg, cleanup == COMMIT_MSG_CLEANUP_ALL);
|
||||
if (!opts->allow_empty_message && message_is_empty(msg, cleanup)) {
|
||||
res = 1; /* run 'git commit' to display error message */
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (commit_tree_extended(msg->buf, msg->len, tree.hash, parents,
|
||||
oid->hash, author, opts->gpg_sign, extra)) {
|
||||
res = error(_("failed to write commit object"));
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (update_head_with_reflog(current_head, oid,
|
||||
getenv("GIT_REFLOG_ACTION"), msg, &err)) {
|
||||
res = error("%s", err.buf);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (flags & AMEND_MSG)
|
||||
commit_post_rewrite(current_head, oid);
|
||||
|
||||
out:
|
||||
free_commit_extra_headers(extra);
|
||||
strbuf_release(&err);
|
||||
strbuf_release(&commit_msg);
|
||||
free(amend_author);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int do_commit(const char *msg_file, const char *author,
|
||||
struct replay_opts *opts, unsigned int flags)
|
||||
{
|
||||
int res = 1;
|
||||
|
||||
if (!(flags & EDIT_MSG) && !(flags & VERIFY_MSG)) {
|
||||
struct object_id oid;
|
||||
struct strbuf sb = STRBUF_INIT;
|
||||
|
||||
if (msg_file && strbuf_read_file(&sb, msg_file, 2048) < 0)
|
||||
return error_errno(_("unable to read commit message "
|
||||
"from '%s'"),
|
||||
msg_file);
|
||||
|
||||
res = try_to_commit(msg_file ? &sb : NULL, author, opts, flags,
|
||||
&oid);
|
||||
strbuf_release(&sb);
|
||||
if (!res) {
|
||||
unlink(git_path_cherry_pick_head());
|
||||
unlink(git_path_merge_msg());
|
||||
if (!is_rebase_i(opts))
|
||||
print_commit_summary(NULL, &oid,
|
||||
SUMMARY_SHOW_AUTHOR_DATE);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
if (res == 1)
|
||||
return run_git_commit(msg_file, opts, flags);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int is_original_commit_empty(struct commit *commit)
|
||||
{
|
||||
const struct object_id *ptree_oid;
|
||||
@ -952,6 +1463,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
|
||||
struct object_id head;
|
||||
struct commit *base, *next, *parent;
|
||||
const char *base_label, *next_label;
|
||||
char *author = NULL;
|
||||
struct commit_message msg = { NULL, NULL, NULL, NULL };
|
||||
struct strbuf msgbuf = STRBUF_INIT;
|
||||
int res, unborn = 0, allow;
|
||||
@ -1066,6 +1578,8 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
|
||||
strbuf_addstr(&msgbuf, oid_to_hex(&commit->object.oid));
|
||||
strbuf_addstr(&msgbuf, ")\n");
|
||||
}
|
||||
if (!is_fixup(command))
|
||||
author = get_author(msg.message);
|
||||
}
|
||||
|
||||
if (command == TODO_REWORD)
|
||||
@ -1091,6 +1605,9 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
|
||||
}
|
||||
}
|
||||
|
||||
if (opts->signoff)
|
||||
append_signoff(&msgbuf, 0, 0);
|
||||
|
||||
if (is_rebase_i(opts) && write_author_script(msg.message) < 0)
|
||||
res = -1;
|
||||
else if (!opts->strategy || !strcmp(opts->strategy, "recursive") || command == TODO_REVERT) {
|
||||
@ -1148,9 +1665,13 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
|
||||
goto leave;
|
||||
} else if (allow)
|
||||
flags |= ALLOW_EMPTY;
|
||||
if (!opts->no_commit)
|
||||
if (!opts->no_commit) {
|
||||
fast_forward_edit:
|
||||
res = run_git_commit(msg_file, opts, flags);
|
||||
if (author || command == TODO_REVERT || (flags & AMEND_MSG))
|
||||
res = do_commit(msg_file, author, opts, flags);
|
||||
else
|
||||
res = error(_("unable to parse commit author"));
|
||||
}
|
||||
|
||||
if (!res && final_fixup) {
|
||||
unlink(rebase_path_fixup_msg());
|
||||
@ -1159,6 +1680,7 @@ fast_forward_edit:
|
||||
|
||||
leave:
|
||||
free_message(commit, &msg);
|
||||
free(author);
|
||||
update_abort_safety_file();
|
||||
|
||||
return res;
|
||||
|
||||
Reference in New Issue
Block a user