* The file to keep track of how many commands were already processed (e.g.
* for the prompt).
*/
-static GIT_PATH_FUNC(rebase_path_msgnum, "rebase-merge/msgnum");
+static GIT_PATH_FUNC(rebase_path_msgnum, "rebase-merge/msgnum")
/*
* The file to keep track of how many commands are to be processed in total
* (e.g. for the prompt).
*/
-static GIT_PATH_FUNC(rebase_path_msgtotal, "rebase-merge/end");
+static GIT_PATH_FUNC(rebase_path_msgtotal, "rebase-merge/end")
/*
* The commit message that is planned to be used for any changes that
* need to be committed following a user interaction.
case REPLAY_INTERACTIVE_REBASE:
return N_("rebase -i");
}
- die(_("Unknown action: %d"), opts->action);
+ die(_("unknown action: %d"), opts->action);
}
struct commit_message {
static struct tree *empty_tree(void)
{
- return lookup_tree(the_hash_algo->empty_tree);
+ return lookup_tree(the_repository, the_repository->hash_algo->empty_tree);
}
static int error_dirty_index(struct replay_opts *opts)
if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL))
return error(_("could not resolve HEAD commit"));
- head_commit = lookup_commit(&head_oid);
+ head_commit = lookup_commit(the_repository, &head_oid);
/*
* If head_commit is NULL, check_commit, called from
/* dequote values and construct ident line in-place */
for (in = out = buf->buf; i < 3 && in - buf->buf < buf->len; i++) {
if (!skip_prefix(in, keys[i], (const char **)&in)) {
- warning("could not parse '%s' (looking for '%s'",
+ warning(_("could not parse '%s' (looking for '%s'"),
rebase_path_author_script(), keys[i]);
return NULL;
}
}
if (i < 3) {
- warning("could not parse '%s' (looking for '%s')",
+ warning(_("could not parse '%s' (looking for '%s')"),
rebase_path_author_script(), keys[i]);
return NULL;
}
struct strbuf author_ident = STRBUF_INIT;
struct strbuf committer_ident = STRBUF_INIT;
- commit = lookup_commit(oid);
+ commit = lookup_commit(the_repository, oid);
if (!commit)
die(_("couldn't look up newly created commit"));
if (parse_commit(commit))
if (get_oid("HEAD", &oid)) {
current_head = NULL;
} else {
- current_head = lookup_commit_reference(&oid);
+ current_head = lookup_commit_reference(the_repository, &oid);
if (!current_head)
return error(_("could not parse HEAD"));
if (oidcmp(&oid, ¤t_head->object.oid)) {
{
if (command < TODO_COMMENT)
return todo_command_info[command].str;
- die("Unknown command: %d", command);
+ die(_("unknown command: %d"), command);
}
static char command_to_char(const enum todo_command command)
if (get_oid("HEAD", &head))
return error(_("need a HEAD to fixup"));
- if (!(head_commit = lookup_commit_reference(&head)))
+ if (!(head_commit = lookup_commit_reference(the_repository, &head)))
return error(_("could not read HEAD"));
if (!(head_message = get_commit_buffer(head_commit, NULL)))
return error(_("could not read HEAD's commit message"));
if (prepare_revision_walk(opts->revs))
return error(_("revision walk setup failed"));
- if (!opts->revs->commits)
- return error(_("empty commit set passed"));
return 0;
}
if (status < 0)
return -1;
- item->commit = lookup_commit_reference(&commit_oid);
+ item->commit = lookup_commit_reference(the_repository, &commit_oid);
return !item->commit;
}
static void read_strategy_opts(struct replay_opts *opts, struct strbuf *buf)
{
int i;
+ char *strategy_opts_string;
strbuf_reset(buf);
if (!read_oneliner(buf, rebase_path_strategy(), 0))
if (!read_oneliner(buf, rebase_path_strategy_opts(), 0))
return;
- opts->xopts_nr = split_cmdline(buf->buf, (const char ***)&opts->xopts);
+ strategy_opts_string = buf->buf;
+ if (*strategy_opts_string == ' ')
+ strategy_opts_string++;
+ opts->xopts_nr = split_cmdline(strategy_opts_string,
+ (const char ***)&opts->xopts);
for (i = 0; i < opts->xopts_nr; i++) {
const char *arg = opts->xopts[i];
short_commit_name(commit), subject_len, subject);
unuse_commit_buffer(commit, commit_buffer);
}
+
+ if (!todo_list->nr)
+ return error(_("empty commit set passed"));
+
return 0;
}
if (intend_to_amend())
return -1;
- fprintf(stderr, "You can amend the commit now, with\n"
- "\n"
- " git commit --amend %s\n"
- "\n"
- "Once you are satisfied with your changes, run\n"
- "\n"
- " git rebase --continue\n", gpg_sign_opt_quoted(opts));
+ fprintf(stderr,
+ _("You can amend the commit now, with\n"
+ "\n"
+ " git commit --amend %s\n"
+ "\n"
+ "Once you are satisfied with your changes, run\n"
+ "\n"
+ " git rebase --continue\n"),
+ gpg_sign_opt_quoted(opts));
} else if (exit_code)
- fprintf(stderr, "Could not apply %s... %.*s\n",
+ fprintf_ln(stderr, _("Could not apply %s... %.*s"),
short_commit_name(commit), subject_len, subject);
return exit_code;
fprintf(stderr, "Executing: %s\n", command_line);
child_argv[0] = command_line;
argv_array_pushf(&child_env, "GIT_DIR=%s", absolute_path(get_git_dir()));
+ argv_array_pushf(&child_env, "GIT_WORK_TREE=%s",
+ absolute_path(get_git_work_tree()));
status = run_command_v_opt_cd_env(child_argv, RUN_USING_SHELL, NULL,
child_env.argv);
struct object_id head_oid;
if (len == 1 && *name == '#')
- return error("Illegal label name: '%.*s'", len, name);
+ return error(_("illegal label name: '%.*s'"), len, name);
strbuf_addf(&ref_name, "refs/rewritten/%.*s", len, name);
strbuf_addf(&msg, "rebase -i (label) '%.*s'", len, name);
return ret;
}
+static struct commit *lookup_label(const char *label, int len,
+ struct strbuf *buf)
+{
+ struct commit *commit;
+
+ strbuf_reset(buf);
+ strbuf_addf(buf, "refs/rewritten/%.*s", len, label);
+ commit = lookup_commit_reference_by_name(buf->buf);
+ if (!commit) {
+ /* fall back to non-rewritten ref or commit */
+ strbuf_splice(buf, 0, strlen("refs/rewritten/"), "", 0);
+ commit = lookup_commit_reference_by_name(buf->buf);
+ }
+
+ if (!commit)
+ error(_("could not resolve '%s'"), buf->buf);
+
+ return commit;
+}
+
static int do_merge(struct commit *commit, const char *arg, int arg_len,
int flags, struct replay_opts *opts)
{
struct strbuf ref_name = STRBUF_INIT;
struct commit *head_commit, *merge_commit, *i;
struct commit_list *bases, *j, *reversed = NULL;
+ struct commit_list *to_merge = NULL, **tail = &to_merge;
struct merge_options o;
- int merge_arg_len, oneline_offset, can_fast_forward, ret;
+ int merge_arg_len, oneline_offset, can_fast_forward, ret, k;
static struct lock_file lock;
const char *p;
goto leave_merge;
}
- oneline_offset = arg_len;
- merge_arg_len = strcspn(arg, " \t\n");
- p = arg + merge_arg_len;
- p += strspn(p, " \t\n");
- if (*p == '#' && (!p[1] || isspace(p[1]))) {
- p += 1 + strspn(p + 1, " \t\n");
- oneline_offset = p - arg;
- } else if (p - arg < arg_len)
- BUG("octopus merges are not supported yet: '%s'", p);
-
- strbuf_addf(&ref_name, "refs/rewritten/%.*s", merge_arg_len, arg);
- merge_commit = lookup_commit_reference_by_name(ref_name.buf);
- if (!merge_commit) {
- /* fall back to non-rewritten ref or commit */
- strbuf_splice(&ref_name, 0, strlen("refs/rewritten/"), "", 0);
- merge_commit = lookup_commit_reference_by_name(ref_name.buf);
+ /*
+ * 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 (!merge_commit) {
- ret = error(_("could not resolve '%s'"), ref_name.buf);
+ if (!to_merge) {
+ ret = error(_("nothing to merge: '%.*s'"), arg_len, arg);
goto leave_merge;
}
* "[new root]", let's simply fast-forward to the merge head.
*/
rollback_lock_file(&lock);
- ret = fast_forward_to(&merge_commit->object.oid,
- &head_commit->object.oid, 0, opts);
+ if (to_merge->next)
+ ret = error(_("octopus merge cannot be executed on "
+ "top of a [new root]"));
+ else
+ ret = fast_forward_to(&to_merge->item->object.oid,
+ &head_commit->object.oid, 0,
+ opts);
goto leave_merge;
}
p = arg + oneline_offset;
len = arg_len - oneline_offset;
} else {
- strbuf_addf(&buf, "Merge branch '%.*s'",
+ strbuf_addf(&buf, "Merge %s '%.*s'",
+ to_merge->next ? "branches" : "branch",
merge_arg_len, arg);
p = buf.buf;
len = buf.len;
&head_commit->object.oid);
/*
- * If the merge head is different from the original one, we cannot
+ * If any merge head is different from the original one, we cannot
* fast-forward.
*/
if (can_fast_forward) {
- struct commit_list *second_parent = commit->parents->next;
+ struct commit_list *p = commit->parents->next;
- if (second_parent && !second_parent->next &&
- oidcmp(&merge_commit->object.oid,
- &second_parent->item->object.oid))
+ for (j = to_merge; j && p; j = j->next, p = p->next)
+ if (oidcmp(&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 && commit->parents->next &&
- !commit->parents->next->next &&
- !oidcmp(&commit->parents->next->item->object.oid,
- &merge_commit->object.oid)) {
+ if (can_fast_forward) {
rollback_lock_file(&lock);
ret = fast_forward_to(&commit->object.oid,
&head_commit->object.oid, 0, opts);
goto leave_merge;
}
+ if (to_merge->next) {
+ /* Octopus merge */
+ struct child_process cmd = CHILD_PROCESS_INIT;
+
+ if (read_env_script(&cmd.env_array)) {
+ const char *gpg_opt = gpg_sign_opt_quoted(opts);
+
+ ret = error(_(staged_changes_advice), gpg_opt, gpg_opt);
+ goto leave_merge;
+ }
+
+ cmd.git_cmd = 1;
+ argv_array_push(&cmd.args, "merge");
+ argv_array_push(&cmd.args, "-s");
+ argv_array_push(&cmd.args, "octopus");
+ argv_array_push(&cmd.args, "--no-edit");
+ argv_array_push(&cmd.args, "--no-ff");
+ argv_array_push(&cmd.args, "--no-log");
+ argv_array_push(&cmd.args, "--no-stat");
+ argv_array_push(&cmd.args, "-F");
+ argv_array_push(&cmd.args, git_path_merge_msg(the_repository));
+ if (opts->gpg_sign)
+ argv_array_push(&cmd.args, opts->gpg_sign);
+
+ /* Add the tips to be merged */
+ for (j = to_merge; j; j = j->next)
+ argv_array_push(&cmd.args,
+ oid_to_hex(&j->item->object.oid));
+
+ strbuf_release(&ref_name);
+ unlink(git_path_cherry_pick_head(the_repository));
+ rollback_lock_file(&lock);
+
+ rollback_lock_file(&lock);
+ ret = run_command(&cmd);
+
+ /* force re-reading of the cache */
+ if (!ret && (discard_cache() < 0 || read_cache() < 0))
+ ret = error(_("could not read index"));
+ goto leave_merge;
+ }
+
+ merge_commit = to_merge->item;
write_message(oid_to_hex(&merge_commit->object.oid), GIT_SHA1_HEXSZ,
git_path_merge_head(the_repository), 0);
write_message("no-ff", 5, git_path_merge_mode(the_repository), 0);
leave_merge:
strbuf_release(&ref_name);
rollback_lock_file(&lock);
+ free_commit_list(to_merge);
return ret;
}
continue;
if (!get_oid(name, &oid)) {
- if (!lookup_commit_reference_gently(&oid, 1)) {
+ if (!lookup_commit_reference_gently(the_repository, &oid, 1)) {
enum object_type type = oid_object_info(the_repository,
&oid,
NULL);
if (prepare_revision_walk(opts->revs))
return error(_("revision walk setup failed"));
cmit = get_revision(opts->revs);
- if (!cmit || get_revision(opts->revs))
- return error("BUG: expected exactly one commit from walk");
+ if (!cmit)
+ return error(_("empty commit set passed"));
+ if (get_revision(opts->revs))
+ BUG("unexpected extra commit from walk");
return single_pick(cmit, opts);
}
*/
while ((commit = get_revision(revs))) {
struct commit_list *to_merge;
- int is_octopus;
const char *p1, *p2;
struct object_id *oid;
int is_empty;
continue;
}
- is_octopus = to_merge && to_merge->next;
-
- if (is_octopus)
- BUG("Octopus merges not yet supported");
-
/* Create a label */
strbuf_reset(&label);
if (skip_prefix(oneline.buf, "Merge ", &p1) &&
strbuf_addf(&buf, "%s -C %s",
cmd_merge, oid_to_hex(&commit->object.oid));
- /* label the tip of merged branch */
- oid = &to_merge->item->object.oid;
- strbuf_addch(&buf, ' ');
+ /* label the tips of merged branches */
+ for (; to_merge; to_merge = to_merge->next) {
+ oid = &to_merge->item->object.oid;
+ strbuf_addch(&buf, ' ');
+
+ if (!oidset_contains(&interesting, oid)) {
+ strbuf_addstr(&buf, label_oid(oid, NULL,
+ &state));
+ continue;
+ }
- if (!oidset_contains(&interesting, oid))
- strbuf_addstr(&buf, label_oid(oid, NULL, &state));
- else {
tips_tail = &commit_list_insert(to_merge->item,
tips_tail)->next;
entry = oidmap_get(&state.commit2label, &commit->object.oid);
if (entry)
- fprintf(out, "\n# Branch %s\n", entry->string);
+ fprintf(out, "\n%c Branch %s\n", comment_line_char, entry->string);
else
fprintf(out, "\n");