diff options
Diffstat (limited to 'sequencer.c')
-rw-r--r-- | sequencer.c | 251 |
1 files changed, 173 insertions, 78 deletions
diff --git a/sequencer.c b/sequencer.c index 16c1411054..f74dafb325 100644 --- a/sequencer.c +++ b/sequencer.c @@ -307,7 +307,7 @@ static const char *action_name(const struct replay_opts *opts) case REPLAY_INTERACTIVE_REBASE: return N_("rebase -i"); } - die(_("Unknown action: %d"), opts->action); + die(_("unknown action: %d"), opts->action); } struct commit_message { @@ -433,7 +433,7 @@ static int read_oneliner(struct strbuf *buf, 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) @@ -594,7 +594,7 @@ static int is_index_unchanged(void) 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 @@ -654,6 +654,7 @@ missing_author: strbuf_addch(&buf, *(message++)); else strbuf_addf(&buf, "'\\\\%c'", *(message++)); + strbuf_addch(&buf, '\''); res = write_message(buf.buf, buf.len, rebase_path_author_script(), 1); strbuf_release(&buf); return res; @@ -708,43 +709,51 @@ static const char *read_author_ident(struct strbuf *buf) const char *keys[] = { "GIT_AUTHOR_NAME=", "GIT_AUTHOR_EMAIL=", "GIT_AUTHOR_DATE=" }; - char *in, *out, *eol; - int i = 0, len; + struct strbuf out = STRBUF_INIT; + char *in, *eol; + const char *val[3]; + int i = 0; if (strbuf_read_file(buf, rebase_path_author_script(), 256) <= 0) return NULL; /* dequote values and construct ident line in-place */ - for (in = out = buf->buf; i < 3 && in - buf->buf < buf->len; i++) { + for (in = 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; } eol = strchrnul(in, '\n'); *eol = '\0'; - sq_dequote(in); - len = strlen(in); - - if (i > 0) /* separate values by spaces */ - *(out++) = ' '; - if (i == 1) /* email needs to be surrounded by <...> */ - *(out++) = '<'; - memmove(out, in, len); - out += len; - if (i == 1) /* email needs to be surrounded by <...> */ - *(out++) = '>'; + if (!sq_dequote(in)) { + warning(_("bad quoting on %s value in '%s'"), + keys[i], rebase_path_author_script()); + return NULL; + } + val[i] = in; in = eol + 1; } 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; } - buf->len = out - buf->buf; + /* validate date since fmt_ident() will die() on bad value */ + if (parse_date(val[2], &out)){ + warning(_("invalid date format '%s' in '%s'"), + val[2], rebase_path_author_script()); + strbuf_release(&out); + return NULL; + } + + strbuf_reset(&out); + strbuf_addstr(&out, fmt_ident(val[0], val[1], val[2], 0)); + strbuf_swap(buf, &out); + strbuf_release(&out); return buf->buf; } @@ -1101,7 +1110,7 @@ void print_commit_summary(const char *prefix, const struct object_id *oid, 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)) @@ -1176,7 +1185,7 @@ static int parse_head(struct commit **head) 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)) { @@ -1445,7 +1454,7 @@ static const char *command_to_string(const enum todo_command command) { 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) @@ -1511,7 +1520,7 @@ static int update_squash_messages(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")); @@ -2007,7 +2016,7 @@ static int parse_insn_line(struct todo_item *item, const char *bol, char *eol) if (status < 0) return -1; - item->commit = lookup_commit_reference(&commit_oid); + item->commit = lookup_commit_reference(the_repository, &commit_oid); return !item->commit; } @@ -2608,15 +2617,17 @@ static int error_with_patch(struct commit *commit, 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; @@ -2645,6 +2656,8 @@ static int do_exec(const char *command_line) 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); @@ -2728,7 +2741,7 @@ static int do_label(const char *name, int len) 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); @@ -2850,6 +2863,26 @@ static int do_reset(const char *name, int len, struct replay_opts *opts) 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) { @@ -2858,8 +2891,9 @@ static int do_merge(struct commit *commit, const char *arg, int arg_len, 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; @@ -2874,26 +2908,34 @@ static int do_merge(struct commit *commit, const char *arg, int arg_len, 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; } @@ -2904,8 +2946,13 @@ static int do_merge(struct commit *commit, const char *arg, int arg_len, * "[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; } @@ -2941,7 +2988,8 @@ static int do_merge(struct commit *commit, const char *arg, int arg_len, 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; @@ -2965,28 +3013,76 @@ static int do_merge(struct commit *commit, const char *arg, int arg_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); @@ -3049,6 +3145,7 @@ static int do_merge(struct commit *commit, const char *arg, int arg_len, leave_merge: strbuf_release(&ref_name); rollback_lock_file(&lock); + free_commit_list(to_merge); return ret; } @@ -3634,7 +3731,7 @@ int sequencer_pick_revisions(struct replay_opts *opts) 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); @@ -3905,7 +4002,6 @@ static int make_script_with_merges(struct pretty_print_context *pp, */ while ((commit = get_revision(revs))) { struct commit_list *to_merge; - int is_octopus; const char *p1, *p2; struct object_id *oid; int is_empty; @@ -3937,11 +4033,6 @@ static int make_script_with_merges(struct pretty_print_context *pp, 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) && @@ -3963,13 +4054,17 @@ static int make_script_with_merges(struct pretty_print_context *pp, 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; |