Merge branch 'ra/cherry-pick-revert-skip'
"git cherry-pick/revert" learned a new "--skip" action. * ra/cherry-pick-revert-skip: cherry-pick/revert: advise using --skip cherry-pick/revert: add --skip option sequencer: use argv_array in reset_merge sequencer: rename reset_for_rollback to reset_merge sequencer: add advice for revert
This commit is contained in:
134
sequencer.c
134
sequencer.c
@ -2654,15 +2654,41 @@ static int walk_revs_populate_todo(struct todo_list *todo_list,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int create_seq_dir(void)
|
||||
static int create_seq_dir(struct repository *r)
|
||||
{
|
||||
if (file_exists(git_path_seq_dir())) {
|
||||
error(_("a cherry-pick or revert is already in progress"));
|
||||
advise(_("try \"git cherry-pick (--continue | --quit | --abort)\""));
|
||||
enum replay_action action;
|
||||
const char *in_progress_error = NULL;
|
||||
const char *in_progress_advice = NULL;
|
||||
unsigned int advise_skip = file_exists(git_path_revert_head(r)) ||
|
||||
file_exists(git_path_cherry_pick_head(r));
|
||||
|
||||
if (!sequencer_get_last_command(r, &action)) {
|
||||
switch (action) {
|
||||
case REPLAY_REVERT:
|
||||
in_progress_error = _("revert is already in progress");
|
||||
in_progress_advice =
|
||||
_("try \"git revert (--continue | %s--abort | --quit)\"");
|
||||
break;
|
||||
case REPLAY_PICK:
|
||||
in_progress_error = _("cherry-pick is already in progress");
|
||||
in_progress_advice =
|
||||
_("try \"git cherry-pick (--continue | %s--abort | --quit)\"");
|
||||
break;
|
||||
default:
|
||||
BUG("unexpected action in create_seq_dir");
|
||||
}
|
||||
}
|
||||
if (in_progress_error) {
|
||||
error("%s", in_progress_error);
|
||||
if (advice_sequencer_in_use)
|
||||
advise(in_progress_advice,
|
||||
advise_skip ? "--skip | " : "");
|
||||
return -1;
|
||||
} else if (mkdir(git_path_seq_dir(), 0777) < 0)
|
||||
}
|
||||
if (mkdir(git_path_seq_dir(), 0777) < 0)
|
||||
return error_errno(_("could not create sequencer directory '%s'"),
|
||||
git_path_seq_dir());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -2713,15 +2739,20 @@ static int rollback_is_safe(void)
|
||||
return oideq(&actual_head, &expected_head);
|
||||
}
|
||||
|
||||
static int reset_for_rollback(const struct object_id *oid)
|
||||
static int reset_merge(const struct object_id *oid)
|
||||
{
|
||||
const char *argv[4]; /* reset --merge <arg> + NULL */
|
||||
int ret;
|
||||
struct argv_array argv = ARGV_ARRAY_INIT;
|
||||
|
||||
argv[0] = "reset";
|
||||
argv[1] = "--merge";
|
||||
argv[2] = oid_to_hex(oid);
|
||||
argv[3] = NULL;
|
||||
return run_command_v_opt(argv, RUN_GIT_CMD);
|
||||
argv_array_pushl(&argv, "reset", "--merge", NULL);
|
||||
|
||||
if (!is_null_oid(oid))
|
||||
argv_array_push(&argv, oid_to_hex(oid));
|
||||
|
||||
ret = run_command_v_opt(argv.argv, RUN_GIT_CMD);
|
||||
argv_array_clear(&argv);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rollback_single_pick(struct repository *r)
|
||||
@ -2735,7 +2766,16 @@ static int rollback_single_pick(struct repository *r)
|
||||
return error(_("cannot resolve HEAD"));
|
||||
if (is_null_oid(&head_oid))
|
||||
return error(_("cannot abort from a branch yet to be born"));
|
||||
return reset_for_rollback(&head_oid);
|
||||
return reset_merge(&head_oid);
|
||||
}
|
||||
|
||||
static int skip_single_pick(void)
|
||||
{
|
||||
struct object_id head;
|
||||
|
||||
if (read_ref_full("HEAD", 0, &head, NULL))
|
||||
return error(_("cannot resolve HEAD"));
|
||||
return reset_merge(&head);
|
||||
}
|
||||
|
||||
int sequencer_rollback(struct repository *r, struct replay_opts *opts)
|
||||
@ -2778,7 +2818,7 @@ int sequencer_rollback(struct repository *r, struct replay_opts *opts)
|
||||
warning(_("You seem to have moved HEAD. "
|
||||
"Not rewinding, check your HEAD!"));
|
||||
} else
|
||||
if (reset_for_rollback(&oid))
|
||||
if (reset_merge(&oid))
|
||||
goto fail;
|
||||
strbuf_release(&buf);
|
||||
return sequencer_remove_state(opts);
|
||||
@ -2787,6 +2827,70 @@ fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
int sequencer_skip(struct repository *r, struct replay_opts *opts)
|
||||
{
|
||||
enum replay_action action = -1;
|
||||
sequencer_get_last_command(r, &action);
|
||||
|
||||
/*
|
||||
* Check whether the subcommand requested to skip the commit is actually
|
||||
* in progress and that it's safe to skip the commit.
|
||||
*
|
||||
* opts->action tells us which subcommand requested to skip the commit.
|
||||
* If the corresponding .git/<ACTION>_HEAD exists, we know that the
|
||||
* action is in progress and we can skip the commit.
|
||||
*
|
||||
* Otherwise we check that the last instruction was related to the
|
||||
* particular subcommand we're trying to execute and barf if that's not
|
||||
* the case.
|
||||
*
|
||||
* Finally we check that the rollback is "safe", i.e., has the HEAD
|
||||
* moved? In this case, it doesn't make sense to "reset the merge" and
|
||||
* "skip the commit" as the user already handled this by committing. But
|
||||
* we'd not want to barf here, instead give advice on how to proceed. We
|
||||
* only need to check that when .git/<ACTION>_HEAD doesn't exist because
|
||||
* it gets removed when the user commits, so if it still exists we're
|
||||
* sure the user can't have committed before.
|
||||
*/
|
||||
switch (opts->action) {
|
||||
case REPLAY_REVERT:
|
||||
if (!file_exists(git_path_revert_head(r))) {
|
||||
if (action != REPLAY_REVERT)
|
||||
return error(_("no revert in progress"));
|
||||
if (!rollback_is_safe())
|
||||
goto give_advice;
|
||||
}
|
||||
break;
|
||||
case REPLAY_PICK:
|
||||
if (!file_exists(git_path_cherry_pick_head(r))) {
|
||||
if (action != REPLAY_PICK)
|
||||
return error(_("no cherry-pick in progress"));
|
||||
if (!rollback_is_safe())
|
||||
goto give_advice;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
BUG("unexpected action in sequencer_skip");
|
||||
}
|
||||
|
||||
if (skip_single_pick())
|
||||
return error(_("failed to skip the commit"));
|
||||
if (!is_directory(git_path_seq_dir()))
|
||||
return 0;
|
||||
|
||||
return sequencer_continue(r, opts);
|
||||
|
||||
give_advice:
|
||||
error(_("there is nothing to skip"));
|
||||
|
||||
if (advice_resolve_conflict) {
|
||||
advise(_("have you committed already?\n"
|
||||
"try \"git %s --continue\""),
|
||||
action == REPLAY_REVERT ? "revert" : "cherry-pick");
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int save_todo(struct todo_list *todo_list, struct replay_opts *opts)
|
||||
{
|
||||
struct lock_file todo_lock = LOCK_INIT;
|
||||
@ -4257,7 +4361,7 @@ int sequencer_pick_revisions(struct repository *r,
|
||||
*/
|
||||
|
||||
if (walk_revs_populate_todo(&todo_list, opts) ||
|
||||
create_seq_dir() < 0)
|
||||
create_seq_dir(r) < 0)
|
||||
return -1;
|
||||
if (get_oid("HEAD", &oid) && (opts->action == REPLAY_REVERT))
|
||||
return error(_("can't revert as initial commit"));
|
||||
|
||||
Reference in New Issue
Block a user