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:
commit
d97c62c828
@ -68,6 +68,8 @@ advice.*::
|
|||||||
resolveConflict::
|
resolveConflict::
|
||||||
Advice shown by various commands when conflicts
|
Advice shown by various commands when conflicts
|
||||||
prevent the operation from being performed.
|
prevent the operation from being performed.
|
||||||
|
sequencerInUse::
|
||||||
|
Advice shown when a sequencer command is already in progress.
|
||||||
implicitIdentity::
|
implicitIdentity::
|
||||||
Advice on how to set your identity configuration when
|
Advice on how to set your identity configuration when
|
||||||
your information is guessed from the system username and
|
your information is guessed from the system username and
|
||||||
|
@ -10,9 +10,7 @@ SYNOPSIS
|
|||||||
[verse]
|
[verse]
|
||||||
'git cherry-pick' [--edit] [-n] [-m parent-number] [-s] [-x] [--ff]
|
'git cherry-pick' [--edit] [-n] [-m parent-number] [-s] [-x] [--ff]
|
||||||
[-S[<keyid>]] <commit>...
|
[-S[<keyid>]] <commit>...
|
||||||
'git cherry-pick' --continue
|
'git cherry-pick' (--continue | --skip | --abort | --quit)
|
||||||
'git cherry-pick' --quit
|
|
||||||
'git cherry-pick' --abort
|
|
||||||
|
|
||||||
DESCRIPTION
|
DESCRIPTION
|
||||||
-----------
|
-----------
|
||||||
|
@ -9,9 +9,7 @@ SYNOPSIS
|
|||||||
--------
|
--------
|
||||||
[verse]
|
[verse]
|
||||||
'git revert' [--[no-]edit] [-n] [-m parent-number] [-s] [-S[<keyid>]] <commit>...
|
'git revert' [--[no-]edit] [-n] [-m parent-number] [-s] [-S[<keyid>]] <commit>...
|
||||||
'git revert' --continue
|
'git revert' (--continue | --skip | --abort | --quit)
|
||||||
'git revert' --quit
|
|
||||||
'git revert' --abort
|
|
||||||
|
|
||||||
DESCRIPTION
|
DESCRIPTION
|
||||||
-----------
|
-----------
|
||||||
|
@ -3,6 +3,10 @@
|
|||||||
`.git/sequencer`. Can be used to continue after resolving
|
`.git/sequencer`. Can be used to continue after resolving
|
||||||
conflicts in a failed cherry-pick or revert.
|
conflicts in a failed cherry-pick or revert.
|
||||||
|
|
||||||
|
--skip::
|
||||||
|
Skip the current commit and continue with the rest of the
|
||||||
|
sequence.
|
||||||
|
|
||||||
--quit::
|
--quit::
|
||||||
Forget about the current operation in progress. Can be used
|
Forget about the current operation in progress. Can be used
|
||||||
to clear the sequencer state after a failed cherry-pick or
|
to clear the sequencer state after a failed cherry-pick or
|
||||||
|
2
advice.c
2
advice.c
@ -17,6 +17,7 @@ int advice_status_ahead_behind_warning = 1;
|
|||||||
int advice_commit_before_merge = 1;
|
int advice_commit_before_merge = 1;
|
||||||
int advice_reset_quiet_warning = 1;
|
int advice_reset_quiet_warning = 1;
|
||||||
int advice_resolve_conflict = 1;
|
int advice_resolve_conflict = 1;
|
||||||
|
int advice_sequencer_in_use = 1;
|
||||||
int advice_implicit_identity = 1;
|
int advice_implicit_identity = 1;
|
||||||
int advice_detached_head = 1;
|
int advice_detached_head = 1;
|
||||||
int advice_set_upstream_failure = 1;
|
int advice_set_upstream_failure = 1;
|
||||||
@ -75,6 +76,7 @@ static struct {
|
|||||||
{ "commitBeforeMerge", &advice_commit_before_merge },
|
{ "commitBeforeMerge", &advice_commit_before_merge },
|
||||||
{ "resetQuiet", &advice_reset_quiet_warning },
|
{ "resetQuiet", &advice_reset_quiet_warning },
|
||||||
{ "resolveConflict", &advice_resolve_conflict },
|
{ "resolveConflict", &advice_resolve_conflict },
|
||||||
|
{ "sequencerInUse", &advice_sequencer_in_use },
|
||||||
{ "implicitIdentity", &advice_implicit_identity },
|
{ "implicitIdentity", &advice_implicit_identity },
|
||||||
{ "detachedHead", &advice_detached_head },
|
{ "detachedHead", &advice_detached_head },
|
||||||
{ "setupStreamFailure", &advice_set_upstream_failure },
|
{ "setupStreamFailure", &advice_set_upstream_failure },
|
||||||
|
1
advice.h
1
advice.h
@ -17,6 +17,7 @@ extern int advice_status_ahead_behind_warning;
|
|||||||
extern int advice_commit_before_merge;
|
extern int advice_commit_before_merge;
|
||||||
extern int advice_reset_quiet_warning;
|
extern int advice_reset_quiet_warning;
|
||||||
extern int advice_resolve_conflict;
|
extern int advice_resolve_conflict;
|
||||||
|
extern int advice_sequencer_in_use;
|
||||||
extern int advice_implicit_identity;
|
extern int advice_implicit_identity;
|
||||||
extern int advice_detached_head;
|
extern int advice_detached_head;
|
||||||
extern int advice_set_upstream_failure;
|
extern int advice_set_upstream_failure;
|
||||||
|
@ -60,15 +60,18 @@ N_("The previous cherry-pick is now empty, possibly due to conflict resolution.\
|
|||||||
"\n");
|
"\n");
|
||||||
|
|
||||||
static const char empty_cherry_pick_advice_single[] =
|
static const char empty_cherry_pick_advice_single[] =
|
||||||
N_("Otherwise, please use 'git reset'\n");
|
N_("Otherwise, please use 'git cherry-pick --skip'\n");
|
||||||
|
|
||||||
static const char empty_cherry_pick_advice_multi[] =
|
static const char empty_cherry_pick_advice_multi[] =
|
||||||
N_("If you wish to skip this commit, use:\n"
|
N_("and then use:\n"
|
||||||
"\n"
|
"\n"
|
||||||
" git reset\n"
|
" git cherry-pick --continue\n"
|
||||||
"\n"
|
"\n"
|
||||||
"Then \"git cherry-pick --continue\" will resume cherry-picking\n"
|
"to resume cherry-picking the remaining commits.\n"
|
||||||
"the remaining commits.\n");
|
"If you wish to skip this commit, use:\n"
|
||||||
|
"\n"
|
||||||
|
" git cherry-pick --skip\n"
|
||||||
|
"\n");
|
||||||
|
|
||||||
static const char *color_status_slots[] = {
|
static const char *color_status_slots[] = {
|
||||||
[WT_STATUS_HEADER] = "header",
|
[WT_STATUS_HEADER] = "header",
|
||||||
|
@ -102,6 +102,7 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)
|
|||||||
OPT_CMDMODE(0, "quit", &cmd, N_("end revert or cherry-pick sequence"), 'q'),
|
OPT_CMDMODE(0, "quit", &cmd, N_("end revert or cherry-pick sequence"), 'q'),
|
||||||
OPT_CMDMODE(0, "continue", &cmd, N_("resume revert or cherry-pick sequence"), 'c'),
|
OPT_CMDMODE(0, "continue", &cmd, N_("resume revert or cherry-pick sequence"), 'c'),
|
||||||
OPT_CMDMODE(0, "abort", &cmd, N_("cancel revert or cherry-pick sequence"), 'a'),
|
OPT_CMDMODE(0, "abort", &cmd, N_("cancel revert or cherry-pick sequence"), 'a'),
|
||||||
|
OPT_CMDMODE(0, "skip", &cmd, N_("skip current commit and continue"), 's'),
|
||||||
OPT_CLEANUP(&cleanup_arg),
|
OPT_CLEANUP(&cleanup_arg),
|
||||||
OPT_BOOL('n', "no-commit", &opts->no_commit, N_("don't automatically commit")),
|
OPT_BOOL('n', "no-commit", &opts->no_commit, N_("don't automatically commit")),
|
||||||
OPT_BOOL('e', "edit", &opts->edit, N_("edit the commit message")),
|
OPT_BOOL('e', "edit", &opts->edit, N_("edit the commit message")),
|
||||||
@ -151,6 +152,8 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)
|
|||||||
this_operation = "--quit";
|
this_operation = "--quit";
|
||||||
else if (cmd == 'c')
|
else if (cmd == 'c')
|
||||||
this_operation = "--continue";
|
this_operation = "--continue";
|
||||||
|
else if (cmd == 's')
|
||||||
|
this_operation = "--skip";
|
||||||
else {
|
else {
|
||||||
assert(cmd == 'a');
|
assert(cmd == 'a');
|
||||||
this_operation = "--abort";
|
this_operation = "--abort";
|
||||||
@ -210,6 +213,8 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)
|
|||||||
return sequencer_continue(the_repository, opts);
|
return sequencer_continue(the_repository, opts);
|
||||||
if (cmd == 'a')
|
if (cmd == 'a')
|
||||||
return sequencer_rollback(the_repository, opts);
|
return sequencer_rollback(the_repository, opts);
|
||||||
|
if (cmd == 's')
|
||||||
|
return sequencer_skip(the_repository, opts);
|
||||||
return sequencer_pick_revisions(the_repository, opts);
|
return sequencer_pick_revisions(the_repository, opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
134
sequencer.c
134
sequencer.c
@ -2654,15 +2654,41 @@ static int walk_revs_populate_todo(struct todo_list *todo_list,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int create_seq_dir(void)
|
static int create_seq_dir(struct repository *r)
|
||||||
{
|
{
|
||||||
if (file_exists(git_path_seq_dir())) {
|
enum replay_action action;
|
||||||
error(_("a cherry-pick or revert is already in progress"));
|
const char *in_progress_error = NULL;
|
||||||
advise(_("try \"git cherry-pick (--continue | --quit | --abort)\""));
|
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;
|
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'"),
|
return error_errno(_("could not create sequencer directory '%s'"),
|
||||||
git_path_seq_dir());
|
git_path_seq_dir());
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2713,15 +2739,20 @@ static int rollback_is_safe(void)
|
|||||||
return oideq(&actual_head, &expected_head);
|
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_array_pushl(&argv, "reset", "--merge", NULL);
|
||||||
argv[1] = "--merge";
|
|
||||||
argv[2] = oid_to_hex(oid);
|
if (!is_null_oid(oid))
|
||||||
argv[3] = NULL;
|
argv_array_push(&argv, oid_to_hex(oid));
|
||||||
return run_command_v_opt(argv, RUN_GIT_CMD);
|
|
||||||
|
ret = run_command_v_opt(argv.argv, RUN_GIT_CMD);
|
||||||
|
argv_array_clear(&argv);
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int rollback_single_pick(struct repository *r)
|
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"));
|
return error(_("cannot resolve HEAD"));
|
||||||
if (is_null_oid(&head_oid))
|
if (is_null_oid(&head_oid))
|
||||||
return error(_("cannot abort from a branch yet to be born"));
|
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)
|
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. "
|
warning(_("You seem to have moved HEAD. "
|
||||||
"Not rewinding, check your HEAD!"));
|
"Not rewinding, check your HEAD!"));
|
||||||
} else
|
} else
|
||||||
if (reset_for_rollback(&oid))
|
if (reset_merge(&oid))
|
||||||
goto fail;
|
goto fail;
|
||||||
strbuf_release(&buf);
|
strbuf_release(&buf);
|
||||||
return sequencer_remove_state(opts);
|
return sequencer_remove_state(opts);
|
||||||
@ -2787,6 +2827,70 @@ fail:
|
|||||||
return -1;
|
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)
|
static int save_todo(struct todo_list *todo_list, struct replay_opts *opts)
|
||||||
{
|
{
|
||||||
struct lock_file todo_lock = LOCK_INIT;
|
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) ||
|
if (walk_revs_populate_todo(&todo_list, opts) ||
|
||||||
create_seq_dir() < 0)
|
create_seq_dir(r) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
if (get_oid("HEAD", &oid) && (opts->action == REPLAY_REVERT))
|
if (get_oid("HEAD", &oid) && (opts->action == REPLAY_REVERT))
|
||||||
return error(_("can't revert as initial commit"));
|
return error(_("can't revert as initial commit"));
|
||||||
|
@ -129,6 +129,7 @@ int sequencer_pick_revisions(struct repository *repo,
|
|||||||
struct replay_opts *opts);
|
struct replay_opts *opts);
|
||||||
int sequencer_continue(struct repository *repo, struct replay_opts *opts);
|
int sequencer_continue(struct repository *repo, struct replay_opts *opts);
|
||||||
int sequencer_rollback(struct repository *repo, struct replay_opts *opts);
|
int sequencer_rollback(struct repository *repo, struct replay_opts *opts);
|
||||||
|
int sequencer_skip(struct repository *repo, struct replay_opts *opts);
|
||||||
int sequencer_remove_state(struct replay_opts *opts);
|
int sequencer_remove_state(struct replay_opts *opts);
|
||||||
|
|
||||||
#define TODO_LIST_KEEP_EMPTY (1U << 0)
|
#define TODO_LIST_KEEP_EMPTY (1U << 0)
|
||||||
|
@ -93,6 +93,128 @@ test_expect_success 'cherry-pick cleans up sequencer state upon success' '
|
|||||||
test_path_is_missing .git/sequencer
|
test_path_is_missing .git/sequencer
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success 'cherry-pick --skip requires cherry-pick in progress' '
|
||||||
|
pristine_detach initial &&
|
||||||
|
test_must_fail git cherry-pick --skip
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'revert --skip requires revert in progress' '
|
||||||
|
pristine_detach initial &&
|
||||||
|
test_must_fail git revert --skip
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'cherry-pick --skip to skip commit' '
|
||||||
|
pristine_detach initial &&
|
||||||
|
test_must_fail git cherry-pick anotherpick &&
|
||||||
|
test_must_fail git revert --skip &&
|
||||||
|
git cherry-pick --skip &&
|
||||||
|
test_cmp_rev initial HEAD &&
|
||||||
|
test_path_is_missing .git/CHERRY_PICK_HEAD
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'revert --skip to skip commit' '
|
||||||
|
pristine_detach anotherpick &&
|
||||||
|
test_must_fail git revert anotherpick~1 &&
|
||||||
|
test_must_fail git cherry-pick --skip &&
|
||||||
|
git revert --skip &&
|
||||||
|
test_cmp_rev anotherpick HEAD
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'skip "empty" commit' '
|
||||||
|
pristine_detach picked &&
|
||||||
|
test_commit dummy foo d &&
|
||||||
|
test_must_fail git cherry-pick anotherpick &&
|
||||||
|
git cherry-pick --skip &&
|
||||||
|
test_cmp_rev dummy HEAD
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'skip a commit and check if rest of sequence is correct' '
|
||||||
|
pristine_detach initial &&
|
||||||
|
echo e >expect &&
|
||||||
|
cat >expect.log <<-EOF &&
|
||||||
|
OBJID
|
||||||
|
:100644 100644 OBJID OBJID M foo
|
||||||
|
OBJID
|
||||||
|
:100644 100644 OBJID OBJID M foo
|
||||||
|
OBJID
|
||||||
|
:100644 100644 OBJID OBJID M unrelated
|
||||||
|
OBJID
|
||||||
|
:000000 100644 OBJID OBJID A foo
|
||||||
|
:000000 100644 OBJID OBJID A unrelated
|
||||||
|
EOF
|
||||||
|
test_must_fail git cherry-pick base..yetanotherpick &&
|
||||||
|
test_must_fail git cherry-pick --skip &&
|
||||||
|
echo d >foo &&
|
||||||
|
git add foo &&
|
||||||
|
git cherry-pick --continue &&
|
||||||
|
{
|
||||||
|
git rev-list HEAD |
|
||||||
|
git diff-tree --root --stdin |
|
||||||
|
sed "s/$OID_REGEX/OBJID/g"
|
||||||
|
} >actual.log &&
|
||||||
|
test_cmp expect foo &&
|
||||||
|
test_cmp expect.log actual.log
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'check advice when we move HEAD by committing' '
|
||||||
|
pristine_detach initial &&
|
||||||
|
cat >expect <<-EOF &&
|
||||||
|
error: there is nothing to skip
|
||||||
|
hint: have you committed already?
|
||||||
|
hint: try "git cherry-pick --continue"
|
||||||
|
fatal: cherry-pick failed
|
||||||
|
EOF
|
||||||
|
test_must_fail git cherry-pick base..yetanotherpick &&
|
||||||
|
echo c >foo &&
|
||||||
|
git commit -a &&
|
||||||
|
test_path_is_missing .git/CHERRY_PICK_HEAD &&
|
||||||
|
test_must_fail git cherry-pick --skip 2>advice &&
|
||||||
|
test_i18ncmp expect advice
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'selectively advise --skip while launching another sequence' '
|
||||||
|
pristine_detach initial &&
|
||||||
|
cat >expect <<-EOF &&
|
||||||
|
error: cherry-pick is already in progress
|
||||||
|
hint: try "git cherry-pick (--continue | --skip | --abort | --quit)"
|
||||||
|
fatal: cherry-pick failed
|
||||||
|
EOF
|
||||||
|
test_must_fail git cherry-pick picked..yetanotherpick &&
|
||||||
|
test_must_fail git cherry-pick picked..yetanotherpick 2>advice &&
|
||||||
|
test_i18ncmp expect advice &&
|
||||||
|
cat >expect <<-EOF &&
|
||||||
|
error: cherry-pick is already in progress
|
||||||
|
hint: try "git cherry-pick (--continue | --abort | --quit)"
|
||||||
|
fatal: cherry-pick failed
|
||||||
|
EOF
|
||||||
|
git reset --merge &&
|
||||||
|
test_must_fail git cherry-pick picked..yetanotherpick 2>advice &&
|
||||||
|
test_i18ncmp expect advice
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'allow skipping commit but not abort for a new history' '
|
||||||
|
pristine_detach initial &&
|
||||||
|
cat >expect <<-EOF &&
|
||||||
|
error: cannot abort from a branch yet to be born
|
||||||
|
fatal: cherry-pick failed
|
||||||
|
EOF
|
||||||
|
git checkout --orphan new_disconnected &&
|
||||||
|
git reset --hard &&
|
||||||
|
test_must_fail git cherry-pick anotherpick &&
|
||||||
|
test_must_fail git cherry-pick --abort 2>advice &&
|
||||||
|
git cherry-pick --skip &&
|
||||||
|
test_i18ncmp expect advice
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'allow skipping stopped cherry-pick because of untracked file modifications' '
|
||||||
|
pristine_detach initial &&
|
||||||
|
git rm --cached unrelated &&
|
||||||
|
git commit -m "untrack unrelated" &&
|
||||||
|
test_must_fail git cherry-pick initial base &&
|
||||||
|
test_path_is_missing .git/CHERRY_PICK_HEAD &&
|
||||||
|
git cherry-pick --skip
|
||||||
|
'
|
||||||
|
|
||||||
test_expect_success '--quit does not complain when no cherry-pick is in progress' '
|
test_expect_success '--quit does not complain when no cherry-pick is in progress' '
|
||||||
pristine_detach initial &&
|
pristine_detach initial &&
|
||||||
git cherry-pick --quit
|
git cherry-pick --quit
|
||||||
|
Loading…
Reference in New Issue
Block a user