Skip to content

Commit d97c62c

Browse files
committed
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
2 parents b4b8c35 + dcb500d commit d97c62c

11 files changed

+266
-26
lines changed

Documentation/config/advice.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ advice.*::
6868
resolveConflict::
6969
Advice shown by various commands when conflicts
7070
prevent the operation from being performed.
71+
sequencerInUse::
72+
Advice shown when a sequencer command is already in progress.
7173
implicitIdentity::
7274
Advice on how to set your identity configuration when
7375
your information is guessed from the system username and

Documentation/git-cherry-pick.txt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,7 @@ SYNOPSIS
1010
[verse]
1111
'git cherry-pick' [--edit] [-n] [-m parent-number] [-s] [-x] [--ff]
1212
[-S[<keyid>]] <commit>...
13-
'git cherry-pick' --continue
14-
'git cherry-pick' --quit
15-
'git cherry-pick' --abort
13+
'git cherry-pick' (--continue | --skip | --abort | --quit)
1614

1715
DESCRIPTION
1816
-----------

Documentation/git-revert.txt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,7 @@ SYNOPSIS
99
--------
1010
[verse]
1111
'git revert' [--[no-]edit] [-n] [-m parent-number] [-s] [-S[<keyid>]] <commit>...
12-
'git revert' --continue
13-
'git revert' --quit
14-
'git revert' --abort
12+
'git revert' (--continue | --skip | --abort | --quit)
1513

1614
DESCRIPTION
1715
-----------

Documentation/sequencer.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
`.git/sequencer`. Can be used to continue after resolving
44
conflicts in a failed cherry-pick or revert.
55

6+
--skip::
7+
Skip the current commit and continue with the rest of the
8+
sequence.
9+
610
--quit::
711
Forget about the current operation in progress. Can be used
812
to clear the sequencer state after a failed cherry-pick or

advice.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ int advice_status_ahead_behind_warning = 1;
1717
int advice_commit_before_merge = 1;
1818
int advice_reset_quiet_warning = 1;
1919
int advice_resolve_conflict = 1;
20+
int advice_sequencer_in_use = 1;
2021
int advice_implicit_identity = 1;
2122
int advice_detached_head = 1;
2223
int advice_set_upstream_failure = 1;
@@ -75,6 +76,7 @@ static struct {
7576
{ "commitBeforeMerge", &advice_commit_before_merge },
7677
{ "resetQuiet", &advice_reset_quiet_warning },
7778
{ "resolveConflict", &advice_resolve_conflict },
79+
{ "sequencerInUse", &advice_sequencer_in_use },
7880
{ "implicitIdentity", &advice_implicit_identity },
7981
{ "detachedHead", &advice_detached_head },
8082
{ "setupStreamFailure", &advice_set_upstream_failure },

advice.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ extern int advice_status_ahead_behind_warning;
1717
extern int advice_commit_before_merge;
1818
extern int advice_reset_quiet_warning;
1919
extern int advice_resolve_conflict;
20+
extern int advice_sequencer_in_use;
2021
extern int advice_implicit_identity;
2122
extern int advice_detached_head;
2223
extern int advice_set_upstream_failure;

builtin/commit.c

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,15 +60,18 @@ N_("The previous cherry-pick is now empty, possibly due to conflict resolution.\
6060
"\n");
6161

6262
static const char empty_cherry_pick_advice_single[] =
63-
N_("Otherwise, please use 'git reset'\n");
63+
N_("Otherwise, please use 'git cherry-pick --skip'\n");
6464

6565
static const char empty_cherry_pick_advice_multi[] =
66-
N_("If you wish to skip this commit, use:\n"
66+
N_("and then use:\n"
6767
"\n"
68-
" git reset\n"
68+
" git cherry-pick --continue\n"
6969
"\n"
70-
"Then \"git cherry-pick --continue\" will resume cherry-picking\n"
71-
"the remaining commits.\n");
70+
"to resume cherry-picking the remaining commits.\n"
71+
"If you wish to skip this commit, use:\n"
72+
"\n"
73+
" git cherry-pick --skip\n"
74+
"\n");
7275

7376
static const char *color_status_slots[] = {
7477
[WT_STATUS_HEADER] = "header",

builtin/revert.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)
102102
OPT_CMDMODE(0, "quit", &cmd, N_("end revert or cherry-pick sequence"), 'q'),
103103
OPT_CMDMODE(0, "continue", &cmd, N_("resume revert or cherry-pick sequence"), 'c'),
104104
OPT_CMDMODE(0, "abort", &cmd, N_("cancel revert or cherry-pick sequence"), 'a'),
105+
OPT_CMDMODE(0, "skip", &cmd, N_("skip current commit and continue"), 's'),
105106
OPT_CLEANUP(&cleanup_arg),
106107
OPT_BOOL('n', "no-commit", &opts->no_commit, N_("don't automatically commit")),
107108
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)
151152
this_operation = "--quit";
152153
else if (cmd == 'c')
153154
this_operation = "--continue";
155+
else if (cmd == 's')
156+
this_operation = "--skip";
154157
else {
155158
assert(cmd == 'a');
156159
this_operation = "--abort";
@@ -210,6 +213,8 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)
210213
return sequencer_continue(the_repository, opts);
211214
if (cmd == 'a')
212215
return sequencer_rollback(the_repository, opts);
216+
if (cmd == 's')
217+
return sequencer_skip(the_repository, opts);
213218
return sequencer_pick_revisions(the_repository, opts);
214219
}
215220

