@@ -39,7 +39,12 @@ static const char * const cherry_pick_usage[] = {
3939 NULL
4040};
4141
42- enum replay_subcommand { REPLAY_NONE , REPLAY_RESET , REPLAY_CONTINUE };
42+ enum replay_subcommand {
43+ REPLAY_NONE ,
44+ REPLAY_REMOVE_STATE ,
45+ REPLAY_CONTINUE ,
46+ REPLAY_ROLLBACK
47+ };
4348
4449struct replay_opts {
4550 enum replay_action action ;
@@ -133,11 +138,13 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
133138{
134139 const char * const * usage_str = revert_or_cherry_pick_usage (opts );
135140 const char * me = action_name (opts );
136- int reset = 0 ;
141+ int remove_state = 0 ;
137142 int contin = 0 ;
143+ int rollback = 0 ;
138144 struct option options [] = {
139- OPT_BOOLEAN (0 , "reset" , & reset , "forget the current operation" ),
140- OPT_BOOLEAN (0 , "continue" , & contin , "continue the current operation" ),
145+ OPT_BOOLEAN (0 , "quit" , & remove_state , "end revert or cherry-pick sequence" ),
146+ 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 ),
@@ -168,25 +175,32 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
168175
169176 /* Check for incompatible subcommands */
170177 verify_opt_mutually_compatible (me ,
171- "--reset " , reset ,
178+ "--quit " , remove_state ,
172179 "--continue" , contin ,
180+ "--abort" , rollback ,
173181 NULL );
174182
175183 /* Set the subcommand */
176- if (reset )
177- opts -> subcommand = REPLAY_RESET ;
184+ if (remove_state )
185+ opts -> subcommand = REPLAY_REMOVE_STATE ;
178186 else if (contin )
179187 opts -> subcommand = REPLAY_CONTINUE ;
188+ else if (rollback )
189+ opts -> subcommand = REPLAY_ROLLBACK ;
180190 else
181191 opts -> subcommand = REPLAY_NONE ;
182192
183193 /* Check for incompatible command line arguments */
184194 if (opts -> subcommand != REPLAY_NONE ) {
185195 char * this_operation ;
186- if (opts -> subcommand == REPLAY_RESET )
187- this_operation = "--reset " ;
188- else
196+ if (opts -> subcommand == REPLAY_REMOVE_STATE )
197+ this_operation = "--quit " ;
198+ else if ( opts -> subcommand == REPLAY_CONTINUE )
189199 this_operation = "--continue" ;
200+ else {
201+ assert (opts -> subcommand == REPLAY_ROLLBACK );
202+ this_operation = "--abort" ;
203+ }
190204
191205 verify_opt_compatible (me , this_operation ,
192206 "--no-commit" , opts -> no_commit ,
@@ -296,15 +310,15 @@ static char *get_encoding(const char *message)
296310 return NULL ;
297311}
298312
299- static void write_cherry_pick_head (struct commit * commit )
313+ static void write_cherry_pick_head (struct commit * commit , const char * pseudoref )
300314{
301315 const char * filename ;
302316 int fd ;
303317 struct strbuf buf = STRBUF_INIT ;
304318
305319 strbuf_addf (& buf , "%s\n" , sha1_to_hex (commit -> object .sha1 ));
306320
307- filename = git_path ("CHERRY_PICK_HEAD" );
321+ filename = git_path (pseudoref );
308322 fd = open (filename , O_WRONLY | O_CREAT , 0666 );
309323 if (fd < 0 )
310324 die_errno (_ ("Could not open '%s' for writing" ), filename );
@@ -604,8 +618,10 @@ static int do_pick_commit(struct commit *commit, enum replay_action action,
604618 * However, if the merge did not even start, then we don't want to
605619 * write it at all.
606620 */
607- if (action == REPLAY_PICK && !opts -> no_commit && (res == 0 || res == 1 ))
608- write_cherry_pick_head (commit );
621+ if (opts -> action == REPLAY_PICK && !opts -> no_commit && (res == 0 || res == 1 ))
622+ write_cherry_pick_head (commit , "CHERRY_PICK_HEAD" );
623+ if (opts -> action == REPLAY_REVERT && ((opts -> no_commit && res == 0 ) || res == 1 ))
624+ write_cherry_pick_head (commit , "REVERT_HEAD" );
609625
610626 if (res ) {
611627 error (action == REPLAY_REVERT
@@ -836,8 +852,11 @@ static int create_seq_dir(void)
836852{
837853 const char * seq_dir = git_path (SEQ_DIR );
838854
839- if (file_exists (seq_dir ))
840- return error (_ ("%s already exists." ), seq_dir );
855+ if (file_exists (seq_dir )) {
856+ error (_ ("a cherry-pick or revert is already in progress" ));
857+ advise (_ ("try \"git cherry-pick (--continue | --quit | --abort)\"" ));
858+ return -1 ;
859+ }
841860 else if (mkdir (seq_dir , 0777 ) < 0 )
842861 die_errno (_ ("Could not create sequencer directory %s" ), seq_dir );
843862 return 0 ;
@@ -858,6 +877,71 @@ static void save_head(const char *head)
858877 die (_ ("Error wrapping up %s." ), head_file );
859878}
860879
880+ static int reset_for_rollback (const unsigned char * sha1 )
881+ {
882+ const char * argv [4 ]; /* reset --merge <arg> + NULL */
883+ argv [0 ] = "reset" ;
884+ argv [1 ] = "--merge" ;
885+ argv [2 ] = sha1_to_hex (sha1 );
886+ argv [3 ] = NULL ;
887+ return run_command_v_opt (argv , RUN_GIT_CMD );
888+ }
889+
890+ static int rollback_single_pick (void )
891+ {
892+ unsigned char head_sha1 [20 ];
893+
894+ if (!file_exists (git_path ("CHERRY_PICK_HEAD" )) &&
895+ !file_exists (git_path ("REVERT_HEAD" )))
896+ return error (_ ("no cherry-pick or revert in progress" ));
897+ if (!resolve_ref ("HEAD" , head_sha1 , 0 , NULL ))
898+ return error (_ ("cannot resolve HEAD" ));
899+ if (is_null_sha1 (head_sha1 ))
900+ return error (_ ("cannot abort from a branch yet to be born" ));
901+ return reset_for_rollback (head_sha1 );
902+ }
903+
904+ static int sequencer_rollback (struct replay_opts * opts )
905+ {
906+ const char * filename ;
907+ FILE * f ;
908+ unsigned char sha1 [20 ];
909+ struct strbuf buf = STRBUF_INIT ;
910+
911+ filename = git_path (SEQ_HEAD_FILE );
912+ f = fopen (filename , "r" );
913+ if (!f && errno == ENOENT ) {
914+ /*
915+ * There is no multiple-cherry-pick in progress.
916+ * If CHERRY_PICK_HEAD or REVERT_HEAD indicates
917+ * a single-cherry-pick in progress, abort that.
918+ */
919+ return rollback_single_pick ();
920+ }
921+ if (!f )
922+ return error (_ ("cannot open %s: %s" ), filename ,
923+ strerror (errno ));
924+ if (strbuf_getline (& buf , f , '\n' )) {
925+ error (_ ("cannot read %s: %s" ), filename , ferror (f ) ?
926+ strerror (errno ) : _ ("unexpected end of file" ));
927+ goto fail ;
928+ }
929+ if (get_sha1_hex (buf .buf , sha1 ) || buf .buf [40 ] != '\0' ) {
930+ error (_ ("stored pre-cherry-pick HEAD file '%s' is corrupt" ),
931+ filename );
932+ goto fail ;
933+ }
934+ if (reset_for_rollback (sha1 ))
935+ goto fail ;
936+ strbuf_release (& buf );
937+ fclose (f );
938+ return 0 ;
939+ fail :
940+ strbuf_release (& buf );
941+ fclose (f );
942+ return -1 ;
943+ }
944+
861945static void save_todo (struct replay_insn_list * todo_list )
862946{
863947 const char * todo_file = git_path (SEQ_TODO_FILE );
@@ -962,43 +1046,41 @@ static int pick_revisions(struct replay_opts *opts)
9621046 * cherry-pick should be handled differently from an existing
9631047 * one that is being continued
9641048 */
965- if (opts -> subcommand == REPLAY_RESET ) {
1049+ if (opts -> subcommand == REPLAY_REMOVE_STATE ) {
9661050 remove_sequencer_state (1 );
9671051 return 0 ;
968- } else if (opts -> subcommand == REPLAY_CONTINUE ) {
1052+ }
1053+ if (opts -> subcommand == REPLAY_ROLLBACK )
1054+ return sequencer_rollback (opts );
1055+ if (opts -> subcommand == REPLAY_CONTINUE ) {
9691056 if (!file_exists (git_path (SEQ_TODO_FILE )))
970- goto error ;
1057+ return error ( _ ( "No %s in progress" ), action_name ( opts )) ;
9711058 read_populate_opts (& opts );
9721059 read_populate_todo (& todo_list );
9731060
9741061 /* Verify that the conflict has been resolved */
9751062 if (!index_differs_from ("HEAD" , 0 ))
9761063 todo_list = todo_list -> next ;
977- } else {
978- /*
979- * Start a new cherry-pick/ revert sequence; but
980- * first, make sure that an existing one isn't in
981- * progress
982- */
1064+ return pick_commits (todo_list , opts );
1065+ }
9831066
984- walk_revs_populate_todo (& todo_list , opts );
985- if (create_seq_dir () < 0 ) {
986- error (_ ("A cherry-pick or revert is in progress." ));
987- advise (_ ("Use --continue to continue the operation" ));
988- advise (_ ("or --reset to forget about it" ));
989- return -1 ;
990- }
991- if (get_sha1 ("HEAD" , sha1 )) {
992- if (opts -> action == REPLAY_REVERT )
993- return error (_ ("Can't revert as initial commit" ));
994- return error (_ ("Can't cherry-pick into empty head" ));
995- }
996- save_head (sha1_to_hex (sha1 ));
997- save_opts (opts );
1067+ /*
1068+ * Start a new cherry-pick/ revert sequence; but
1069+ * first, make sure that an existing one isn't in
1070+ * progress
1071+ */
1072+
1073+ walk_revs_populate_todo (& todo_list , opts );
1074+ if (create_seq_dir () < 0 )
1075+ return -1 ;
1076+ if (get_sha1 ("HEAD" , sha1 )) {
1077+ if (opts -> action == REPLAY_REVERT )
1078+ return error (_ ("Can't revert as initial commit" ));
1079+ return error (_ ("Can't cherry-pick into empty head" ));
9981080 }
1081+ save_head (sha1_to_hex (sha1 ));
1082+ save_opts (opts );
9991083 return pick_commits (todo_list , opts );
1000- error :
1001- return error (_ ("No %s in progress" ), action_name (opts ));
10021084}
10031085
10041086int cmd_revert (int argc , const char * * argv , const char * prefix )
0 commit comments