in sequencer.c [2229:2532]
static int do_pick_commit(struct repository *r,
struct todo_item *item,
struct replay_opts *opts,
int final_fixup, int *check_todo)
{
struct replay_ctx *ctx = opts->ctx;
unsigned int flags = should_edit(opts) ? EDIT_MSG : 0;
const char *msg_file = should_edit(opts) ? NULL : git_path_merge_msg(r);
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 };
int res, unborn = 0, reword = 0, allow, drop_commit;
enum todo_command command = item->command;
struct commit *commit = item->commit;
if (opts->no_commit) {
/*
* We do not intend to commit immediately. We just want to
* merge the differences in, so let's compute the tree
* that represents the "current" state for the merge machinery
* to work on.
*/
if (write_index_as_tree(&head, r->index, r->index_file, 0, NULL))
return error(_("your index file is unmerged."));
} else {
unborn = repo_get_oid(r, "HEAD", &head);
/* Do we want to generate a root commit? */
if (is_pick_or_similar(command) && opts->have_squash_onto &&
oideq(&head, &opts->squash_onto)) {
if (is_fixup(command))
return error(_("cannot fixup root commit"));
flags |= CREATE_ROOT_COMMIT;
unborn = 1;
} else if (unborn)
oidcpy(&head, the_hash_algo->empty_tree);
if (index_differs_from(r, unborn ? empty_tree_oid_hex(the_repository->hash_algo) : "HEAD",
NULL, 0))
return error_dirty_index(r, opts);
}
discard_index(r->index);
if (!commit->parents)
parent = NULL;
else if (commit->parents->next) {
/* Reverting or cherry-picking a merge commit */
int cnt;
struct commit_list *p;
if (!opts->mainline)
return error(_("commit %s is a merge but no -m option was given."),
oid_to_hex(&commit->object.oid));
for (cnt = 1, p = commit->parents;
cnt != opts->mainline && p;
cnt++)
p = p->next;
if (cnt != opts->mainline || !p)
return error(_("commit %s does not have parent %d"),
oid_to_hex(&commit->object.oid), opts->mainline);
parent = p->item;
} else if (1 < opts->mainline)
/*
* Non-first parent explicitly specified as mainline for
* non-merge commit
*/
return error(_("commit %s does not have parent %d"),
oid_to_hex(&commit->object.oid), opts->mainline);
else
parent = commit->parents->item;
if (get_message(commit, &msg) != 0)
return error(_("cannot get commit message for %s"),
oid_to_hex(&commit->object.oid));
if (opts->allow_ff && !is_fixup(command) &&
((parent && oideq(&parent->object.oid, &head)) ||
(!parent && unborn))) {
if (is_rebase_i(opts))
write_author_script(msg.message);
res = fast_forward_to(r, &commit->object.oid, &head, unborn,
opts);
if (res || command != TODO_REWORD)
goto leave;
reword = 1;
msg_file = NULL;
goto fast_forward_edit;
}
if (parent && repo_parse_commit(r, parent) < 0)
/* TRANSLATORS: The first %s will be a "todo" command like
"revert" or "pick", the second %s a SHA1. */
return error(_("%s: cannot parse parent commit %s"),
command_to_string(command),
oid_to_hex(&parent->object.oid));
/*
* "commit" is an existing commit. We would want to apply
* the difference it introduces since its first parent "prev"
* on top of the current HEAD if we are cherry-pick. Or the
* reverse of it if we are revert.
*/
if (command == TODO_REVERT) {
const char *orig_subject;
base = commit;
base_label = msg.label;
next = parent;
next_label = msg.parent_label;
if (opts->commit_use_reference) {
strbuf_commented_addf(&ctx->message, comment_line_str,
"*** SAY WHY WE ARE REVERTING ON THE TITLE LINE ***");
} else if (skip_prefix(msg.subject, "Revert \"", &orig_subject) &&
/*
* We don't touch pre-existing repeated reverts, because
* theoretically these can be nested arbitrarily deeply,
* thus requiring excessive complexity to deal with.
*/
!starts_with(orig_subject, "Revert \"")) {
strbuf_addstr(&ctx->message, "Reapply \"");
strbuf_addstr(&ctx->message, orig_subject);
strbuf_addstr(&ctx->message, "\n");
} else {
strbuf_addstr(&ctx->message, "Revert \"");
strbuf_addstr(&ctx->message, msg.subject);
strbuf_addstr(&ctx->message, "\"\n");
}
strbuf_addstr(&ctx->message, "\nThis reverts commit ");
refer_to_commit(opts, &ctx->message, commit);
if (commit->parents && commit->parents->next) {
strbuf_addstr(&ctx->message, ", reversing\nchanges made to ");
refer_to_commit(opts, &ctx->message, parent);
}
strbuf_addstr(&ctx->message, ".\n");
} else {
const char *p;
base = parent;
base_label = msg.parent_label;
next = commit;
next_label = msg.label;
/* Append the commit log message to ctx->message. */
if (find_commit_subject(msg.message, &p))
strbuf_addstr(&ctx->message, p);
if (opts->record_origin) {
strbuf_complete_line(&ctx->message);
if (!has_conforming_footer(&ctx->message, NULL, 0))
strbuf_addch(&ctx->message, '\n');
strbuf_addstr(&ctx->message, cherry_picked_prefix);
strbuf_addstr(&ctx->message, oid_to_hex(&commit->object.oid));
strbuf_addstr(&ctx->message, ")\n");
}
if (!is_fixup(command))
author = get_author(msg.message);
}
ctx->have_message = 1;
if (command == TODO_REWORD)
reword = 1;
else if (is_fixup(command)) {
if (update_squash_messages(r, command, commit,
opts, item->flags)) {
res = -1;
goto leave;
}
flags |= AMEND_MSG;
if (!final_fixup)
msg_file = rebase_path_squash_msg();
else if (file_exists(rebase_path_fixup_msg())) {
flags |= VERBATIM_MSG;
msg_file = rebase_path_fixup_msg();
} else {
const char *dest = git_path_squash_msg(r);
unlink(dest);
if (copy_file(dest, rebase_path_squash_msg(), 0666)) {
res = error(_("could not copy '%s' to '%s'"),
rebase_path_squash_msg(), dest);
goto leave;
}
unlink(git_path_merge_msg(r));
msg_file = dest;
flags |= EDIT_MSG;
}
}
if (opts->signoff && !is_fixup(command))
append_signoff(&ctx->message, 0, 0);
if (is_rebase_i(opts) && write_author_script(msg.message) < 0)
res = -1;
else if (!opts->strategy ||
!strcmp(opts->strategy, "recursive") ||
!strcmp(opts->strategy, "ort") ||
command == TODO_REVERT) {
res = do_recursive_merge(r, base, next, base_label, next_label,
&head, &ctx->message, opts);
if (res < 0)
goto leave;
res |= write_message(ctx->message.buf, ctx->message.len,
git_path_merge_msg(r), 0);
} else {
struct commit_list *common = NULL;
struct commit_list *remotes = NULL;
res = write_message(ctx->message.buf, ctx->message.len,
git_path_merge_msg(r), 0);
commit_list_insert(base, &common);
commit_list_insert(next, &remotes);
res |= try_merge_command(r, opts->strategy,
opts->xopts.nr, opts->xopts.v,
common, oid_to_hex(&head), remotes);
free_commit_list(common);
free_commit_list(remotes);
}
/*
* If the merge was clean or if it failed due to conflict, we write
* CHERRY_PICK_HEAD for the subsequent invocation of commit to use.
* However, if the merge did not even start, then we don't want to
* write it at all.
*/
if ((command == TODO_PICK || command == TODO_REWORD ||
command == TODO_EDIT) && !opts->no_commit &&
(res == 0 || res == 1) &&
refs_update_ref(get_main_ref_store(the_repository), NULL, "CHERRY_PICK_HEAD", &commit->object.oid, NULL,
REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR))
res = -1;
if (command == TODO_REVERT && ((opts->no_commit && res == 0) || res == 1) &&
refs_update_ref(get_main_ref_store(the_repository), NULL, "REVERT_HEAD", &commit->object.oid, NULL,
REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR))
res = -1;
if (res) {
error(command == TODO_REVERT
? _("could not revert %s... %s")
: _("could not apply %s... %s"),
short_commit_name(r, commit), msg.subject);
print_advice(r, res == 1, opts);
repo_rerere(r, opts->allow_rerere_auto);
goto leave;
}
drop_commit = 0;
allow = allow_empty(r, opts, commit);
if (allow < 0) {
res = allow;
goto leave;
} else if (allow == 1) {
flags |= ALLOW_EMPTY;
} else if (allow == 2) {
drop_commit = 1;
refs_delete_ref(get_main_ref_store(r), "", "CHERRY_PICK_HEAD",
NULL, REF_NO_DEREF);
unlink(git_path_merge_msg(r));
refs_delete_ref(get_main_ref_store(r), "", "AUTO_MERGE",
NULL, REF_NO_DEREF);
fprintf(stderr,
_("dropping %s %s -- patch contents already upstream\n"),
oid_to_hex(&commit->object.oid), msg.subject);
} /* else allow == 0 and there's nothing special to do */
if (!opts->no_commit && !drop_commit) {
if (author || command == TODO_REVERT || (flags & AMEND_MSG))
res = do_commit(r, msg_file, author, opts, flags,
commit? &commit->object.oid : NULL);
else
res = error(_("unable to parse commit author"));
*check_todo = !!(flags & EDIT_MSG);
if (!res && reword) {
fast_forward_edit:
/*
* To reword we amend the commit we just
* picked or fast-forwarded. As the commit has
* already been picked we want to use the same
* set of commit flags regardless of how we
* got here.
*/
flags = EDIT_MSG | VERIFY_MSG | AMEND_MSG | ALLOW_EMPTY;
res = run_git_commit(NULL, opts, flags);
*check_todo = 1;
}
}
if (!res && final_fixup) {
unlink(rebase_path_fixup_msg());
unlink(rebase_path_squash_msg());
unlink(rebase_path_current_fixups());
strbuf_reset(&ctx->current_fixups);
ctx->current_fixup_count = 0;
}
leave:
free_message(commit, &msg);
free(author);
update_abort_safety_file();
return res;
}