in sequencer.c [3809:4123]
static int do_merge(struct repository *r,
struct commit *commit,
const char *arg, int arg_len,
int flags, int *check_todo, struct replay_opts *opts)
{
int run_commit_flags = 0;
struct strbuf ref_name = STRBUF_INIT;
struct commit *head_commit, *merge_commit, *i;
struct commit_list *bases, *j;
struct commit_list *to_merge = NULL, **tail = &to_merge;
const char *strategy = !opts->xopts_nr &&
(!opts->strategy ||
!strcmp(opts->strategy, "recursive") ||
!strcmp(opts->strategy, "ort")) ?
NULL : opts->strategy;
struct merge_options o;
int merge_arg_len, oneline_offset, can_fast_forward, ret, k;
static struct lock_file lock;
const char *p;
if (repo_hold_locked_index(r, &lock, LOCK_REPORT_ON_ERROR) < 0) {
ret = -1;
goto leave_merge;
}
head_commit = lookup_commit_reference_by_name("HEAD");
if (!head_commit) {
ret = error(_("cannot merge without a current revision"));
goto leave_merge;
}
/*
* For octopus merges, the arg starts with the list of revisions to be
* merged. The list is optionally followed by '#' and the oneline.
*/
merge_arg_len = oneline_offset = arg_len;
for (p = arg; p - arg < arg_len; p += strspn(p, " \t\n")) {
if (!*p)
break;
if (*p == '#' && (!p[1] || isspace(p[1]))) {
p += 1 + strspn(p + 1, " \t\n");
oneline_offset = p - arg;
break;
}
k = strcspn(p, " \t\n");
if (!k)
continue;
merge_commit = lookup_label(p, k, &ref_name);
if (!merge_commit) {
ret = error(_("unable to parse '%.*s'"), k, p);
goto leave_merge;
}
tail = &commit_list_insert(merge_commit, tail)->next;
p += k;
merge_arg_len = p - arg;
}
if (!to_merge) {
ret = error(_("nothing to merge: '%.*s'"), arg_len, arg);
goto leave_merge;
}
if (opts->have_squash_onto &&
oideq(&head_commit->object.oid, &opts->squash_onto)) {
/*
* When the user tells us to "merge" something into a
* "[new root]", let's simply fast-forward to the merge head.
*/
rollback_lock_file(&lock);
if (to_merge->next)
ret = error(_("octopus merge cannot be executed on "
"top of a [new root]"));
else
ret = fast_forward_to(r, &to_merge->item->object.oid,
&head_commit->object.oid, 0,
opts);
goto leave_merge;
}
/*
* If HEAD is not identical to the first parent of the original merge
* commit, we cannot fast-forward.
*/
can_fast_forward = opts->allow_ff && commit && commit->parents &&
oideq(&commit->parents->item->object.oid,
&head_commit->object.oid);
/*
* If any merge head is different from the original one, we cannot
* fast-forward.
*/
if (can_fast_forward) {
struct commit_list *p = commit->parents->next;
for (j = to_merge; j && p; j = j->next, p = p->next)
if (!oideq(&j->item->object.oid,
&p->item->object.oid)) {
can_fast_forward = 0;
break;
}
/*
* If the number of merge heads differs from the original merge
* commit, we cannot fast-forward.
*/
if (j || p)
can_fast_forward = 0;
}
if (can_fast_forward) {
rollback_lock_file(&lock);
ret = fast_forward_to(r, &commit->object.oid,
&head_commit->object.oid, 0, opts);
if (flags & TODO_EDIT_MERGE_MSG)
goto fast_forward_edit;
goto leave_merge;
}
if (commit) {
const char *encoding = get_commit_output_encoding();
const char *message = logmsg_reencode(commit, NULL, encoding);
const char *body;
int len;
if (!message) {
ret = error(_("could not get commit message of '%s'"),
oid_to_hex(&commit->object.oid));
goto leave_merge;
}
write_author_script(message);
find_commit_subject(message, &body);
len = strlen(body);
ret = write_message(body, len, git_path_merge_msg(r), 0);
unuse_commit_buffer(commit, message);
if (ret) {
error_errno(_("could not write '%s'"),
git_path_merge_msg(r));
goto leave_merge;
}
} else {
struct strbuf buf = STRBUF_INIT;
int len;
strbuf_addf(&buf, "author %s", git_author_info(0));
write_author_script(buf.buf);
strbuf_reset(&buf);
if (oneline_offset < arg_len) {
p = arg + oneline_offset;
len = arg_len - oneline_offset;
} else {
strbuf_addf(&buf, "Merge %s '%.*s'",
to_merge->next ? "branches" : "branch",
merge_arg_len, arg);
p = buf.buf;
len = buf.len;
}
ret = write_message(p, len, git_path_merge_msg(r), 0);
strbuf_release(&buf);
if (ret) {
error_errno(_("could not write '%s'"),
git_path_merge_msg(r));
goto leave_merge;
}
}
if (strategy || to_merge->next) {
/* Octopus merge */
struct child_process cmd = CHILD_PROCESS_INIT;
if (read_env_script(&cmd.env)) {
const char *gpg_opt = gpg_sign_opt_quoted(opts);
ret = error(_(staged_changes_advice), gpg_opt, gpg_opt);
goto leave_merge;
}
if (opts->committer_date_is_author_date)
strvec_pushf(&cmd.env, "GIT_COMMITTER_DATE=%s",
opts->ignore_date ?
"" :
author_date_from_env(&cmd.env));
if (opts->ignore_date)
strvec_push(&cmd.env, "GIT_AUTHOR_DATE=");
cmd.git_cmd = 1;
strvec_push(&cmd.args, "merge");
strvec_push(&cmd.args, "-s");
if (!strategy)
strvec_push(&cmd.args, "octopus");
else {
strvec_push(&cmd.args, strategy);
for (k = 0; k < opts->xopts_nr; k++)
strvec_pushf(&cmd.args,
"-X%s", opts->xopts[k]);
}
if (!(flags & TODO_EDIT_MERGE_MSG))
strvec_push(&cmd.args, "--no-edit");
else
strvec_push(&cmd.args, "--edit");
strvec_push(&cmd.args, "--no-ff");
strvec_push(&cmd.args, "--no-log");
strvec_push(&cmd.args, "--no-stat");
strvec_push(&cmd.args, "-F");
strvec_push(&cmd.args, git_path_merge_msg(r));
if (opts->gpg_sign)
strvec_pushf(&cmd.args, "-S%s", opts->gpg_sign);
else
strvec_push(&cmd.args, "--no-gpg-sign");
/* Add the tips to be merged */
for (j = to_merge; j; j = j->next)
strvec_push(&cmd.args,
oid_to_hex(&j->item->object.oid));
strbuf_release(&ref_name);
refs_delete_ref(get_main_ref_store(r), "", "CHERRY_PICK_HEAD",
NULL, 0);
rollback_lock_file(&lock);
ret = run_command(&cmd);
/* force re-reading of the cache */
if (!ret && (discard_index(r->index) < 0 ||
repo_read_index(r) < 0))
ret = error(_("could not read index"));
goto leave_merge;
}
merge_commit = to_merge->item;
bases = get_merge_bases(head_commit, merge_commit);
if (bases && oideq(&merge_commit->object.oid,
&bases->item->object.oid)) {
ret = 0;
/* skip merging an ancestor of HEAD */
goto leave_merge;
}
write_message(oid_to_hex(&merge_commit->object.oid), the_hash_algo->hexsz,
git_path_merge_head(r), 0);
write_message("no-ff", 5, git_path_merge_mode(r), 0);
bases = reverse_commit_list(bases);
repo_read_index(r);
init_merge_options(&o, r);
o.branch1 = "HEAD";
o.branch2 = ref_name.buf;
o.buffer_output = 2;
if (!opts->strategy || !strcmp(opts->strategy, "ort")) {
/*
* TODO: Should use merge_incore_recursive() and
* merge_switch_to_result(), skipping the call to
* merge_switch_to_result() when we don't actually need to
* update the index and working copy immediately.
*/
ret = merge_ort_recursive(&o,
head_commit, merge_commit, bases,
&i);
} else {
ret = merge_recursive(&o, head_commit, merge_commit, bases,
&i);
}
if (ret <= 0)
fputs(o.obuf.buf, stdout);
strbuf_release(&o.obuf);
if (ret < 0) {
error(_("could not even attempt to merge '%.*s'"),
merge_arg_len, arg);
goto leave_merge;
}
/*
* The return value of merge_recursive() is 1 on clean, and 0 on
* unclean merge.
*
* Let's reverse that, so that do_merge() returns 0 upon success and
* 1 upon failed merge (keeping the return value -1 for the cases where
* we will want to reschedule the `merge` command).
*/
ret = !ret;
if (r->index->cache_changed &&
write_locked_index(r->index, &lock, COMMIT_LOCK)) {
ret = error(_("merge: Unable to write new index file"));
goto leave_merge;
}
rollback_lock_file(&lock);
if (ret)
repo_rerere(r, opts->allow_rerere_auto);
else
/*
* In case of problems, we now want to return a positive
* value (a negative one would indicate that the `merge`
* command needs to be rescheduled).
*/
ret = !!run_git_commit(git_path_merge_msg(r), opts,
run_commit_flags);
if (!ret && flags & TODO_EDIT_MERGE_MSG) {
fast_forward_edit:
*check_todo = 1;
run_commit_flags |= AMEND_MSG | EDIT_MSG | VERIFY_MSG;
ret = !!run_git_commit(NULL, opts, run_commit_flags);
}
leave_merge:
strbuf_release(&ref_name);
rollback_lock_file(&lock);
free_commit_list(to_merge);
return ret;
}