diff options
Diffstat (limited to 'sequencer.c')
-rw-r--r-- | sequencer.c | 302 |
1 files changed, 177 insertions, 125 deletions
diff --git a/sequencer.c b/sequencer.c index 7f07cd00f3..6abd72160c 100644 --- a/sequencer.c +++ b/sequencer.c @@ -8,6 +8,7 @@ #include "sequencer.h" #include "tag.h" #include "run-command.h" +#include "hook.h" #include "exec-cmd.h" #include "utf8.h" #include "cache-tree.h" @@ -403,7 +404,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 @@ -418,10 +419,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()"); } } @@ -486,7 +499,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; } @@ -636,7 +649,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); @@ -652,6 +665,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); @@ -983,7 +997,10 @@ 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) && + ((opts->committer_date_is_author_date && !opts->ignore_date) || + !(!defmsg && (flags & AMEND_MSG))) && + read_env_script(&cmd.env_array)) { const char *gpg_opt = gpg_sign_opt_quoted(opts); return error(_(staged_changes_advice), @@ -1147,18 +1164,14 @@ static int run_rewrite_hook(const struct object_id *oldoid, const struct object_id *newoid) { struct child_process proc = CHILD_PROCESS_INIT; - const char *argv[3]; int code; struct strbuf sb = STRBUF_INIT; + const char *hook_path = find_hook("post-rewrite"); - argv[0] = find_hook("post-rewrite"); - if (!argv[0]) + if (!hook_path) return 0; - argv[1] = "amend"; - argv[2] = NULL; - - proc.argv = argv; + strvec_pushl(&proc.args, hook_path, "amend", NULL); proc.in = -1; proc.stdout_to_stderr = 1; proc.trace2_hook_name = "post-rewrite"; @@ -1241,7 +1254,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); @@ -1267,6 +1280,8 @@ void print_commit_summary(struct repository *r, struct pretty_print_context pctx = {0}; struct strbuf author_ident = STRBUF_INIT; struct strbuf committer_ident = STRBUF_INIT; + struct ref_store *refs; + int resolve_errno; commit = lookup_commit(r, oid); if (!commit) @@ -1293,7 +1308,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()); } @@ -1316,9 +1331,13 @@ void print_commit_summary(struct repository *r, rev.diffopt.break_opt = 0; diff_setup_done(&rev.diffopt); - head = resolve_ref_unsafe("HEAD", 0, NULL, NULL); - if (!head) + refs = get_main_ref_store(the_repository); + head = refs_resolve_ref_unsafe(refs, "HEAD", 0, NULL, NULL, + &resolve_errno); + if (!head) { + errno = resolve_errno; die_errno(_("unable to resolve HEAD after creating commit")); + } if (!strcmp(head, "HEAD")) head = _("detached HEAD"); else @@ -1445,7 +1464,7 @@ static int try_to_commit(struct repository *r, } } - if (find_hook("prepare-commit-msg")) { + if (hook_exists("prepare-commit-msg")) { res = run_prepare_commit_msg_hook(r, msg, hook_commit); if (res) goto out; @@ -2065,7 +2084,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)) @@ -2346,6 +2365,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)) { @@ -2353,6 +2373,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; } @@ -2671,7 +2698,6 @@ static int read_populate_todo(struct repository *r, struct todo_list *todo_list, struct replay_opts *opts) { - struct stat st; const char *todo_file = get_todo_path(opts); int res; @@ -2679,11 +2705,6 @@ static int read_populate_todo(struct repository *r, if (strbuf_read_file_or_whine(&todo_list->buf, todo_file) < 0) return -1; - res = stat(todo_file, &st); - if (res) - return error(_("could not stat '%s'"), todo_file); - fill_stat_data(&todo_list->stat, &st); - res = todo_list_parse_insn_buffer(r, todo_list->buf.buf, todo_list); if (res) { if (is_rebase_i(opts)) @@ -3041,7 +3062,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; @@ -3245,7 +3266,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"); @@ -3476,17 +3497,12 @@ static int error_failed_squash(struct repository *r, static int do_exec(struct repository *r, const char *command_line) { - struct strvec child_env = STRVEC_INIT; const char *child_argv[] = { NULL, NULL }; int dirty, status; fprintf(stderr, _("Executing: %s\n"), command_line); child_argv[0] = command_line; - strvec_pushf(&child_env, "GIT_DIR=%s", absolute_path(get_git_dir())); - strvec_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.v); + status = run_command_v_opt(child_argv, RUN_USING_SHELL); /* force re-reading of the cache */ if (discard_index(r->index) < 0 || repo_read_index(r) < 0) @@ -3516,8 +3532,6 @@ static int do_exec(struct repository *r, const char *command_line) status = 1; } - strvec_clear(&child_env); - return status; } @@ -3628,9 +3642,9 @@ static int do_reset(struct repository *r, struct strbuf ref_name = STRBUF_INIT; struct object_id oid; struct lock_file lock = LOCK_INIT; - struct tree_desc desc; + struct tree_desc desc = { 0 }; struct tree *tree; - struct unpack_trees_options unpack_tree_opts; + struct unpack_trees_options unpack_tree_opts = { 0 }; int ret = 0; if (repo_hold_locked_index(r, &lock, LOCK_REPORT_ON_ERROR) < 0) @@ -3662,14 +3676,11 @@ static int do_reset(struct repository *r, strbuf_addf(&ref_name, "refs/rewritten/%.*s", len, name); if (get_oid(ref_name.buf, &oid) && get_oid(ref_name.buf + strlen("refs/rewritten/"), &oid)) { - error(_("could not read '%s'"), ref_name.buf); - rollback_lock_file(&lock); - strbuf_release(&ref_name); - return -1; + ret = error(_("could not read '%s'"), ref_name.buf); + goto cleanup; } } - memset(&unpack_tree_opts, 0, sizeof(unpack_tree_opts)); setup_unpack_trees_porcelain(&unpack_tree_opts, "reset"); unpack_tree_opts.head_idx = 1; unpack_tree_opts.src_index = r->index; @@ -3677,27 +3688,22 @@ static int do_reset(struct repository *r, unpack_tree_opts.fn = oneway_merge; unpack_tree_opts.merge = 1; unpack_tree_opts.update = 1; + unpack_tree_opts.preserve_ignored = 0; /* FIXME: !overwrite_ignore */ init_checkout_metadata(&unpack_tree_opts.meta, name, &oid, NULL); if (repo_read_index_unmerged(r)) { - rollback_lock_file(&lock); - strbuf_release(&ref_name); - return error_resolve_conflict(_(action_name(opts))); + ret = error_resolve_conflict(_(action_name(opts))); + goto cleanup; } if (!fill_tree_descriptor(r, &desc, &oid)) { - error(_("failed to find tree of %s"), oid_to_hex(&oid)); - rollback_lock_file(&lock); - free((void *)desc.buffer); - strbuf_release(&ref_name); - return -1; + ret = error(_("failed to find tree of %s"), oid_to_hex(&oid)); + goto cleanup; } if (unpack_trees(1, &desc, &unpack_tree_opts)) { - rollback_lock_file(&lock); - free((void *)desc.buffer); - strbuf_release(&ref_name); - return -1; + ret = -1; + goto cleanup; } tree = parse_tree_indirect(&oid); @@ -3705,14 +3711,17 @@ static int do_reset(struct repository *r, if (write_locked_index(r->index, &lock, COMMIT_LOCK) < 0) ret = error(_("could not write index")); - free((void *)desc.buffer); if (!ret) ret = update_ref(reflog_message(opts, "reset", "'%.*s'", len, name), "HEAD", &oid, NULL, 0, UPDATE_REFS_MSG_ON_ERR); - +cleanup: + free((void *)desc.buffer); + if (ret < 0) + rollback_lock_file(&lock); strbuf_release(&ref_name); + clear_unpack_trees_porcelain(&unpack_tree_opts); return ret; } @@ -3739,10 +3748,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; @@ -3816,6 +3824,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); @@ -3865,46 +3912,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; @@ -3935,7 +3942,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"); @@ -3988,7 +3998,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 @@ -4035,10 +4045,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); @@ -4206,6 +4223,8 @@ static int run_git_checkout(struct repository *r, struct replay_opts *opts, cmd.git_cmd = 1; + if (startup_info->original_cwd) + cmd.dir = startup_info->original_cwd; strvec_push(&cmd.args, "checkout"); strvec_push(&cmd.args, commit); strvec_pushf(&cmd.env_array, GIT_REFLOG_ACTION "=%s", action); @@ -4254,6 +4273,30 @@ static int stopped_at_head(struct repository *r) } +static int reread_todo_if_changed(struct repository *r, + struct todo_list *todo_list, + struct replay_opts *opts) +{ + int offset; + struct strbuf buf = STRBUF_INIT; + + if (strbuf_read_file_or_whine(&buf, get_todo_path(opts)) < 0) + return -1; + offset = get_item_line_offset(todo_list, todo_list->current + 1); + if (buf.len != todo_list->buf.len - offset || + memcmp(buf.buf, todo_list->buf.buf + offset, buf.len)) { + /* Reread the todo file if it has changed. */ + todo_list_release(todo_list); + if (read_populate_todo(r, todo_list, opts)) + return -1; /* message was printed */ + /* `current` will be incremented on return */ + todo_list->current = -1; + } + strbuf_release(&buf); + + return 0; +} + static const char rescheduled_advice[] = N_("Could not execute the todo command\n" "\n" @@ -4405,9 +4448,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, @@ -4433,20 +4475,9 @@ static int pick_commits(struct repository *r, item->commit, arg, item->arg_len, opts, res, 0); - } else if (is_rebase_i(opts) && check_todo && !res) { - struct stat st; - - if (stat(get_todo_path(opts), &st)) { - res = error_errno(_("could not stat '%s'"), - get_todo_path(opts)); - } else if (match_stat_data(&todo_list->stat, &st)) { - /* Reread the todo file if it has changed. */ - todo_list_release(todo_list); - if (read_populate_todo(r, todo_list, opts)) - res = -1; /* message was printed */ - /* `current` will be incremented below */ - todo_list->current = -1; - } + } else if (is_rebase_i(opts) && check_todo && !res && + reread_todo_if_changed(r, todo_list, opts)) { + return -1; } todo_list->current++; @@ -4716,6 +4747,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; } @@ -5099,6 +5133,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; @@ -5149,8 +5184,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; @@ -5214,6 +5254,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: @@ -5334,6 +5377,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; @@ -5369,8 +5413,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, @@ -5380,6 +5429,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; } @@ -5387,8 +5439,8 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc, * Add commands after pick and (series of) squash/fixup commands * in the todo list. */ -void todo_list_add_exec_commands(struct todo_list *todo_list, - struct string_list *commands) +static void todo_list_add_exec_commands(struct todo_list *todo_list, + struct string_list *commands) { struct strbuf *buf = &todo_list->buf; size_t base_offset = buf->len; @@ -5441,7 +5493,7 @@ void todo_list_add_exec_commands(struct todo_list *todo_list, } /* insert or append final <commands> */ - if (insert || nr == todo_list->nr) { + if (insert) { ALLOC_GROW(items, nr + commands->nr, alloc); COPY_ARRAY(items + nr, base_items, commands->nr); nr += commands->nr; |