sequencer.c

Lines changed: 119 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2654,15 +2654,41 @@ static int walk_revs_populate_todo(struct todo_list *todo_list,
26542654
return 0;
26552655
}
26562656

2657-
static int create_seq_dir(void)
2657+
static int create_seq_dir(struct repository *r)
26582658
{
2659-
if (file_exists(git_path_seq_dir())) {
2660-
error(_("a cherry-pick or revert is already in progress"));
2661-
advise(_("try \"git cherry-pick (--continue | --quit | --abort)\""));
2659+
enum replay_action action;
2660+
const char *in_progress_error = NULL;
2661+
const char *in_progress_advice = NULL;
2662+
unsigned int advise_skip = file_exists(git_path_revert_head(r)) ||
2663+
file_exists(git_path_cherry_pick_head(r));
2664+
2665+
if (!sequencer_get_last_command(r, &action)) {
2666+
switch (action) {
2667+
case REPLAY_REVERT:
2668+
in_progress_error = _("revert is already in progress");
2669+
in_progress_advice =
2670+
_("try \"git revert (--continue | %s--abort | --quit)\"");
2671+
break;
2672+
case REPLAY_PICK:
2673+
in_progress_error = _("cherry-pick is already in progress");
2674+
in_progress_advice =
2675+
_("try \"git cherry-pick (--continue | %s--abort | --quit)\"");
2676+
break;
2677+
default:
2678+
BUG("unexpected action in create_seq_dir");
2679+
}
2680+
}
2681+
if (in_progress_error) {
2682+
error("%s", in_progress_error);
2683+
if (advice_sequencer_in_use)
2684+
advise(in_progress_advice,
2685+
advise_skip ? "--skip | " : "");
26622686
return -1;
2663-
} else if (mkdir(git_path_seq_dir(), 0777) < 0)
2687+
}
2688+
if (mkdir(git_path_seq_dir(), 0777) < 0)
26642689
return error_errno(_("could not create sequencer directory '%s'"),
26652690
git_path_seq_dir());
2691+
26662692
return 0;
26672693
}
26682694

@@ -2713,15 +2739,20 @@ static int rollback_is_safe(void)
27132739
return oideq(&actual_head, &expected_head);
27142740
}
27152741

2716-
static int reset_for_rollback(const struct object_id *oid)
2742+
static int reset_merge(const struct object_id *oid)
27172743
{
2718-
const char *argv[4]; /* reset --merge <arg> + NULL */
2744+
int ret;
2745+
struct argv_array argv = ARGV_ARRAY_INIT;
27192746

2720-
argv[0] = "reset";
2721-
argv[1] = "--merge";
2722-
argv[2] = oid_to_hex(oid);
2723-
argv[3] = NULL;
2724-
return run_command_v_opt(argv, RUN_GIT_CMD);
2747+
argv_array_pushl(&argv, "reset", "--merge", NULL);
2748+
2749+
if (!is_null_oid(oid))
2750+
argv_array_push(&argv, oid_to_hex(oid));
2751+
2752+
ret = run_command_v_opt(argv.argv, RUN_GIT_CMD);
2753+
argv_array_clear(&argv);
2754+
2755+
return ret;
27252756
}
27262757

27272758
static int rollback_single_pick(struct repository *r)
@@ -2735,7 +2766,16 @@ static int rollback_single_pick(struct repository *r)
27352766
return error(_("cannot resolve HEAD"));
27362767
if (is_null_oid(&head_oid))
27372768
return error(_("cannot abort from a branch yet to be born"));
2738-
return reset_for_rollback(&head_oid);
2769+
return reset_merge(&head_oid);
2770+
}
2771+
2772+
static int skip_single_pick(void)
2773+
{
2774+
struct object_id head;
2775+
2776+
if (read_ref_full("HEAD", 0, &head, NULL))
2777+
return error(_("cannot resolve HEAD"));
2778+
return reset_merge(&head);
27392779
}
27402780

