@ -592,6 +592,18 @@ static int read_env_script(struct argv_array *env)
@@ -592,6 +592,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"
@ -984,6 +996,160 @@ void print_commit_summary(const char *prefix, const struct object_id *oid,
@@ -984,6 +996,160 @@ void print_commit_summary(const char *prefix, const struct object_id *oid,
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 amend_msg = STRBUF_INIT;
char *amend_author = NULL;
const char *gpg_sign;
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 = &amend_msg;
strbuf_addstr(msg, orig_message);
}
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);
}
cleanup = (flags & CLEANUP_MSG) ? COMMIT_MSG_CLEANUP_ALL :
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;
}
gpg_sign = opts->gpg_sign ? opts->gpg_sign : default_gpg_sign;
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 (commit_tree_extended(msg->buf, msg->len, tree.hash, parents,
oid->hash, author, 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(&amend_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;
@ -1235,6 +1401,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
@@ -1235,6 +1401,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;
@ -1351,6 +1518,8 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
@@ -1351,6 +1518,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)
@ -1436,9 +1605,13 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
@@ -1436,9 +1605,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());
@ -1447,6 +1620,7 @@ fast_forward_edit:
@@ -1447,6 +1620,7 @@ fast_forward_edit:
leave:
free_message(commit, &msg);
free(author);
update_abort_safety_file();
return res;