@@ -40,7 +40,12 @@ static const char * const cherry_pick_usage[] = {
4040};
4141
4242enum 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+ };
4449
4550struct replay_opts {
4651 enum replay_action action ;
@@ -135,9 +140,11 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
135140 const char * me = action_name (opts );
136141 int remove_state = 0 ;
137142 int contin = 0 ;
143+ int rollback = 0 ;
138144 struct option options [] = {
139145 OPT_BOOLEAN (0 , "quit" , & remove_state , "end revert or cherry-pick sequence" ),
140146 OPT_BOOLEAN (0 , "continue" , & contin , "resume revert or cherry-pick sequence" ),
147+ OPT_BOOLEAN (0 , "abort" , & rollback , "cancel revert or cherry-pick sequence" ),
141148 OPT_BOOLEAN ('n' , "no-commit" , & opts -> no_commit , "don't automatically commit" ),
142149 OPT_BOOLEAN ('e' , "edit" , & opts -> edit , "edit the commit message" ),
143150 OPT_NOOP_NOARG ('r' , NULL ),
@@ -173,13 +180,16 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
173180 verify_opt_mutually_compatible (me ,
174181 "--quit" , remove_state ,
175182 "--continue" , contin ,
183+ "--abort" , rollback ,
176184 NULL );
177185
178186 /* Set the subcommand */
179187 if (remove_state )
180188 opts -> subcommand = REPLAY_REMOVE_STATE ;
181189 else if (contin )
182190 opts -> subcommand = REPLAY_CONTINUE ;
191+ else if (rollback )
192+ opts -> subcommand = REPLAY_ROLLBACK ;
183193 else
184194 opts -> subcommand = REPLAY_NONE ;
185195
@@ -188,8 +198,12 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
188198 char * this_operation ;
189199 if (opts -> subcommand == REPLAY_REMOVE_STATE )
190200 this_operation = "--quit" ;
191- else
201+ else if ( opts -> subcommand == REPLAY_CONTINUE )
192202 this_operation = "--continue" ;
203+ else {
204+ assert (opts -> subcommand == REPLAY_ROLLBACK );
205+ this_operation = "--abort" ;
206+ }
193207
194208 verify_opt_compatible (me , this_operation ,
195209 "--no-commit" , opts -> no_commit ,
@@ -850,7 +864,7 @@ static int create_seq_dir(void)
850864
851865 if (file_exists (seq_dir )) {
852866 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 )\"" ));
854868 return -1 ;
855869 }
856870 else if (mkdir (seq_dir , 0777 ) < 0 )
@@ -873,6 +887,71 @@ static void save_head(const char *head)
873887 die (_ ("Error wrapping up %s." ), head_file );
874888}
875889
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+
876955static void save_todo (struct commit_list * todo_list , struct replay_opts * opts )
877956{
878957 const char * todo_file = git_path (SEQ_TODO_FILE );
@@ -977,6 +1056,8 @@ static int pick_revisions(struct replay_opts *opts)
9771056 remove_sequencer_state (1 );
9781057 return 0 ;
9791058 }
1059+ if (opts -> subcommand == REPLAY_ROLLBACK )
1060+ return sequencer_rollback (opts );
9801061 if (opts -> subcommand == REPLAY_CONTINUE ) {
9811062 if (!file_exists (git_path (SEQ_TODO_FILE )))
9821063 return error (_ ("No %s in progress" ), action_name (opts ));
0 commit comments