27412781
int sequencer_rollback(struct repository *r, struct replay_opts *opts)
@@ -2778,7 +2818,7 @@ int sequencer_rollback(struct repository *r, struct replay_opts *opts)
27782818
warning(_("You seem to have moved HEAD. "
27792819
"Not rewinding, check your HEAD!"));
27802820
} else
2781-
if (reset_for_rollback(&oid))
2821+
if (reset_merge(&oid))
27822822
goto fail;
27832823
strbuf_release(&buf);
27842824
return sequencer_remove_state(opts);
@@ -2787,6 +2827,70 @@ int sequencer_rollback(struct repository *r, struct replay_opts *opts)
27872827
return -1;
27882828
}
27892829

2830+
int sequencer_skip(struct repository *r, struct replay_opts *opts)
2831+
{
2832+
enum replay_action action = -1;
2833+
sequencer_get_last_command(r, &action);
2834+
2835+
/*
2836+
* Check whether the subcommand requested to skip the commit is actually
2837+
* in progress and that it's safe to skip the commit.
2838+
*
2839+
* opts->action tells us which subcommand requested to skip the commit.
2840+
* If the corresponding .git/<ACTION>_HEAD exists, we know that the
2841+
* action is in progress and we can skip the commit.
2842+
*
2843+
* Otherwise we check that the last instruction was related to the
2844+
* particular subcommand we're trying to execute and barf if that's not
2845+
* the case.
2846+
*
2847+
* Finally we check that the rollback is "safe", i.e., has the HEAD
2848+
* moved? In this case, it doesn't make sense to "reset the merge" and
2849+
* "skip the commit" as the user already handled this by committing. But
2850+
* we'd not want to barf here, instead give advice on how to proceed. We
2851+
* only need to check that when .git/<ACTION>_HEAD doesn't exist because
2852+
* it gets removed when the user commits, so if it still exists we're
2853+
* sure the user can't have committed before.
2854+
*/
2855+
switch (opts->action) {
2856+
case REPLAY_REVERT:
2857+
if (!file_exists(git_path_revert_head(r))) {
2858+
if (action != REPLAY_REVERT)
2859+
return error(_("no revert in progress"));
2860+
if (!rollback_is_safe())
2861+
goto give_advice;
2862+
}
2863+
break;
2864+
case REPLAY_PICK:
2865+
if (!file_exists(git_path_cherry_pick_head(r))) {
2866+
if (action != REPLAY_PICK)
2867+
return error(_("no cherry-pick in progress"));
2868+
if (!rollback_is_safe())
2869+
goto give_advice;
2870+
}
2871+
break;
2872+
default:
2873+
BUG("unexpected action in sequencer_skip");
2874+
}
2875+
2876+
if (skip_single_pick())
2877+
return error(_("failed to skip the commit"));
2878+
if (!is_directory(git_path_seq_dir()))
2879+
return 0;
2880+
2881+
return sequencer_continue(r, opts);
2882+
2883+
give_advice:
2884+
error(_("there is nothing to skip"));
2885+
2886+
if (advice_resolve_conflict) {
2887+
advise(_("have you committed already?\n"
2888+
"try \"git %s --continue\""),
2889+
action == REPLAY_REVERT ? "revert" : "cherry-pick");
2890+
}
2891+
return -1;
2892+
}
2893+
27902894
static int save_todo(struct todo_list *todo_list, struct replay_opts *opts)
27912895
{
27922896
struct lock_file todo_lock = LOCK_INIT;
@@ -4257,7 +4361,7 @@ int sequencer_pick_revisions(struct repository *r,
42574361
*/
42584362

42594363
if (walk_revs_populate_todo(&todo_list, opts) ||
4260-
create_seq_dir() < 0)
4364+
create_seq_dir(r) < 0)
42614365
return -1;
42624366
if (get_oid("HEAD", &oid) && (opts->action == REPLAY_REVERT))
42634367
return error(_("can't revert as initial commit"));

sequencer.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ int sequencer_pick_revisions(struct repository *repo,
129129
struct replay_opts *opts);
130130
int sequencer_continue(struct repository *repo, struct replay_opts *opts);
131131
int sequencer_rollback(struct repository *repo, struct replay_opts *opts);
132+
int sequencer_skip(struct repository *repo, struct replay_opts *opts);
132133
int sequencer_remove_state(struct replay_opts *opts);
133134

134135
#define TODO_LIST_KEEP_EMPTY (1U << 0)

0 commit comments

Comments
 (0)