in sequencer.c [2120:2400]
static int do_pick_commit(struct repository *r,
struct todo_item *item,
struct replay_opts *opts,
int final_fixup, int *check_todo)
{
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 };
struct strbuf msgbuf = STRBUF_INIT;
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 = get_oid("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() : "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 && parse_commit(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) {
base = commit;
base_label = msg.label;
next = parent;
next_label = msg.parent_label;
if (opts->commit_use_reference) {
strbuf_addstr(&msgbuf,
"# *** SAY WHY WE ARE REVERTING ON THE TITLE LINE ***");
} else {
strbuf_addstr(&msgbuf, "Revert \"");
strbuf_addstr(&msgbuf, msg.subject);
strbuf_addstr(&msgbuf, "\"");
}
strbuf_addstr(&msgbuf, "\n\nThis reverts commit ");
refer_to_commit(opts, &msgbuf, commit);
if (commit->parents && commit->parents->next) {
strbuf_addstr(&msgbuf, ", reversing\nchanges made to ");
refer_to_commit(opts, &msgbuf, parent);
}
strbuf_addstr(&msgbuf, ".\n");
} else {
const char *p;
base = parent;
base_label = msg.parent_label;
next = commit;
next_label = msg.label;
/* Append the commit log message to msgbuf. */
if (find_commit_subject(msg.message, &p))
strbuf_addstr(&msgbuf, p);
if (opts->record_origin) {
strbuf_complete_line(&msgbuf);
if (!has_conforming_footer(&msgbuf, NULL, 0))
strbuf_addch(&msgbuf, '\n');
strbuf_addstr(&msgbuf, cherry_picked_prefix);
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)
reword = 1;
else if (is_fixup(command)) {
if (update_squash_messages(r, command, commit,
opts, item->flags))
return -1;
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))
return error(_("could not rename '%s' to '%s'"),
rebase_path_squash_msg(), dest);
unlink(git_path_merge_msg(r));
msg_file = dest;
flags |= EDIT_MSG;
}
}
if (opts->signoff && !is_fixup(command))
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") ||
!strcmp(opts->strategy, "ort") ||
command == TODO_REVERT) {
res = do_recursive_merge(r, base, next, base_label, next_label,
&head, &msgbuf, opts);
if (res < 0)
goto leave;
res |= write_message(msgbuf.buf, msgbuf.len,
git_path_merge_msg(r), 0);
} else {
struct commit_list *common = NULL;
struct commit_list *remotes = NULL;
res = write_message(msgbuf.buf, msgbuf.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, (const char **)opts->xopts,
common, oid_to_hex(&head), remotes);
free_commit_list(common);
free_commit_list(remotes);
}
strbuf_release(&msgbuf);
/*
* 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) &&
update_ref(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) &&
update_ref(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(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, 0);
unlink(git_path_merge_msg(r));
unlink(git_path_auto_merge(r));
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:
res = run_git_commit(NULL, opts, EDIT_MSG |
VERIFY_MSG | AMEND_MSG |
(flags & ALLOW_EMPTY));
*check_todo = 1;
}
}
if (!res && final_fixup) {
unlink(rebase_path_fixup_msg());
unlink(rebase_path_squash_msg());
unlink(rebase_path_current_fixups());
strbuf_reset(&opts->current_fixups);
opts->current_fixup_count = 0;
}
leave:
free_message(commit, &msg);
free(author);
update_abort_safety_file();
return res;
}