diff options
Diffstat (limited to 'sequencer.c')
-rw-r--r-- | sequencer.c | 285 |
1 files changed, 187 insertions, 98 deletions
diff --git a/sequencer.c b/sequencer.c index b4c63ae207..614d56f5e2 100644 --- a/sequencer.c +++ b/sequencer.c @@ -164,6 +164,7 @@ static GIT_PATH_FUNC(rebase_path_strategy, "rebase-merge/strategy") static GIT_PATH_FUNC(rebase_path_strategy_opts, "rebase-merge/strategy_opts") static GIT_PATH_FUNC(rebase_path_allow_rerere_autoupdate, "rebase-merge/allow_rerere_autoupdate") static GIT_PATH_FUNC(rebase_path_reschedule_failed_exec, "rebase-merge/reschedule-failed-exec") +static GIT_PATH_FUNC(rebase_path_no_reschedule_failed_exec, "rebase-merge/no-reschedule-failed-exec") static GIT_PATH_FUNC(rebase_path_drop_redundant_commits, "rebase-merge/drop_redundant_commits") static GIT_PATH_FUNC(rebase_path_keep_redundant_commits, "rebase-merge/keep_redundant_commits") @@ -402,7 +403,7 @@ static void print_advice(struct repository *r, int show_hint, char *msg = getenv("GIT_CHERRY_PICK_HELP"); if (msg) { - fprintf(stderr, "%s\n", msg); + advise("%s\n", msg); /* * A conflict has occurred but the porcelain * (typically rebase --interactive) wants to take care @@ -417,10 +418,22 @@ static void print_advice(struct repository *r, int show_hint, if (opts->no_commit) advise(_("after resolving the conflicts, mark the corrected paths\n" "with 'git add <paths>' or 'git rm <paths>'")); + else if (opts->action == REPLAY_PICK) + advise(_("After resolving the conflicts, mark them with\n" + "\"git add/rm <pathspec>\", then run\n" + "\"git cherry-pick --continue\".\n" + "You can instead skip this commit with \"git cherry-pick --skip\".\n" + "To abort and get back to the state before \"git cherry-pick\",\n" + "run \"git cherry-pick --abort\".")); + else if (opts->action == REPLAY_REVERT) + advise(_("After resolving the conflicts, mark them with\n" + "\"git add/rm <pathspec>\", then run\n" + "\"git revert --continue\".\n" + "You can instead skip this commit with \"git revert --skip\".\n" + "To abort and get back to the state before \"git revert\",\n" + "run \"git revert --abort\".")); else - advise(_("after resolving the conflicts, mark the corrected paths\n" - "with 'git add <paths>' or 'git rm <paths>'\n" - "and commit the result with 'git commit'")); + BUG("unexpected pick action in print_advice()"); } } @@ -485,7 +498,7 @@ static int error_dirty_index(struct repository *repo, struct replay_opts *opts) error(_("your local changes would be overwritten by %s."), _(action_name(opts))); - if (advice_commit_before_merge) + if (advice_enabled(ADVICE_COMMIT_BEFORE_MERGE)) advise(_("commit your changes or stash them to proceed.")); return -1; } @@ -524,7 +537,7 @@ static int fast_forward_to(struct repository *r, if (!transaction || ref_transaction_update(transaction, "HEAD", to, unborn && !is_rebase_i(opts) ? - &null_oid : from, + null_oid() : from, 0, sb.buf, &err) || ref_transaction_commit(transaction, &err)) { ref_transaction_free(transaction); @@ -635,7 +648,7 @@ static int do_recursive_merge(struct repository *r, for (i = 0; i < opts->xopts_nr; i++) parse_merge_opt(&o, opts->xopts[i]); - if (opts->strategy && !strcmp(opts->strategy, "ort")) { + if (!opts->strategy || !strcmp(opts->strategy, "ort")) { memset(&result, 0, sizeof(result)); merge_incore_nonrecursive(&o, base_tree, head_tree, next_tree, &result); @@ -651,6 +664,7 @@ static int do_recursive_merge(struct repository *r, merge_switch_to_result(&o, head_tree, &result, 1, show_output); clean = result.clean; } else { + ensure_full_index(r->index); clean = merge_trees(&o, head_tree, next_tree, base_tree); if (is_rebase_i(opts) && clean <= 0) fputs(o.obuf.buf, stdout); @@ -982,7 +996,8 @@ static int run_git_commit(const char *defmsg, cmd.git_cmd = 1; - if (is_rebase_i(opts) && read_env_script(&cmd.env_array)) { + if (is_rebase_i(opts) && !(!defmsg && (flags & AMEND_MSG)) && + read_env_script(&cmd.env_array)) { const char *gpg_opt = gpg_sign_opt_quoted(opts); return error(_(staged_changes_advice), @@ -1131,7 +1146,7 @@ int update_head_with_reflog(const struct commit *old_head, transaction = ref_transaction_begin(err); if (!transaction || ref_transaction_update(transaction, "HEAD", new_head, - old_head ? &old_head->object.oid : &null_oid, + old_head ? &old_head->object.oid : null_oid(), 0, sb.buf, err) || ref_transaction_commit(transaction, err)) { ret = -1; @@ -1240,7 +1255,7 @@ N_("Your name and email address were configured automatically based\n" static const char *implicit_ident_advice(void) { - char *user_config = expand_user_path("~/.gitconfig", 0); + char *user_config = interpolate_path("~/.gitconfig", 0); char *xdg_config = xdg_config_home("config"); int config_exists = file_exists(user_config) || file_exists(xdg_config); @@ -1292,7 +1307,7 @@ void print_commit_summary(struct repository *r, if (!committer_ident_sufficiently_given()) { strbuf_addstr(&format, "\n Committer: "); strbuf_addbuf_percentquote(&format, &committer_ident); - if (advice_implicit_identity) { + if (advice_enabled(ADVICE_IMPLICIT_IDENTITY)) { strbuf_addch(&format, '\n'); strbuf_addstr(&format, implicit_ident_advice()); } @@ -2032,13 +2047,24 @@ static void record_in_rewritten(struct object_id *oid, flush_rewritten_pending(); } +static int should_edit(struct replay_opts *opts) { + if (opts->edit < 0) + /* + * Note that we only handle the case of non-conflicted + * commits; continue_single_pick() handles the conflicted + * commits itself instead of calling this function. + */ + return (opts->action == REPLAY_REVERT && isatty(0)) ? 1 : 0; + return opts->edit; +} + 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 = opts->edit ? EDIT_MSG : 0; - const char *msg_file = opts->edit ? NULL : git_path_merge_msg(r); + 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; @@ -2053,7 +2079,7 @@ static int do_pick_commit(struct repository *r, /* * 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 merge-recursive + * 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)) @@ -2270,6 +2296,7 @@ static int do_pick_commit(struct repository *r, 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); @@ -2333,6 +2360,7 @@ static int read_and_refresh_cache(struct repository *r, _(action_name(opts))); } refresh_index(r->index, REFRESH_QUIET|REFRESH_UNMERGED, NULL, NULL, NULL); + if (index_fd >= 0) { if (write_locked_index(r->index, &index_lock, COMMIT_LOCK | SKIP_IF_UNCHANGED)) { @@ -2340,6 +2368,13 @@ static int read_and_refresh_cache(struct repository *r, _(action_name(opts))); } } + + /* + * If we are resolving merges in any way other than "ort", then + * expand the sparse index. + */ + if (opts->strategy && strcmp(opts->strategy, "ort")) + ensure_full_index(r->index); return 0; } @@ -2633,6 +2668,8 @@ void sequencer_post_commit_cleanup(struct repository *r, int verbose) need_cleanup = 1; } + unlink(git_path_auto_merge(r)); + if (!need_cleanup) return; @@ -2854,6 +2891,8 @@ static int read_populate_opts(struct replay_opts *opts) if (file_exists(rebase_path_reschedule_failed_exec())) opts->reschedule_failed_exec = 1; + else if (file_exists(rebase_path_no_reschedule_failed_exec())) + opts->reschedule_failed_exec = 0; if (file_exists(rebase_path_drop_redundant_commits())) opts->drop_redundant_commits = 1; @@ -2954,6 +2993,8 @@ int write_basic_state(struct replay_opts *opts, const char *head_name, write_file(rebase_path_ignore_date(), "%s", ""); if (opts->reschedule_failed_exec) write_file(rebase_path_reschedule_failed_exec(), "%s", ""); + else + write_file(rebase_path_no_reschedule_failed_exec(), "%s", ""); return 0; } @@ -3022,7 +3063,7 @@ static int create_seq_dir(struct repository *r) } if (in_progress_error) { error("%s", in_progress_error); - if (advice_sequencer_in_use) + if (advice_enabled(ADVICE_SEQUENCER_IN_USE)) advise(in_progress_advice, advise_skip ? "--skip | " : ""); return -1; @@ -3226,7 +3267,7 @@ int sequencer_skip(struct repository *r, struct replay_opts *opts) give_advice: error(_("there is nothing to skip")); - if (advice_resolve_conflict) { + if (advice_enabled(ADVICE_RESOLVE_CONFLICT)) { advise(_("have you committed already?\n" "try \"git %s --continue\""), action == REPLAY_REVERT ? "revert" : "cherry-pick"); @@ -3283,9 +3324,9 @@ static int save_opts(struct replay_opts *opts) if (opts->no_commit) res |= git_config_set_in_file_gently(opts_file, "options.no-commit", "true"); - if (opts->edit) - res |= git_config_set_in_file_gently(opts_file, - "options.edit", "true"); + if (opts->edit >= 0) + res |= git_config_set_in_file_gently(opts_file, "options.edit", + opts->edit ? "true" : "false"); if (opts->allow_empty) res |= git_config_set_in_file_gently(opts_file, "options.allow-empty", "true"); @@ -3502,6 +3543,7 @@ static int do_exec(struct repository *r, const char *command_line) return status; } +__attribute__((format (printf, 2, 3))) static int safe_append(const char *filename, const char *fmt, ...) { va_list ap; @@ -3579,8 +3621,27 @@ static int do_label(struct repository *r, const char *name, int len) return ret; } +__attribute__((format (printf, 3, 4))) static const char *reflog_message(struct replay_opts *opts, - const char *sub_action, const char *fmt, ...); + const char *sub_action, const char *fmt, ...) +{ + va_list ap; + static struct strbuf buf = STRBUF_INIT; + char *reflog_action = getenv(GIT_REFLOG_ACTION); + + va_start(ap, fmt); + strbuf_reset(&buf); + strbuf_addstr(&buf, reflog_action ? reflog_action : action_name(opts)); + if (sub_action) + strbuf_addf(&buf, " (%s)", sub_action); + if (fmt) { + strbuf_addstr(&buf, ": "); + strbuf_vaddf(&buf, fmt, ap); + } + va_end(ap); + + return buf.buf; +} static int do_reset(struct repository *r, const char *name, int len, @@ -3700,10 +3761,9 @@ static struct commit *lookup_label(const char *label, int len, static int do_merge(struct repository *r, struct commit *commit, const char *arg, int arg_len, - int flags, struct replay_opts *opts) + int flags, int *check_todo, struct replay_opts *opts) { - int run_commit_flags = (flags & TODO_EDIT_MERGE_MSG) ? - EDIT_MSG | VERIFY_MSG : 0; + int run_commit_flags = 0; struct strbuf ref_name = STRBUF_INIT; struct commit *head_commit, *merge_commit, *i; struct commit_list *bases, *j, *reversed = NULL; @@ -3777,6 +3837,45 @@ static int do_merge(struct repository *r, 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); @@ -3826,46 +3925,6 @@ static int do_merge(struct repository *r, } } - /* - * 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) { - run_commit_flags |= AMEND_MSG; - goto fast_forward_edit; - } - goto leave_merge; - } - if (strategy || to_merge->next) { /* Octopus merge */ struct child_process cmd = CHILD_PROCESS_INIT; @@ -3896,7 +3955,10 @@ static int do_merge(struct repository *r, strvec_pushf(&cmd.args, "-X%s", opts->xopts[k]); } - strvec_push(&cmd.args, "--no-edit"); + 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"); @@ -3949,7 +4011,7 @@ static int do_merge(struct repository *r, o.branch2 = ref_name.buf; o.buffer_output = 2; - if (opts->strategy && !strcmp(opts->strategy, "ort")) { + if (!opts->strategy || !strcmp(opts->strategy, "ort")) { /* * TODO: Should use merge_incore_recursive() and * merge_switch_to_result(), skipping the call to @@ -3996,10 +4058,17 @@ static int do_merge(struct repository *r, * value (a negative one would indicate that the `merge` * command needs to be rescheduled). */ - fast_forward_edit: 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); @@ -4159,27 +4228,6 @@ int apply_autostash_oid(const char *stash_oid) return apply_save_autostash_oid(stash_oid, 1); } -static const char *reflog_message(struct replay_opts *opts, - const char *sub_action, const char *fmt, ...) -{ - va_list ap; - static struct strbuf buf = STRBUF_INIT; - char *reflog_action = getenv(GIT_REFLOG_ACTION); - - va_start(ap, fmt); - strbuf_reset(&buf); - strbuf_addstr(&buf, reflog_action ? reflog_action : action_name(opts)); - if (sub_action) - strbuf_addf(&buf, " (%s)", sub_action); - if (fmt) { - strbuf_addstr(&buf, ": "); - strbuf_vaddf(&buf, fmt, ap); - } - va_end(ap); - - return buf.buf; -} - static int run_git_checkout(struct repository *r, struct replay_opts *opts, const char *commit, const char *action) { @@ -4259,7 +4307,7 @@ static int pick_commits(struct repository *r, prev_reflog_action = xstrdup(getenv(GIT_REFLOG_ACTION)); if (opts->allow_ff) assert(!(opts->signoff || opts->no_commit || - opts->record_origin || opts->edit || + opts->record_origin || should_edit(opts) || opts->committer_date_is_author_date || opts->ignore_date)); if (read_and_refresh_cache(r, opts)) @@ -4293,6 +4341,7 @@ static int pick_commits(struct repository *r, unlink(rebase_path_stopped_sha()); unlink(rebase_path_amend()); unlink(git_path_merge_head(r)); + unlink(git_path_auto_merge(r)); delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF); if (item->command == TODO_BREAK) { @@ -4386,9 +4435,8 @@ static int pick_commits(struct repository *r, if ((res = do_reset(r, arg, item->arg_len, opts))) reschedule = 1; } else if (item->command == TODO_MERGE) { - if ((res = do_merge(r, item->commit, - arg, item->arg_len, - item->flags, opts)) < 0) + if ((res = do_merge(r, item->commit, arg, item->arg_len, + item->flags, &check_todo, opts)) < 0) reschedule = 1; else if (item->commit) record_in_rewritten(&item->commit->object.oid, @@ -4552,14 +4600,33 @@ cleanup_head_ref: return sequencer_remove_state(opts); } -static int continue_single_pick(struct repository *r) +static int continue_single_pick(struct repository *r, struct replay_opts *opts) { - const char *argv[] = { "commit", NULL }; + struct strvec argv = STRVEC_INIT; + int ret; if (!refs_ref_exists(get_main_ref_store(r), "CHERRY_PICK_HEAD") && !refs_ref_exists(get_main_ref_store(r), "REVERT_HEAD")) return error(_("no cherry-pick or revert in progress")); - return run_command_v_opt(argv, RUN_GIT_CMD); + + strvec_push(&argv, "commit"); + + /* + * continue_single_pick() handles the case of recovering from a + * conflict. should_edit() doesn't handle that case; for a conflict, + * we want to edit if the user asked for it, or if they didn't specify + * and stdin is a tty. + */ + if (!opts->edit || (opts->edit < 0 && !isatty(0))) + /* + * Include --cleanup=strip as well because we don't want the + * "# Conflicts:" messages. + */ + strvec_pushl(&argv, "--no-edit", "--cleanup=strip", NULL); + + ret = run_command_v_opt(argv.v, RUN_GIT_CMD); + strvec_clear(&argv); + return ret; } static int commit_staged_changes(struct repository *r, @@ -4678,6 +4745,9 @@ static int commit_staged_changes(struct repository *r, refs_delete_ref(get_main_ref_store(r), "", "CHERRY_PICK_HEAD", NULL, 0)) return error(_("could not remove CHERRY_PICK_HEAD")); + if (unlink(git_path_merge_msg(r)) && errno != ENOENT) + return error_errno(_("could not remove '%s'"), + git_path_merge_msg(r)); if (!final_fixup) return 0; } @@ -4687,6 +4757,7 @@ static int commit_staged_changes(struct repository *r, return error(_("could not commit staged changes.")); unlink(rebase_path_amend()); unlink(git_path_merge_head(r)); + unlink(git_path_auto_merge(r)); if (final_fixup) { unlink(rebase_path_fixup_msg()); unlink(rebase_path_squash_msg()); @@ -4729,7 +4800,7 @@ int sequencer_continue(struct repository *r, struct replay_opts *opts) goto release_todo_list; } } else if (!file_exists(get_todo_path(opts))) - return continue_single_pick(r); + return continue_single_pick(r, opts); else if ((res = read_populate_todo(r, &todo_list, opts))) goto release_todo_list; @@ -4738,7 +4809,7 @@ int sequencer_continue(struct repository *r, struct replay_opts *opts) if (refs_ref_exists(get_main_ref_store(r), "CHERRY_PICK_HEAD") || refs_ref_exists(get_main_ref_store(r), "REVERT_HEAD")) { - res = continue_single_pick(r); + res = continue_single_pick(r, opts); if (res) goto release_todo_list; } @@ -5060,6 +5131,7 @@ static int make_script_with_merges(struct pretty_print_context *pp, int keep_empty = flags & TODO_LIST_KEEP_EMPTY; int rebase_cousins = flags & TODO_LIST_REBASE_COUSINS; int root_with_onto = flags & TODO_LIST_ROOT_WITH_ONTO; + int skipped_commit = 0; struct strbuf buf = STRBUF_INIT, oneline = STRBUF_INIT; struct strbuf label = STRBUF_INIT; struct commit_list *commits = NULL, **tail = &commits, *iter; @@ -5110,8 +5182,13 @@ static int make_script_with_merges(struct pretty_print_context *pp, oidset_insert(&interesting, &commit->object.oid); is_empty = is_original_commit_empty(commit); - if (!is_empty && (commit->object.flags & PATCHSAME)) + if (!is_empty && (commit->object.flags & PATCHSAME)) { + if (flags & TODO_LIST_WARN_SKIPPED_CHERRY_PICKS) + warning(_("skipped previously applied commit %s"), + short_commit_name(commit)); + skipped_commit = 1; continue; + } if (is_empty && !keep_empty) continue; @@ -5175,6 +5252,9 @@ static int make_script_with_merges(struct pretty_print_context *pp, oidcpy(&entry->entry.oid, &commit->object.oid); oidmap_put(&commit2todo, entry); } + if (skipped_commit) + advise_if_enabled(ADVICE_SKIPPED_CHERRY_PICKS, + _("use --reapply-cherry-picks to include skipped commits")); /* * Second phase: @@ -5295,6 +5375,7 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc, const char *insn = flags & TODO_LIST_ABBREVIATE_CMDS ? "p" : "pick"; int rebase_merges = flags & TODO_LIST_REBASE_MERGES; int reapply_cherry_picks = flags & TODO_LIST_REAPPLY_CHERRY_PICKS; + int skipped_commit = 0; repo_init_revisions(r, &revs, NULL); revs.verbose_header = 1; @@ -5330,8 +5411,13 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc, while ((commit = get_revision(&revs))) { int is_empty = is_original_commit_empty(commit); - if (!is_empty && (commit->object.flags & PATCHSAME)) + if (!is_empty && (commit->object.flags & PATCHSAME)) { + if (flags & TODO_LIST_WARN_SKIPPED_CHERRY_PICKS) + warning(_("skipped previously applied commit %s"), + short_commit_name(commit)); + skipped_commit = 1; continue; + } if (is_empty && !keep_empty) continue; strbuf_addf(out, "%s %s ", insn, @@ -5341,6 +5427,9 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc, strbuf_addf(out, " %c empty", comment_line_char); strbuf_addch(out, '\n'); } + if (skipped_commit) + advise_if_enabled(ADVICE_SKIPPED_CHERRY_PICKS, + _("use --reapply-cherry-picks to include skipped commits")); return 0; } |