static int do_pick_commit()

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;
}