@@ -40,7 +40,12 @@ static const char * const cherry_pick_usage[] = {
40
40
};
41
41
42
42
enum replay_action { REVERT , CHERRY_PICK };
43
- enum replay_subcommand { REPLAY_NONE , REPLAY_REMOVE_STATE , REPLAY_CONTINUE };
43
+ enum replay_subcommand {
44
+ REPLAY_NONE ,
45
+ REPLAY_REMOVE_STATE ,
46
+ REPLAY_CONTINUE ,
47
+ REPLAY_ROLLBACK
48
+ };
44
49
45
50
struct replay_opts {
46
51
enum replay_action action ;
@@ -135,9 +140,11 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
135
140
const char * me = action_name (opts );
136
141
int remove_state = 0 ;
137
142
int contin = 0 ;
143
+ int rollback = 0 ;
138
144
struct option options [] = {
139
145
OPT_BOOLEAN (0 , "quit" , & remove_state , "end revert or cherry-pick sequence" ),
140
146
OPT_BOOLEAN (0 , "continue" , & contin , "resume revert or cherry-pick sequence" ),
147
+ OPT_BOOLEAN (0 , "abort" , & rollback , "cancel revert or cherry-pick sequence" ),
141
148
OPT_BOOLEAN ('n' , "no-commit" , & opts -> no_commit , "don't automatically commit" ),
142
149
OPT_BOOLEAN ('e' , "edit" , & opts -> edit , "edit the commit message" ),
143
150
OPT_NOOP_NOARG ('r' , NULL ),
@@ -173,13 +180,16 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
173
180
verify_opt_mutually_compatible (me ,
174
181
"--quit" , remove_state ,
175
182
"--continue" , contin ,
183
+ "--abort" , rollback ,
176
184
NULL );
177
185
178
186
/* Set the subcommand */
179
187
if (remove_state )
180
188
opts -> subcommand = REPLAY_REMOVE_STATE ;
181
189
else if (contin )
182
190
opts -> subcommand = REPLAY_CONTINUE ;
191
+ else if (rollback )
192
+ opts -> subcommand = REPLAY_ROLLBACK ;
183
193
else
184
194
opts -> subcommand = REPLAY_NONE ;
185
195
@@ -188,8 +198,12 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
188
198
char * this_operation ;
189
199
if (opts -> subcommand == REPLAY_REMOVE_STATE )
190
200
this_operation = "--quit" ;
191
- else
201
+ else if ( opts -> subcommand == REPLAY_CONTINUE )
192
202
this_operation = "--continue" ;
203
+ else {
204
+ assert (opts -> subcommand == REPLAY_ROLLBACK );
205
+ this_operation = "--abort" ;
206
+ }
193
207
194
208
verify_opt_compatible (me , this_operation ,
195
209
"--no-commit" , opts -> no_commit ,
@@ -850,7 +864,7 @@ static int create_seq_dir(void)
850
864
851
865
if (file_exists (seq_dir )) {
852
866
error (_ ("a cherry-pick or revert is already in progress" ));
853
- advise (_ ("try \"git cherry-pick (--continue | --quit)\"" ));
867
+ advise (_ ("try \"git cherry-pick (--continue | --quit | --abort )\"" ));
854
868
return -1 ;
855
869
}
856
870
else if (mkdir (seq_dir , 0777 ) < 0 )
@@ -873,6 +887,71 @@ static void save_head(const char *head)
873
887
die (_ ("Error wrapping up %s." ), head_file );
874
888
}
875
889
890
+ static int reset_for_rollback (const unsigned char * sha1 )
891
+ {
892
+ const char * argv [4 ]; /* reset --merge <arg> + NULL */
893
+ argv [0 ] = "reset" ;
894
+ argv [1 ] = "--merge" ;
895
+ argv [2 ] = sha1_to_hex (sha1 );
896
+ argv [3 ] = NULL ;
897
+ return run_command_v_opt (argv , RUN_GIT_CMD );
898
+ }
899
+
900
+ static int rollback_single_pick (void )
901
+ {
902
+ unsigned char head_sha1 [20 ];
903
+
904
+ if (!file_exists (git_path ("CHERRY_PICK_HEAD" )) &&
905
+ !file_exists (git_path ("REVERT_HEAD" )))
906
+ return error (_ ("no cherry-pick or revert in progress" ));
907
+ if (!resolve_ref ("HEAD" , head_sha1 , 0 , NULL ))
908
+ return error (_ ("cannot resolve HEAD" ));
909
+ if (is_null_sha1 (head_sha1 ))
910
+ return error (_ ("cannot abort from a branch yet to be born" ));
911
+ return reset_for_rollback (head_sha1 );
912
+ }
913
+
914
+ static int sequencer_rollback (struct replay_opts * opts )
915
+ {
916
+ const char * filename ;
917
+ FILE * f ;
918
+ unsigned char sha1 [20 ];
919
+ struct strbuf buf = STRBUF_INIT ;
920
+
921
+ filename = git_path (SEQ_HEAD_FILE );
922
+ f = fopen (filename , "r" );
923
+ if (!f && errno == ENOENT ) {
924
+ /*
925
+ * There is no multiple-cherry-pick in progress.
926
+ * If CHERRY_PICK_HEAD or REVERT_HEAD indicates
927
+ * a single-cherry-pick in progress, abort that.
928
+ */
929
+ return rollback_single_pick ();
930
+ }
931
+ if (!f )
932
+ return error (_ ("cannot open %s: %s" ), filename ,
933
+ strerror (errno ));
934
+ if (strbuf_getline (& buf , f , '\n' )) {
935
+ error (_ ("cannot read %s: %s" ), filename , ferror (f ) ?
936
+ strerror (errno ) : _ ("unexpected end of file" ));
937
+ goto fail ;
938
+ }
939
+ if (get_sha1_hex (buf .buf , sha1 ) || buf .buf [40 ] != '\0' ) {
940
+ error (_ ("stored pre-cherry-pick HEAD file '%s' is corrupt" ),
941
+ filename );
942
+ goto fail ;
943
+ }
944
+ if (reset_for_rollback (sha1 ))
945
+ goto fail ;
946
+ strbuf_release (& buf );
947
+ fclose (f );
948
+ return 0 ;
949
+ fail :
950
+ strbuf_release (& buf );
951
+ fclose (f );
952
+ return -1 ;
953
+ }
954
+
876
955
static void save_todo (struct commit_list * todo_list , struct replay_opts * opts )
877
956
{
878
957
const char * todo_file = git_path (SEQ_TODO_FILE );
@@ -977,6 +1056,8 @@ static int pick_revisions(struct replay_opts *opts)
977
1056
remove_sequencer_state (1 );
978
1057
return 0 ;
979
1058
}
1059
+ if (opts -> subcommand == REPLAY_ROLLBACK )
1060
+ return sequencer_rollback (opts );
980
1061
if (opts -> subcommand == REPLAY_CONTINUE ) {
981
1062
if (!file_exists (git_path (SEQ_TODO_FILE )))
982
1063
return error (_ ("No %s in progress" ), action_name (opts ));
0 commit comments